Changeset 27882 in vbox
- Timestamp:
- Mar 31, 2010 11:52:27 AM (15 years ago)
- svn:sync-xref-src-repo-rev:
- 59569
- Location:
- trunk/src/VBox/Main
- Files:
-
- 3 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/ApplianceImpl.cpp
r27829 r27882 480 480 } 481 481 482 /**483 * Public method implementation.484 * @return485 */486 STDMETHODIMP Appliance::Interpret()487 {488 // @todo:489 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))490 // - Appropriate handle errors like not supported file formats491 AutoCaller autoCaller(this);492 if (FAILED(autoCaller.rc())) return autoCaller.rc();493 494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);495 496 if (!isApplianceIdle())497 return E_ACCESSDENIED;498 499 HRESULT rc = S_OK;500 501 /* Clear any previous virtual system descriptions */502 m->virtualSystemDescriptions.clear();503 504 /* We need the default path for storing disk images */505 ComPtr<ISystemProperties> systemProps;506 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());507 if (FAILED(rc)) return rc;508 Bstr bstrDefaultHardDiskLocation;509 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());510 if (FAILED(rc)) return rc;511 512 if (!m->pReader)513 return setError(E_FAIL,514 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));515 516 // Change the appliance state so we can safely leave the lock while doing time-consuming517 // disk imports; also the below method calls do all kinds of locking which conflicts with518 // the appliance object lock519 m->state = Data::ApplianceImporting;520 alock.release();521 522 /* Try/catch so we can clean up on error */523 try524 {525 list<VirtualSystem>::const_iterator it;526 /* Iterate through all virtual systems */527 for (it = m->pReader->m_llVirtualSystems.begin();528 it != m->pReader->m_llVirtualSystems.end();529 ++it)530 {531 const VirtualSystem &vsysThis = *it;532 533 ComObjPtr<VirtualSystemDescription> pNewDesc;534 rc = pNewDesc.createObject();535 if (FAILED(rc)) throw rc;536 rc = pNewDesc->init();537 if (FAILED(rc)) throw rc;538 539 /* Guest OS type */540 Utf8Str strOsTypeVBox,541 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);542 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);543 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,544 "",545 strCIMOSType,546 strOsTypeVBox);547 548 /* VM name */549 /* If the there isn't any name specified create a default one out of550 * the OS type */551 Utf8Str nameVBox = vsysThis.strName;552 if (nameVBox.isEmpty())553 nameVBox = strOsTypeVBox;554 searchUniqueVMName(nameVBox);555 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,556 "",557 vsysThis.strName,558 nameVBox);559 560 /* VM Product */561 if (!vsysThis.strProduct.isEmpty())562 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,563 "",564 vsysThis.strProduct,565 vsysThis.strProduct);566 567 /* VM Vendor */568 if (!vsysThis.strVendor.isEmpty())569 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,570 "",571 vsysThis.strVendor,572 vsysThis.strVendor);573 574 /* VM Version */575 if (!vsysThis.strVersion.isEmpty())576 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,577 "",578 vsysThis.strVersion,579 vsysThis.strVersion);580 581 /* VM ProductUrl */582 if (!vsysThis.strProductUrl.isEmpty())583 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,584 "",585 vsysThis.strProductUrl,586 vsysThis.strProductUrl);587 588 /* VM VendorUrl */589 if (!vsysThis.strVendorUrl.isEmpty())590 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,591 "",592 vsysThis.strVendorUrl,593 vsysThis.strVendorUrl);594 595 /* VM description */596 if (!vsysThis.strDescription.isEmpty())597 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,598 "",599 vsysThis.strDescription,600 vsysThis.strDescription);601 602 /* VM license */603 if (!vsysThis.strLicenseText.isEmpty())604 pNewDesc->addEntry(VirtualSystemDescriptionType_License,605 "",606 vsysThis.strLicenseText,607 vsysThis.strLicenseText);608 609 /* Now that we know the OS type, get our internal defaults based on that. */610 ComPtr<IGuestOSType> pGuestOSType;611 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());612 if (FAILED(rc)) throw rc;613 614 /* CPU count */615 ULONG cpuCountVBox = vsysThis.cCPUs;616 /* Check for the constrains */617 if (cpuCountVBox > SchemaDefs::MaxCPUCount)618 {619 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),620 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);621 cpuCountVBox = SchemaDefs::MaxCPUCount;622 }623 if (vsysThis.cCPUs == 0)624 cpuCountVBox = 1;625 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,626 "",627 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),628 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));629 630 /* RAM */631 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;632 /* Check for the constrains */633 if (ullMemSizeVBox != 0 &&634 (ullMemSizeVBox < MM_RAM_MIN_IN_MB ||635 ullMemSizeVBox > MM_RAM_MAX_IN_MB))636 {637 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."),638 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);639 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);640 }641 if (vsysThis.ullMemorySize == 0)642 {643 /* If the RAM of the OVF is zero, use our predefined values */644 ULONG memSizeVBox2;645 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);646 if (FAILED(rc)) throw rc;647 /* VBox stores that in MByte */648 ullMemSizeVBox = (uint64_t)memSizeVBox2;649 }650 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,651 "",652 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),653 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));654 655 /* Audio */656 if (!vsysThis.strSoundCardType.isEmpty())657 /* Currently we set the AC97 always.658 @todo: figure out the hardware which could be possible */659 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,660 "",661 vsysThis.strSoundCardType,662 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));663 664 #ifdef VBOX_WITH_USB665 /* USB Controller */666 if (vsysThis.fHasUsbController)667 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");668 #endif /* VBOX_WITH_USB */669 670 /* Network Controller */671 size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();672 if (cEthernetAdapters > 0)673 {674 /* Check for the constrains */675 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)676 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),677 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);678 679 /* Get the default network adapter type for the selected guest OS */680 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;681 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);682 if (FAILED(rc)) throw rc;683 684 EthernetAdaptersList::const_iterator itEA;685 /* Iterate through all abstract networks. We support 8 network686 * adapters at the maximum, so the first 8 will be added only. */687 size_t a = 0;688 for (itEA = vsysThis.llEthernetAdapters.begin();689 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;690 ++itEA, ++a)691 {692 const EthernetAdapter &ea = *itEA; // logical network to connect to693 Utf8Str strNetwork = ea.strNetworkName;694 // make sure it's one of these two695 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))696 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))697 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))698 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))699 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))700 )701 strNetwork = "Bridged"; // VMware assumes this is the default apparently702 703 /* Figure out the hardware type */704 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;705 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))706 {707 /* If the default adapter is already one of the two708 * PCNet adapters use the default one. If not use the709 * Am79C970A as fallback. */710 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||711 defaultAdapterVBox == NetworkAdapterType_Am79C973))712 nwAdapterVBox = NetworkAdapterType_Am79C970A;713 }714 #ifdef VBOX_WITH_E1000715 /* VMWare accidentally write this with VirtualCenter 3.5,716 so make sure in this case always to use the VMWare one */717 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))718 nwAdapterVBox = NetworkAdapterType_I82545EM;719 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))720 {721 /* Check if this OVF was written by VirtualBox */722 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))723 {724 /* If the default adapter is already one of the three725 * E1000 adapters use the default one. If not use the726 * I82545EM as fallback. */727 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||728 defaultAdapterVBox == NetworkAdapterType_I82543GC ||729 defaultAdapterVBox == NetworkAdapterType_I82545EM))730 nwAdapterVBox = NetworkAdapterType_I82540EM;731 }732 else733 /* Always use this one since it's what VMware uses */734 nwAdapterVBox = NetworkAdapterType_I82545EM;735 }736 #endif /* VBOX_WITH_E1000 */737 738 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,739 "", // ref740 ea.strNetworkName, // orig741 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf742 0,743 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf744 }745 }746 747 /* Floppy Drive */748 if (vsysThis.fHasFloppyDrive)749 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");750 751 /* CD Drive */752 if (vsysThis.fHasCdromDrive)753 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");754 755 /* Hard disk Controller */756 uint16_t cIDEused = 0;757 uint16_t cSATAused = 0; NOREF(cSATAused);758 uint16_t cSCSIused = 0; NOREF(cSCSIused);759 ControllersMap::const_iterator hdcIt;760 /* Iterate through all hard disk controllers */761 for (hdcIt = vsysThis.mapControllers.begin();762 hdcIt != vsysThis.mapControllers.end();763 ++hdcIt)764 {765 const HardDiskController &hdc = hdcIt->second;766 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);767 768 switch (hdc.system)769 {770 case HardDiskController::IDE:771 {772 /* Check for the constrains */773 /* @todo: I'm very confused! Are these bits *one* controller or774 is every port/bus declared as an extra controller. */775 if (cIDEused < 4)776 {777 // @todo: figure out the IDE types778 /* Use PIIX4 as default */779 Utf8Str strType = "PIIX4";780 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))781 strType = "PIIX3";782 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))783 strType = "ICH6";784 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,785 strControllerID,786 hdc.strControllerType,787 strType);788 }789 else790 {791 /* Warn only once */792 if (cIDEused == 1)793 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),794 vsysThis.strName.c_str());795 796 }797 ++cIDEused;798 break;799 }800 801 case HardDiskController::SATA:802 {803 #ifdef VBOX_WITH_AHCI804 /* Check for the constrains */805 if (cSATAused < 1)806 {807 // @todo: figure out the SATA types808 /* We only support a plain AHCI controller, so use them always */809 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,810 strControllerID,811 hdc.strControllerType,812 "AHCI");813 }814 else815 {816 /* Warn only once */817 if (cSATAused == 1)818 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),819 vsysThis.strName.c_str());820 821 }822 ++cSATAused;823 break;824 #else /* !VBOX_WITH_AHCI */825 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SATA controller emulation"),826 vsysThis.strName.c_str());827 #endif /* !VBOX_WITH_AHCI */828 }829 830 case HardDiskController::SCSI:831 {832 #ifdef VBOX_WITH_LSILOGIC833 /* Check for the constrains */834 if (cSCSIused < 1)835 {836 Utf8Str hdcController = "LsiLogic";837 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))838 hdcController = "BusLogic";839 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,840 strControllerID,841 hdc.strControllerType,842 hdcController);843 }844 else845 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."),846 vsysThis.strName.c_str(),847 hdc.strControllerType.c_str(),848 strControllerID.c_str());849 ++cSCSIused;850 break;851 #else /* !VBOX_WITH_LSILOGIC */852 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SCSI controller emulation"),853 vsysThis.strName.c_str());854 #endif /* !VBOX_WITH_LSILOGIC */855 }856 }857 }858 859 /* Hard disks */860 if (vsysThis.mapVirtualDisks.size() > 0)861 {862 VirtualDisksMap::const_iterator itVD;863 /* Iterate through all hard disks ()*/864 for (itVD = vsysThis.mapVirtualDisks.begin();865 itVD != vsysThis.mapVirtualDisks.end();866 ++itVD)867 {868 const VirtualDisk &hd = itVD->second;869 /* Get the associated disk image */870 const DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId];871 872 // @todo:873 // - figure out all possible vmdk formats we also support874 // - figure out if there is a url specifier for vhd already875 // - we need a url specifier for the vdi format876 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)877 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))878 {879 /* If the href is empty use the VM name as filename */880 Utf8Str strFilename = di.strHref;881 if (!strFilename.length())882 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());883 /* Construct a unique target path */884 Utf8StrFmt strPath("%ls%c%s",885 bstrDefaultHardDiskLocation.raw(),886 RTPATH_DELIMITER,887 strFilename.c_str());888 searchUniqueDiskImageFilePath(strPath);889 890 /* find the description for the hard disk controller891 * that has the same ID as hd.idController */892 const VirtualSystemDescriptionEntry *pController;893 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))894 throw setError(E_FAIL,895 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),896 hd.idController,897 di.strHref.c_str());898 899 /* controller to attach to, and the bus within that controller */900 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",901 pController->ulIndex,902 hd.ulAddressOnParent);903 ULONG ulSize = 0;904 if (di.iCapacity != -1)905 ulSize = (ULONG)(di.iCapacity / _1M);906 else if (di.iPopulatedSize != -1)907 ulSize = (ULONG)(di.iPopulatedSize / _1M);908 else if (di.iSize != -1)909 ulSize = (ULONG)(di.iSize / _1M);910 if (ulSize == 0)911 ulSize = 10000; // assume 10 GB, this is for the progress bar only anyway912 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,913 hd.strDiskId,914 di.strHref,915 strPath,916 ulSize,917 strExtraConfig);918 }919 else920 throw setError(VBOX_E_FILE_ERROR,921 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));922 }923 }924 925 m->virtualSystemDescriptions.push_back(pNewDesc);926 }927 }928 catch (HRESULT aRC)929 {930 /* On error we clear the list & return */931 m->virtualSystemDescriptions.clear();932 rc = aRC;933 }934 935 // reset the appliance state936 alock.acquire();937 m->state = Data::ApplianceIdle;938 939 return rc;940 }941 942 /**943 * Public method implementation.944 * @param aProgress945 * @return946 */947 STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)948 {949 CheckComArgOutPointerValid(aProgress);950 951 AutoCaller autoCaller(this);952 if (FAILED(autoCaller.rc())) return autoCaller.rc();953 954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);955 956 // do not allow entering this method if the appliance is busy reading or writing957 if (!isApplianceIdle())958 return E_ACCESSDENIED;959 960 if (!m->pReader)961 return setError(E_FAIL,962 tr("Cannot import machines without reading it first (call read() before importMachines())"));963 964 ComObjPtr<Progress> progress;965 HRESULT rc = S_OK;966 try967 {968 rc = importImpl(m->locInfo, progress);969 }970 catch (HRESULT aRC)971 {972 rc = aRC;973 }974 975 if (SUCCEEDED(rc))976 /* Return progress to the caller */977 progress.queryInterfaceTo(aProgress);978 979 return rc;980 }981 982 482 STDMETHODIMP Appliance::CreateVFSExplorer(IN_BSTR aURI, IVFSExplorer **aExplorer) 983 483 { … … 1431 931 } 1432 932 1433 HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)1434 {1435 /* Initialize our worker task */1436 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this));1437 /* What should the task do */1438 task->taskType = TaskImportOVF::Read;1439 /* Copy the current location info to the task */1440 task->locInfo = aLocInfo;1441 1442 BstrFmt bstrDesc = BstrFmt(tr("Read appliance '%s'"),1443 aLocInfo.strPath.c_str());1444 HRESULT rc;1445 /* Create the progress object */1446 aProgress.createObject();1447 if (task->locInfo.storageType == VFSType_File)1448 {1449 /* 1 operation only */1450 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),1451 bstrDesc,1452 TRUE /* aCancelable */);1453 }1454 else1455 {1456 /* 4/5 is downloading, 1/5 is reading */1457 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),1458 bstrDesc,1459 TRUE /* aCancelable */,1460 2, // ULONG cOperations,1461 5, // ULONG ulTotalOperationsWeight,1462 BstrFmt(tr("Download appliance '%s'"),1463 aLocInfo.strPath.c_str()), // CBSTR bstrFirstOperationDescription,1464 4); // ULONG ulFirstOperationWeight,1465 }1466 if (FAILED(rc)) throw rc;1467 1468 task->progress = aProgress;1469 1470 rc = task->startThread();1471 if (FAILED(rc)) throw rc;1472 1473 /* Don't destruct on success */1474 task.release();1475 1476 return rc;1477 }1478 1479 /**1480 * Implementation of the import code. This gets called from the public Appliance::ImportMachines()1481 * method as well as Appliance::importS3().1482 * @param aLocInfo1483 * @param aProgress1484 * @return1485 */1486 HRESULT Appliance::importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)1487 {1488 /* Initialize our worker task */1489 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this));1490 /* What should the task do */1491 task->taskType = TaskImportOVF::Import;1492 /* Copy the current location info to the task */1493 task->locInfo = aLocInfo;1494 1495 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),1496 aLocInfo.strPath.c_str());1497 1498 HRESULT rc = S_OK;1499 1500 /* todo: This progress init stuff should be done a little bit more generic */1501 if (task->locInfo.storageType == VFSType_File)1502 rc = setUpProgressFS(aProgress, progressDesc);1503 else1504 rc = setUpProgressImportS3(aProgress, progressDesc);1505 if (FAILED(rc)) throw rc;1506 1507 task->progress = aProgress;1508 1509 rc = task->startThread();1510 if (FAILED(rc)) throw rc;1511 1512 /* Don't destruct on success */1513 task.release();1514 1515 return rc;1516 }1517 1518 /**1519 * Worker thread implementation for Read() (ovf reader).1520 * @param aThread1521 * @param pvUser1522 */1523 /* static */1524 DECLCALLBACK(int) Appliance::taskThreadImportOVF(RTTHREAD /* aThread */, void *pvUser)1525 {1526 std::auto_ptr<TaskImportOVF> task(static_cast<TaskImportOVF*>(pvUser));1527 AssertReturn(task.get(), VERR_GENERAL_FAILURE);1528 1529 Appliance *pAppliance = task->pAppliance;1530 1531 LogFlowFuncEnter();1532 LogFlowFunc(("Appliance %p\n", pAppliance));1533 1534 HRESULT rc = S_OK;1535 1536 switch (task->taskType)1537 {1538 case TaskImportOVF::Read:1539 {1540 if (task->locInfo.storageType == VFSType_File)1541 rc = pAppliance->readFS(task.get());1542 else if (task->locInfo.storageType == VFSType_S3)1543 rc = pAppliance->readS3(task.get());1544 break;1545 }1546 case TaskImportOVF::Import:1547 {1548 if (task->locInfo.storageType == VFSType_File)1549 rc = pAppliance->importFS(task.get());1550 else if (task->locInfo.storageType == VFSType_S3)1551 rc = pAppliance->importS3(task.get());1552 break;1553 }1554 }1555 1556 LogFlowFunc(("rc=%Rhrc\n", rc));1557 LogFlowFuncLeave();1558 1559 return VINF_SUCCESS;1560 }1561 1562 int Appliance::TaskImportOVF::startThread()1563 {1564 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportOVF, this,1565 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,1566 "Appliance::Task");1567 1568 ComAssertMsgRCRet(vrc,1569 ("Could not create taskThreadImportOVF (%Rrc)\n", vrc), E_FAIL);1570 1571 return S_OK;1572 }1573 1574 int Appliance::readFS(TaskImportOVF *pTask)1575 {1576 LogFlowFuncEnter();1577 LogFlowFunc(("Appliance %p\n", this));1578 1579 AutoCaller autoCaller(this);1580 if (FAILED(autoCaller.rc())) return autoCaller.rc();1581 1582 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);1583 1584 HRESULT rc = S_OK;1585 1586 try1587 {1588 /* Read & parse the XML structure of the OVF file */1589 m->pReader = new OVFReader(pTask->locInfo.strPath);1590 /* Create the SHA1 sum of the OVF file for later validation */1591 char *pszDigest;1592 int vrc = RTSha1Digest(pTask->locInfo.strPath.c_str(), &pszDigest);1593 if (RT_FAILURE(vrc))1594 throw setError(VBOX_E_FILE_ERROR,1595 tr("Couldn't calculate SHA1 digest for file '%s' (%Rrc)"),1596 RTPathFilename(pTask->locInfo.strPath.c_str()), vrc);1597 m->strOVFSHA1Digest = pszDigest;1598 RTStrFree(pszDigest);1599 }1600 catch(xml::Error &x)1601 {1602 rc = setError(VBOX_E_FILE_ERROR,1603 x.what());1604 }1605 catch(HRESULT aRC)1606 {1607 rc = aRC;1608 }1609 1610 pTask->rc = rc;1611 1612 if (!pTask->progress.isNull())1613 pTask->progress->notifyComplete(rc);1614 1615 LogFlowFunc(("rc=%Rhrc\n", rc));1616 LogFlowFuncLeave();1617 1618 return VINF_SUCCESS;1619 }1620 1621 int Appliance::readS3(TaskImportOVF *pTask)1622 {1623 LogFlowFuncEnter();1624 LogFlowFunc(("Appliance %p\n", this));1625 1626 AutoCaller autoCaller(this);1627 if (FAILED(autoCaller.rc())) return autoCaller.rc();1628 1629 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);1630 1631 HRESULT rc = S_OK;1632 int vrc = VINF_SUCCESS;1633 RTS3 hS3 = NIL_RTS3;1634 char szOSTmpDir[RTPATH_MAX];1635 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));1636 /* The template for the temporary directory created below */1637 char *pszTmpDir;1638 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);1639 list< pair<Utf8Str, ULONG> > filesList;1640 Utf8Str strTmpOvf;1641 1642 try1643 {1644 /* Extract the bucket */1645 Utf8Str tmpPath = pTask->locInfo.strPath;1646 Utf8Str bucket;1647 parseBucket(tmpPath, bucket);1648 1649 /* We need a temporary directory which we can put the OVF file & all1650 * disk images in */1651 vrc = RTDirCreateTemp(pszTmpDir);1652 if (RT_FAILURE(vrc))1653 throw setError(VBOX_E_FILE_ERROR,1654 tr("Cannot create temporary directory '%s'"), pszTmpDir);1655 1656 /* The temporary name of the target OVF file */1657 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));1658 1659 /* Next we have to download the OVF */1660 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);1661 if(RT_FAILURE(vrc))1662 throw setError(VBOX_E_IPRT_ERROR,1663 tr("Cannot create S3 service handler"));1664 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);1665 1666 /* Get it */1667 char *pszFilename = RTPathFilename(strTmpOvf.c_str());1668 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strTmpOvf.c_str());1669 if (RT_FAILURE(vrc))1670 {1671 if(vrc == VERR_S3_CANCELED)1672 throw S_OK; /* todo: !!!!!!!!!!!!! */1673 else if(vrc == VERR_S3_ACCESS_DENIED)1674 throw setError(E_ACCESSDENIED,1675 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);1676 else if(vrc == VERR_S3_NOT_FOUND)1677 throw setError(VBOX_E_FILE_ERROR,1678 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);1679 else1680 throw setError(VBOX_E_IPRT_ERROR,1681 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);1682 }1683 1684 /* Close the connection early */1685 RTS3Destroy(hS3);1686 hS3 = NIL_RTS3;1687 1688 if (!pTask->progress.isNull())1689 pTask->progress->SetNextOperation(Bstr(tr("Reading")), 1);1690 1691 /* Prepare the temporary reading of the OVF */1692 ComObjPtr<Progress> progress;1693 LocationInfo li;1694 li.strPath = strTmpOvf;1695 /* Start the reading from the fs */1696 rc = readImpl(li, progress);1697 if (FAILED(rc)) throw rc;1698 1699 /* Unlock the appliance for the reading thread */1700 appLock.release();1701 /* Wait until the reading is done, but report the progress back to the1702 caller */1703 ComPtr<IProgress> progressInt(progress);1704 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */1705 1706 /* Again lock the appliance for the next steps */1707 appLock.acquire();1708 }1709 catch(HRESULT aRC)1710 {1711 rc = aRC;1712 }1713 /* Cleanup */1714 RTS3Destroy(hS3);1715 /* Delete all files which where temporary created */1716 if (RTPathExists(strTmpOvf.c_str()))1717 {1718 vrc = RTFileDelete(strTmpOvf.c_str());1719 if(RT_FAILURE(vrc))1720 rc = setError(VBOX_E_FILE_ERROR,1721 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);1722 }1723 /* Delete the temporary directory */1724 if (RTPathExists(pszTmpDir))1725 {1726 vrc = RTDirRemove(pszTmpDir);1727 if(RT_FAILURE(vrc))1728 rc = setError(VBOX_E_FILE_ERROR,1729 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);1730 }1731 if (pszTmpDir)1732 RTStrFree(pszTmpDir);1733 1734 pTask->rc = rc;1735 1736 if (!pTask->progress.isNull())1737 pTask->progress->notifyComplete(rc);1738 1739 LogFlowFunc(("rc=%Rhrc\n", rc));1740 LogFlowFuncLeave();1741 1742 return VINF_SUCCESS;1743 }1744 1745 int Appliance::importFS(TaskImportOVF *pTask)1746 {1747 LogFlowFuncEnter();1748 LogFlowFunc(("Appliance %p\n", this));1749 1750 AutoCaller autoCaller(this);1751 if (FAILED(autoCaller.rc())) return autoCaller.rc();1752 1753 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);1754 1755 if (!isApplianceIdle())1756 return VERR_ACCESS_DENIED;1757 1758 // Change the appliance state so we can safely leave the lock while doing time-consuming1759 // disk imports; also the below method calls do all kinds of locking which conflicts with1760 // the appliance object lock1761 m->state = Data::ApplianceImporting;1762 appLock.release();1763 1764 HRESULT rc = S_OK;1765 1766 // rollback for errors:1767 // a list of images that we created/imported1768 list<MyHardDiskAttachment> llHardDiskAttachments;1769 list< ComPtr<IMedium> > llHardDisksCreated;1770 list<Bstr> llMachinesRegistered; // list of string UUIDs1771 1772 ComPtr<ISession> session;1773 bool fSessionOpen = false;1774 rc = session.createInprocObject(CLSID_Session);1775 if (FAILED(rc)) return rc;1776 1777 const OVFReader &reader = *m->pReader;1778 // this is safe to access because this thread only gets started1779 // if pReader != NULL1780 1781 /* If an manifest file exists, verify the content. Therefore we need all1782 * files which are referenced by the OVF & the OVF itself */1783 Utf8Str strMfFile = manifestFileName(pTask->locInfo.strPath);1784 list<Utf8Str> filesList;1785 if (RTPathExists(strMfFile.c_str()))1786 {1787 Utf8Str strSrcDir(pTask->locInfo.strPath);1788 strSrcDir.stripFilename();1789 /* Add every disks of every virtual system to an internal list */1790 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;1791 for (it = m->virtualSystemDescriptions.begin();1792 it != m->virtualSystemDescriptions.end();1793 ++it)1794 {1795 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);1796 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);1797 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;1798 for (itH = avsdeHDs.begin();1799 itH != avsdeHDs.end();1800 ++itH)1801 {1802 VirtualSystemDescriptionEntry *vsdeHD = *itH;1803 /* Find the disk from the OVF's disk list */1804 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);1805 const DiskImage &di = itDiskImage->second;1806 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());1807 filesList.push_back(strSrcFilePath);1808 }1809 }1810 /* Create the test list */1811 PRTMANIFESTTEST pTestList = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST)*(filesList.size()+1));1812 pTestList[0].pszTestFile = (char*)pTask->locInfo.strPath.c_str();1813 pTestList[0].pszTestDigest = (char*)m->strOVFSHA1Digest.c_str();1814 int vrc = VINF_SUCCESS;1815 size_t i = 1;1816 list<Utf8Str>::const_iterator it1;1817 for (it1 = filesList.begin();1818 it1 != filesList.end();1819 ++it1, ++i)1820 {1821 char* pszDigest;1822 vrc = RTSha1Digest((*it1).c_str(), &pszDigest);1823 pTestList[i].pszTestFile = (char*)(*it1).c_str();1824 pTestList[i].pszTestDigest = pszDigest;1825 }1826 size_t cIndexOnError;1827 vrc = RTManifestVerify(strMfFile.c_str(), pTestList, filesList.size() + 1, &cIndexOnError);1828 if (vrc == VERR_MANIFEST_DIGEST_MISMATCH)1829 rc = setError(VBOX_E_FILE_ERROR,1830 tr("The SHA1 digest of '%s' doesn't match to the one in '%s'"),1831 RTPathFilename(pTestList[cIndexOnError].pszTestFile),1832 RTPathFilename(strMfFile.c_str()));1833 else if (RT_FAILURE(vrc))1834 rc = setError(VBOX_E_FILE_ERROR,1835 tr("Couldn't verify the content of '%s' against the available files (%Rrc)"),1836 RTPathFilename(strMfFile.c_str()),1837 vrc);1838 /* Cleanup */1839 for (size_t j = 1;1840 j < filesList.size();1841 ++j)1842 RTStrFree(pTestList[j].pszTestDigest);1843 RTMemFree(pTestList);1844 if (FAILED(rc))1845 {1846 /* Return on error */1847 pTask->rc = rc;1848 1849 if (!pTask->progress.isNull())1850 pTask->progress->notifyComplete(rc);1851 return rc;1852 }1853 }1854 1855 list<VirtualSystem>::const_iterator it;1856 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;1857 /* Iterate through all virtual systems of that appliance */1858 size_t i = 0;1859 for (it = reader.m_llVirtualSystems.begin(),1860 it1 = m->virtualSystemDescriptions.begin();1861 it != reader.m_llVirtualSystems.end();1862 ++it, ++it1, ++i)1863 {1864 const VirtualSystem &vsysThis = *it;1865 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);1866 1867 ComPtr<IMachine> pNewMachine;1868 1869 /* Catch possible errors */1870 try1871 {1872 /* Guest OS type */1873 std::list<VirtualSystemDescriptionEntry*> vsdeOS;1874 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);1875 if (vsdeOS.size() < 1)1876 throw setError(VBOX_E_FILE_ERROR,1877 tr("Missing guest OS type"));1878 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;1879 1880 /* Now that we know the base system get our internal defaults based on that. */1881 ComPtr<IGuestOSType> osType;1882 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());1883 if (FAILED(rc)) throw rc;1884 1885 /* Create the machine */1886 /* First get the name */1887 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);1888 if (vsdeName.size() < 1)1889 throw setError(VBOX_E_FILE_ERROR,1890 tr("Missing VM name"));1891 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;1892 rc = mVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),1893 Bstr(), Bstr(), FALSE,1894 pNewMachine.asOutParam());1895 if (FAILED(rc)) throw rc;1896 1897 // and the description1898 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);1899 if (vsdeDescription.size())1900 {1901 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;1902 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));1903 if (FAILED(rc)) throw rc;1904 }1905 1906 /* CPU count */1907 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU);1908 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL);1909 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox;1910 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str());1911 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount);1912 if (FAILED(rc)) throw rc;1913 bool fEnableIOApic = false;1914 /* We need HWVirt & IO-APIC if more than one CPU is requested */1915 if (tmpCount > 1)1916 {1917 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE);1918 if (FAILED(rc)) throw rc;1919 1920 fEnableIOApic = true;1921 }1922 1923 /* RAM */1924 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);1925 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);1926 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;1927 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());1928 rc = pNewMachine->COMSETTER(MemorySize)(tt);1929 if (FAILED(rc)) throw rc;1930 1931 /* VRAM */1932 /* Get the recommended VRAM for this guest OS type */1933 ULONG vramVBox;1934 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);1935 if (FAILED(rc)) throw rc;1936 1937 /* Set the VRAM */1938 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);1939 if (FAILED(rc)) throw rc;1940 1941 /* I/O APIC: so far we have no setting for this. Enable it if we1942 import a Windows VM because if if Windows was installed without IOAPIC,1943 it will not mind finding an one later on, but if Windows was installed1944 _with_ an IOAPIC, it will bluescreen if it's not found */1945 Bstr bstrFamilyId;1946 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());1947 if (FAILED(rc)) throw rc;1948 1949 Utf8Str strFamilyId(bstrFamilyId);1950 if (strFamilyId == "Windows")1951 fEnableIOApic = true;1952 1953 /* If IP-APIC should be enabled could be have different reasons.1954 See CPU count & the Win test above. Here we enable it if it was1955 previously requested. */1956 if (fEnableIOApic)1957 {1958 ComPtr<IBIOSSettings> pBIOSSettings;1959 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());1960 if (FAILED(rc)) throw rc;1961 1962 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);1963 if (FAILED(rc)) throw rc;1964 }1965 1966 /* Audio Adapter */1967 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);1968 /* @todo: we support one audio adapter only */1969 if (vsdeAudioAdapter.size() > 0)1970 {1971 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;1972 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)1973 {1974 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());1975 ComPtr<IAudioAdapter> audioAdapter;1976 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());1977 if (FAILED(rc)) throw rc;1978 rc = audioAdapter->COMSETTER(Enabled)(true);1979 if (FAILED(rc)) throw rc;1980 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));1981 if (FAILED(rc)) throw rc;1982 }1983 }1984 1985 #ifdef VBOX_WITH_USB1986 /* USB Controller */1987 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);1988 // USB support is enabled if there's at least one such entry; to disable USB support,1989 // the type of the USB item would have been changed to "ignore"1990 bool fUSBEnabled = vsdeUSBController.size() > 0;1991 1992 ComPtr<IUSBController> usbController;1993 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());1994 if (FAILED(rc)) throw rc;1995 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);1996 if (FAILED(rc)) throw rc;1997 #endif /* VBOX_WITH_USB */1998 1999 /* Change the network adapters */2000 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);2001 if (vsdeNW.size() == 0)2002 {2003 /* No network adapters, so we have to disable our default one */2004 ComPtr<INetworkAdapter> nwVBox;2005 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());2006 if (FAILED(rc)) throw rc;2007 rc = nwVBox->COMSETTER(Enabled)(false);2008 if (FAILED(rc)) throw rc;2009 }2010 else2011 {2012 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;2013 /* Iterate through all network cards. We support 8 network adapters2014 * at the maximum. (@todo: warn if there are more!) */2015 size_t a = 0;2016 for (nwIt = vsdeNW.begin();2017 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);2018 ++nwIt, ++a)2019 {2020 const VirtualSystemDescriptionEntry* pvsys = *nwIt;2021 2022 const Utf8Str &nwTypeVBox = pvsys->strVbox;2023 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());2024 ComPtr<INetworkAdapter> pNetworkAdapter;2025 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());2026 if (FAILED(rc)) throw rc;2027 /* Enable the network card & set the adapter type */2028 rc = pNetworkAdapter->COMSETTER(Enabled)(true);2029 if (FAILED(rc)) throw rc;2030 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));2031 if (FAILED(rc)) throw rc;2032 2033 // default is NAT; change to "bridged" if extra conf says so2034 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))2035 {2036 /* Attach to the right interface */2037 rc = pNetworkAdapter->AttachToBridgedInterface();2038 if (FAILED(rc)) throw rc;2039 ComPtr<IHost> host;2040 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());2041 if (FAILED(rc)) throw rc;2042 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;2043 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));2044 if (FAILED(rc)) throw rc;2045 /* We search for the first host network interface which2046 * is usable for bridged networking */2047 for (size_t j = 0;2048 j < nwInterfaces.size();2049 ++j)2050 {2051 HostNetworkInterfaceType_T itype;2052 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);2053 if (FAILED(rc)) throw rc;2054 if (itype == HostNetworkInterfaceType_Bridged)2055 {2056 Bstr name;2057 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());2058 if (FAILED(rc)) throw rc;2059 /* Set the interface name to attach to */2060 pNetworkAdapter->COMSETTER(HostInterface)(name);2061 if (FAILED(rc)) throw rc;2062 break;2063 }2064 }2065 }2066 /* Next test for host only interfaces */2067 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))2068 {2069 /* Attach to the right interface */2070 rc = pNetworkAdapter->AttachToHostOnlyInterface();2071 if (FAILED(rc)) throw rc;2072 ComPtr<IHost> host;2073 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());2074 if (FAILED(rc)) throw rc;2075 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;2076 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));2077 if (FAILED(rc)) throw rc;2078 /* We search for the first host network interface which2079 * is usable for host only networking */2080 for (size_t j = 0;2081 j < nwInterfaces.size();2082 ++j)2083 {2084 HostNetworkInterfaceType_T itype;2085 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);2086 if (FAILED(rc)) throw rc;2087 if (itype == HostNetworkInterfaceType_HostOnly)2088 {2089 Bstr name;2090 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());2091 if (FAILED(rc)) throw rc;2092 /* Set the interface name to attach to */2093 pNetworkAdapter->COMSETTER(HostInterface)(name);2094 if (FAILED(rc)) throw rc;2095 break;2096 }2097 }2098 }2099 }2100 }2101 2102 /* Hard disk controller IDE */2103 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);2104 if (vsdeHDCIDE.size() > 1)2105 throw setError(VBOX_E_FILE_ERROR,2106 tr("Too many IDE controllers in OVF; import facility only supports one"));2107 if (vsdeHDCIDE.size() == 1)2108 {2109 ComPtr<IStorageController> pController;2110 rc = pNewMachine->AddStorageController(Bstr("IDE Controller"), StorageBus_IDE, pController.asOutParam());2111 if (FAILED(rc)) throw rc;2112 2113 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();2114 if (!strcmp(pcszIDEType, "PIIX3"))2115 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);2116 else if (!strcmp(pcszIDEType, "PIIX4"))2117 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);2118 else if (!strcmp(pcszIDEType, "ICH6"))2119 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);2120 else2121 throw setError(VBOX_E_FILE_ERROR,2122 tr("Invalid IDE controller type \"%s\""),2123 pcszIDEType);2124 if (FAILED(rc)) throw rc;2125 }2126 #ifdef VBOX_WITH_AHCI2127 /* Hard disk controller SATA */2128 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);2129 if (vsdeHDCSATA.size() > 1)2130 throw setError(VBOX_E_FILE_ERROR,2131 tr("Too many SATA controllers in OVF; import facility only supports one"));2132 if (vsdeHDCSATA.size() > 0)2133 {2134 ComPtr<IStorageController> pController;2135 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;2136 if (hdcVBox == "AHCI")2137 {2138 rc = pNewMachine->AddStorageController(Bstr("SATA Controller"), StorageBus_SATA, pController.asOutParam());2139 if (FAILED(rc)) throw rc;2140 }2141 else2142 throw setError(VBOX_E_FILE_ERROR,2143 tr("Invalid SATA controller type \"%s\""),2144 hdcVBox.c_str());2145 }2146 #endif /* VBOX_WITH_AHCI */2147 2148 #ifdef VBOX_WITH_LSILOGIC2149 /* Hard disk controller SCSI */2150 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);2151 if (vsdeHDCSCSI.size() > 1)2152 throw setError(VBOX_E_FILE_ERROR,2153 tr("Too many SCSI controllers in OVF; import facility only supports one"));2154 if (vsdeHDCSCSI.size() > 0)2155 {2156 ComPtr<IStorageController> pController;2157 StorageControllerType_T controllerType;2158 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;2159 if (hdcVBox == "LsiLogic")2160 controllerType = StorageControllerType_LsiLogic;2161 else if (hdcVBox == "BusLogic")2162 controllerType = StorageControllerType_BusLogic;2163 else2164 throw setError(VBOX_E_FILE_ERROR,2165 tr("Invalid SCSI controller type \"%s\""),2166 hdcVBox.c_str());2167 2168 rc = pNewMachine->AddStorageController(Bstr("SCSI Controller"), StorageBus_SCSI, pController.asOutParam());2169 if (FAILED(rc)) throw rc;2170 rc = pController->COMSETTER(ControllerType)(controllerType);2171 if (FAILED(rc)) throw rc;2172 }2173 #endif /* VBOX_WITH_LSILOGIC */2174 2175 /* Now its time to register the machine before we add any hard disks */2176 rc = mVirtualBox->RegisterMachine(pNewMachine);2177 if (FAILED(rc)) throw rc;2178 2179 Bstr bstrNewMachineId;2180 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());2181 if (FAILED(rc)) throw rc;2182 2183 // store new machine for roll-back in case of errors2184 llMachinesRegistered.push_back(bstrNewMachineId);2185 2186 // Add floppies and CD-ROMs to the appropriate controllers.2187 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);2188 if (vsdeFloppy.size() > 1)2189 throw setError(VBOX_E_FILE_ERROR,2190 tr("Too many floppy controllers in OVF; import facility only supports one"));2191 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM);2192 if ( (vsdeFloppy.size() > 0)2193 || (vsdeCDROM.size() > 0)2194 )2195 {2196 // If there's an error here we need to close the session, so2197 // we need another try/catch block.2198 2199 try2200 {2201 /* In order to attach things we need to open a session2202 * for the new machine */2203 rc = mVirtualBox->OpenSession(session, bstrNewMachineId);2204 if (FAILED(rc)) throw rc;2205 fSessionOpen = true;2206 2207 ComPtr<IMachine> sMachine;2208 rc = session->COMGETTER(Machine)(sMachine.asOutParam());2209 if (FAILED(rc)) throw rc;2210 2211 // floppy first2212 if (vsdeFloppy.size() == 1)2213 {2214 ComPtr<IStorageController> pController;2215 rc = sMachine->AddStorageController(Bstr("Floppy Controller"), StorageBus_Floppy, pController.asOutParam());2216 if (FAILED(rc)) throw rc;2217 2218 Bstr bstrName;2219 rc = pController->COMGETTER(Name)(bstrName.asOutParam());2220 if (FAILED(rc)) throw rc;2221 2222 // this is for rollback later2223 MyHardDiskAttachment mhda;2224 mhda.bstrUuid = bstrNewMachineId;2225 mhda.pMachine = pNewMachine;2226 mhda.controllerType = bstrName;2227 mhda.lChannel = 0;2228 mhda.lDevice = 0;2229 2230 Log(("Attaching floppy\n"));2231 2232 rc = sMachine->AttachDevice(mhda.controllerType,2233 mhda.lChannel,2234 mhda.lDevice,2235 DeviceType_Floppy,2236 NULL);2237 if (FAILED(rc)) throw rc;2238 2239 llHardDiskAttachments.push_back(mhda);2240 }2241 2242 2243 // CD-ROMs next2244 for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin();2245 jt != vsdeCDROM.end();2246 ++jt)2247 {2248 // for now always attach to secondary master on IDE controller;2249 // there seems to be no useful information in OVF where else to2250 // attach jt (@todo test with latest versions of OVF software)2251 2252 // find the IDE controller2253 const HardDiskController *pController = NULL;2254 for (ControllersMap::const_iterator kt = vsysThis.mapControllers.begin();2255 kt != vsysThis.mapControllers.end();2256 ++kt)2257 {2258 if (kt->second.system == HardDiskController::IDE)2259 {2260 pController = &kt->second;2261 }2262 }2263 2264 if (!pController)2265 throw setError(VBOX_E_FILE_ERROR,2266 tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox"));2267 2268 // this is for rollback later2269 MyHardDiskAttachment mhda;2270 mhda.bstrUuid = bstrNewMachineId;2271 mhda.pMachine = pNewMachine;2272 2273 ConvertDiskAttachmentValues(*pController,2274 2, // interpreted as secondary master2275 mhda.controllerType, // Bstr2276 mhda.lChannel,2277 mhda.lDevice);2278 2279 Log(("Attaching CD-ROM to channel %d on device %d\n", mhda.lChannel, mhda.lDevice));2280 2281 rc = sMachine->AttachDevice(mhda.controllerType,2282 mhda.lChannel,2283 mhda.lDevice,2284 DeviceType_DVD,2285 NULL);2286 if (FAILED(rc)) throw rc;2287 2288 llHardDiskAttachments.push_back(mhda);2289 } // end for (itHD = avsdeHDs.begin();2290 2291 rc = sMachine->SaveSettings();2292 if (FAILED(rc)) throw rc;2293 2294 // only now that we're done with all disks, close the session2295 rc = session->Close();2296 if (FAILED(rc)) throw rc;2297 fSessionOpen = false;2298 }2299 catch(HRESULT /* aRC */)2300 {2301 if (fSessionOpen)2302 session->Close();2303 2304 throw;2305 }2306 }2307 2308 /* Create the hard disks & connect them to the appropriate controllers. */2309 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);2310 if (avsdeHDs.size() > 0)2311 {2312 // If there's an error here we need to close the session, so2313 // we need another try/catch block.2314 ComPtr<IMedium> srcHdVBox;2315 bool fSourceHdNeedsClosing = false;2316 2317 try2318 {2319 /* In order to attach hard disks we need to open a session2320 * for the new machine */2321 rc = mVirtualBox->OpenSession(session, bstrNewMachineId);2322 if (FAILED(rc)) throw rc;2323 fSessionOpen = true;2324 2325 /* The disk image has to be on the same place as the OVF file. So2326 * strip the filename out of the full file path. */2327 Utf8Str strSrcDir(pTask->locInfo.strPath);2328 strSrcDir.stripFilename();2329 2330 /* Iterate over all given disk images */2331 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;2332 for (itHD = avsdeHDs.begin();2333 itHD != avsdeHDs.end();2334 ++itHD)2335 {2336 VirtualSystemDescriptionEntry *vsdeHD = *itHD;2337 2338 /* Check if the destination file exists already or the2339 * destination path is empty. */2340 if ( vsdeHD->strVbox.isEmpty()2341 || RTPathExists(vsdeHD->strVbox.c_str())2342 )2343 /* This isn't allowed */2344 throw setError(VBOX_E_FILE_ERROR,2345 tr("Destination file '%s' exists",2346 vsdeHD->strVbox.c_str()));2347 2348 /* Find the disk from the OVF's disk list */2349 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);2350 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist2351 in the virtual system's disks map under that ID and also in the global images map. */2352 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);2353 2354 if ( itDiskImage == reader.m_mapDisks.end()2355 || itVirtualDisk == vsysThis.mapVirtualDisks.end()2356 )2357 throw setError(E_FAIL,2358 tr("Internal inconsistency looking up disk images."));2359 2360 const DiskImage &di = itDiskImage->second;2361 const VirtualDisk &vd = itVirtualDisk->second;2362 2363 /* Make sure all target directories exists */2364 rc = VirtualBox::ensureFilePathExists(vsdeHD->strVbox.c_str());2365 if (FAILED(rc))2366 throw rc;2367 2368 // subprogress object for hard disk2369 ComPtr<IProgress> pProgress2;2370 2371 ComPtr<IMedium> dstHdVBox;2372 /* If strHref is empty we have to create a new file */2373 if (di.strHref.isEmpty())2374 {2375 /* Which format to use? */2376 Bstr srcFormat = L"VDI";2377 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)2378 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))2379 srcFormat = L"VMDK";2380 /* Create an empty hard disk */2381 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam());2382 if (FAILED(rc)) throw rc;2383 2384 /* Create a dynamic growing disk image with the given capacity */2385 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam());2386 if (FAILED(rc)) throw rc;2387 2388 /* Advance to the next operation */2389 if (!pTask->progress.isNull())2390 pTask->progress->SetNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), vsdeHD->strVbox.c_str()),2391 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally2392 }2393 else2394 {2395 /* Construct the source file path */2396 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());2397 /* Check if the source file exists */2398 if (!RTPathExists(strSrcFilePath.c_str()))2399 /* This isn't allowed */2400 throw setError(VBOX_E_FILE_ERROR,2401 tr("Source virtual disk image file '%s' doesn't exist"),2402 strSrcFilePath.c_str());2403 2404 /* Clone the disk image (this is necessary cause the id has2405 * to be recreated for the case the same hard disk is2406 * attached already from a previous import) */2407 2408 /* First open the existing disk image */2409 rc = mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),2410 AccessMode_ReadOnly,2411 false,2412 NULL,2413 false,2414 NULL,2415 srcHdVBox.asOutParam());2416 if (FAILED(rc)) throw rc;2417 fSourceHdNeedsClosing = true;2418 2419 /* We need the format description of the source disk image */2420 Bstr srcFormat;2421 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());2422 if (FAILED(rc)) throw rc;2423 /* Create a new hard disk interface for the destination disk image */2424 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam());2425 if (FAILED(rc)) throw rc;2426 /* Clone the source disk image */2427 rc = srcHdVBox->CloneTo(dstHdVBox, MediumVariant_Standard, NULL, pProgress2.asOutParam());2428 if (FAILED(rc)) throw rc;2429 2430 /* Advance to the next operation */2431 if (!pTask->progress.isNull())2432 pTask->progress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),2433 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally);2434 }2435 2436 // now wait for the background disk operation to complete; this throws HRESULTs on error2437 waitForAsyncProgress(pTask->progress, pProgress2);2438 2439 if (fSourceHdNeedsClosing)2440 {2441 rc = srcHdVBox->Close();2442 if (FAILED(rc)) throw rc;2443 fSourceHdNeedsClosing = false;2444 }2445 2446 llHardDisksCreated.push_back(dstHdVBox);2447 /* Now use the new uuid to attach the disk image to our new machine */2448 ComPtr<IMachine> sMachine;2449 rc = session->COMGETTER(Machine)(sMachine.asOutParam());2450 if (FAILED(rc)) throw rc;2451 Bstr hdId;2452 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());2453 if (FAILED(rc)) throw rc;2454 2455 /* For now we assume we have one controller of every type only */2456 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;2457 2458 // this is for rollback later2459 MyHardDiskAttachment mhda;2460 mhda.bstrUuid = bstrNewMachineId;2461 mhda.pMachine = pNewMachine;2462 2463 ConvertDiskAttachmentValues(hdc,2464 vd.ulAddressOnParent,2465 mhda.controllerType, // Bstr2466 mhda.lChannel,2467 mhda.lDevice);2468 2469 Log(("Attaching disk %s to channel %d on device %d\n", vsdeHD->strVbox.c_str(), mhda.lChannel, mhda.lDevice));2470 2471 rc = sMachine->AttachDevice(mhda.controllerType,2472 mhda.lChannel,2473 mhda.lDevice,2474 DeviceType_HardDisk,2475 hdId);2476 if (FAILED(rc)) throw rc;2477 2478 llHardDiskAttachments.push_back(mhda);2479 2480 rc = sMachine->SaveSettings();2481 if (FAILED(rc)) throw rc;2482 } // end for (itHD = avsdeHDs.begin();2483 2484 // only now that we're done with all disks, close the session2485 rc = session->Close();2486 if (FAILED(rc)) throw rc;2487 fSessionOpen = false;2488 }2489 catch(HRESULT /* aRC */)2490 {2491 if (fSourceHdNeedsClosing)2492 srcHdVBox->Close();2493 2494 if (fSessionOpen)2495 session->Close();2496 2497 throw;2498 }2499 }2500 }2501 catch(HRESULT aRC)2502 {2503 rc = aRC;2504 }2505 2506 if (FAILED(rc))2507 break;2508 2509 } // for (it = pAppliance->m->llVirtualSystems.begin(),2510 2511 if (FAILED(rc))2512 {2513 // with _whatever_ error we've had, do a complete roll-back of2514 // machines and disks we've created; unfortunately this is2515 // not so trivially done...2516 2517 HRESULT rc2;2518 // detach all hard disks from all machines we created2519 list<MyHardDiskAttachment>::iterator itM;2520 for (itM = llHardDiskAttachments.begin();2521 itM != llHardDiskAttachments.end();2522 ++itM)2523 {2524 const MyHardDiskAttachment &mhda = *itM;2525 Bstr bstrUuid(mhda.bstrUuid); // make a copy, Windows can't handle const Bstr2526 rc2 = mVirtualBox->OpenSession(session, bstrUuid);2527 if (SUCCEEDED(rc2))2528 {2529 ComPtr<IMachine> sMachine;2530 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());2531 if (SUCCEEDED(rc2))2532 {2533 rc2 = sMachine->DetachDevice(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);2534 rc2 = sMachine->SaveSettings();2535 }2536 session->Close();2537 }2538 }2539 2540 // now clean up all hard disks we created2541 list< ComPtr<IMedium> >::iterator itHD;2542 for (itHD = llHardDisksCreated.begin();2543 itHD != llHardDisksCreated.end();2544 ++itHD)2545 {2546 ComPtr<IMedium> pDisk = *itHD;2547 ComPtr<IProgress> pProgress;2548 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());2549 rc2 = pProgress->WaitForCompletion(-1);2550 }2551 2552 // finally, deregister and remove all machines2553 list<Bstr>::iterator itID;2554 for (itID = llMachinesRegistered.begin();2555 itID != llMachinesRegistered.end();2556 ++itID)2557 {2558 Bstr bstrGuid = *itID; // make a copy, Windows can't handle const Bstr2559 ComPtr<IMachine> failedMachine;2560 rc2 = mVirtualBox->UnregisterMachine(bstrGuid, failedMachine.asOutParam());2561 if (SUCCEEDED(rc2))2562 rc2 = failedMachine->DeleteSettings();2563 }2564 }2565 2566 // restore the appliance state2567 appLock.acquire();2568 m->state = Data::ApplianceIdle;2569 2570 pTask->rc = rc;2571 2572 if (!pTask->progress.isNull())2573 pTask->progress->notifyComplete(rc);2574 2575 LogFlowFunc(("rc=%Rhrc\n", rc));2576 LogFlowFuncLeave();2577 2578 return VINF_SUCCESS;2579 }2580 2581 /**2582 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values.2583 * Throws HRESULT values on errors!2584 *2585 * @param hdc2586 * @param vd2587 * @param mhda2588 */2589 void Appliance::ConvertDiskAttachmentValues(const HardDiskController &hdc,2590 uint32_t ulAddressOnParent,2591 Bstr &controllerType,2592 int32_t &lChannel,2593 int32_t &lDevice)2594 {2595 switch (hdc.system)2596 {2597 case HardDiskController::IDE:2598 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary2599 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,2600 // the device number can be either 0 or 1, to specify the master or the slave device,2601 // respectively. For the secondary IDE controller, the device number is always 1 because2602 // the master device is reserved for the CD-ROM drive.2603 controllerType = Bstr("IDE Controller");2604 switch (ulAddressOnParent)2605 {2606 case 0: // interpret this as primary master2607 lChannel = (long)0;2608 lDevice = (long)0;2609 break;2610 2611 case 1: // interpret this as primary slave2612 lChannel = (long)0;2613 lDevice = (long)1;2614 break;2615 2616 case 2: // interpret this as secondary master2617 lChannel = (long)1;2618 lDevice = (long)0;2619 break;2620 2621 case 3: // interpret this as secondary slave2622 lChannel = (long)1;2623 lDevice = (long)1;2624 break;2625 2626 default:2627 throw setError(VBOX_E_NOT_SUPPORTED,2628 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), ulAddressOnParent);2629 break;2630 }2631 break;2632 2633 case HardDiskController::SATA:2634 controllerType = Bstr("SATA Controller");2635 lChannel = (long)ulAddressOnParent;2636 lDevice = (long)0;2637 break;2638 2639 case HardDiskController::SCSI:2640 controllerType = Bstr("SCSI Controller");2641 lChannel = (long)ulAddressOnParent;2642 lDevice = (long)0;2643 break;2644 2645 default: break;2646 }2647 }2648 2649 int Appliance::importS3(TaskImportOVF *pTask)2650 {2651 LogFlowFuncEnter();2652 LogFlowFunc(("Appliance %p\n", this));2653 2654 AutoCaller autoCaller(this);2655 if (FAILED(autoCaller.rc())) return autoCaller.rc();2656 2657 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);2658 2659 int vrc = VINF_SUCCESS;2660 RTS3 hS3 = NIL_RTS3;2661 char szOSTmpDir[RTPATH_MAX];2662 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));2663 /* The template for the temporary directory created below */2664 char *pszTmpDir;2665 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);2666 list< pair<Utf8Str, ULONG> > filesList;2667 2668 HRESULT rc = S_OK;2669 try2670 {2671 /* Extract the bucket */2672 Utf8Str tmpPath = pTask->locInfo.strPath;2673 Utf8Str bucket;2674 parseBucket(tmpPath, bucket);2675 2676 /* We need a temporary directory which we can put the all disk images2677 * in */2678 vrc = RTDirCreateTemp(pszTmpDir);2679 if (RT_FAILURE(vrc))2680 throw setError(VBOX_E_FILE_ERROR,2681 tr("Cannot create temporary directory '%s'"), pszTmpDir);2682 2683 /* Add every disks of every virtual system to an internal list */2684 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;2685 for (it = m->virtualSystemDescriptions.begin();2686 it != m->virtualSystemDescriptions.end();2687 ++it)2688 {2689 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);2690 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);2691 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;2692 for (itH = avsdeHDs.begin();2693 itH != avsdeHDs.end();2694 ++itH)2695 {2696 const Utf8Str &strTargetFile = (*itH)->strOvf;2697 if (!strTargetFile.isEmpty())2698 {2699 /* The temporary name of the target disk file */2700 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str()));2701 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB));2702 }2703 }2704 }2705 2706 /* Next we have to download the disk images */2707 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);2708 if(RT_FAILURE(vrc))2709 throw setError(VBOX_E_IPRT_ERROR,2710 tr("Cannot create S3 service handler"));2711 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);2712 2713 /* Download all files */2714 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)2715 {2716 const pair<Utf8Str, ULONG> &s = (*it1);2717 const Utf8Str &strSrcFile = s.first;2718 /* Construct the source file name */2719 char *pszFilename = RTPathFilename(strSrcFile.c_str());2720 /* Advance to the next operation */2721 if (!pTask->progress.isNull())2722 pTask->progress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), s.second);2723 2724 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str());2725 if (RT_FAILURE(vrc))2726 {2727 if(vrc == VERR_S3_CANCELED)2728 throw S_OK; /* todo: !!!!!!!!!!!!! */2729 else if(vrc == VERR_S3_ACCESS_DENIED)2730 throw setError(E_ACCESSDENIED,2731 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);2732 else if(vrc == VERR_S3_NOT_FOUND)2733 throw setError(VBOX_E_FILE_ERROR,2734 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);2735 else2736 throw setError(VBOX_E_IPRT_ERROR,2737 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);2738 }2739 }2740 2741 /* Provide a OVF file (haven't to exist) so the import routine can2742 * figure out where the disk images/manifest file are located. */2743 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));2744 /* Now check if there is an manifest file. This is optional. */2745 Utf8Str strManifestFile = manifestFileName(strTmpOvf);2746 char *pszFilename = RTPathFilename(strManifestFile.c_str());2747 if (!pTask->progress.isNull())2748 pTask->progress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), 1);2749 2750 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */2751 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str());2752 if (RT_SUCCESS(vrc))2753 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0));2754 else if (RT_FAILURE(vrc))2755 {2756 if(vrc == VERR_S3_CANCELED)2757 throw S_OK; /* todo: !!!!!!!!!!!!! */2758 else if(vrc == VERR_S3_NOT_FOUND)2759 vrc = VINF_SUCCESS; /* Not found is ok */2760 else if(vrc == VERR_S3_ACCESS_DENIED)2761 throw setError(E_ACCESSDENIED,2762 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);2763 else2764 throw setError(VBOX_E_IPRT_ERROR,2765 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);2766 }2767 2768 /* Close the connection early */2769 RTS3Destroy(hS3);2770 hS3 = NIL_RTS3;2771 2772 if (!pTask->progress.isNull())2773 pTask->progress->SetNextOperation(BstrFmt(tr("Importing appliance")), m->ulWeightPerOperation);2774 2775 ComObjPtr<Progress> progress;2776 /* Import the whole temporary OVF & the disk images */2777 LocationInfo li;2778 li.strPath = strTmpOvf;2779 rc = importImpl(li, progress);2780 if (FAILED(rc)) throw rc;2781 2782 /* Unlock the appliance for the fs import thread */2783 appLock.release();2784 /* Wait until the import is done, but report the progress back to the2785 caller */2786 ComPtr<IProgress> progressInt(progress);2787 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */2788 2789 /* Again lock the appliance for the next steps */2790 appLock.acquire();2791 }2792 catch(HRESULT aRC)2793 {2794 rc = aRC;2795 }2796 /* Cleanup */2797 RTS3Destroy(hS3);2798 /* Delete all files which where temporary created */2799 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)2800 {2801 const char *pszFilePath = (*it1).first.c_str();2802 if (RTPathExists(pszFilePath))2803 {2804 vrc = RTFileDelete(pszFilePath);2805 if(RT_FAILURE(vrc))2806 rc = setError(VBOX_E_FILE_ERROR,2807 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);2808 }2809 }2810 /* Delete the temporary directory */2811 if (RTPathExists(pszTmpDir))2812 {2813 vrc = RTDirRemove(pszTmpDir);2814 if(RT_FAILURE(vrc))2815 rc = setError(VBOX_E_FILE_ERROR,2816 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);2817 }2818 if (pszTmpDir)2819 RTStrFree(pszTmpDir);2820 2821 pTask->rc = rc;2822 2823 if (!pTask->progress.isNull())2824 pTask->progress->notifyComplete(rc);2825 2826 LogFlowFunc(("rc=%Rhrc\n", rc));2827 LogFlowFuncLeave();2828 2829 return VINF_SUCCESS;2830 }2831 2832 933 int Appliance::TaskExportOVF::startThread() 2833 934 { -
trunk/src/VBox/Main/ApplianceImplImport.cpp
r27855 r27882 51 51 //////////////////////////////////////////////////////////////////////////////// 52 52 // 53 // Internal helpers54 //55 ////////////////////////////////////////////////////////////////////////////////56 57 static const struct58 {59 CIMOSType_T cim;60 const char *pcszVbox;61 }62 g_osTypes[] =63 {64 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },65 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },66 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },67 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },68 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },69 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },70 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },71 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },72 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },73 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris },74 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_OpenSolaris },75 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },76 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },77 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },78 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },79 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },80 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },81 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },82 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },83 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },84 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },85 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },86 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },87 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },88 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },89 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },90 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },91 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },92 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },93 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },94 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 },95 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },96 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },97 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },98 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },99 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },100 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },101 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },102 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},103 104 // { CIMOSType_CIMOS_TurboLinux_64, },105 106 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },107 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },108 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },109 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },110 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },111 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },112 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },113 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },114 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },115 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 },116 { CIMOSType_CIMOS_Linux_64, SchemaDefs_OSTypeId_Linux26_64 }117 };118 119 /* Pattern structure for matching the OS type description field */120 struct osTypePattern121 {122 const char *pcszPattern;123 const char *pcszVbox;124 };125 126 /* These are the 32-Bit ones. They are sorted by priority. */127 static const osTypePattern g_osTypesPattern[] =128 {129 {"Windows NT", SchemaDefs_OSTypeId_WindowsNT4},130 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP},131 {"Windows 2000", SchemaDefs_OSTypeId_Windows2000},132 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003},133 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista},134 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008},135 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE},136 {"Novell", SchemaDefs_OSTypeId_OpenSUSE},137 {"Red Hat", SchemaDefs_OSTypeId_RedHat},138 {"Mandriva", SchemaDefs_OSTypeId_Mandriva},139 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu},140 {"Debian", SchemaDefs_OSTypeId_Debian},141 {"QNX", SchemaDefs_OSTypeId_QNX},142 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24},143 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26},144 {"Linux", SchemaDefs_OSTypeId_Linux},145 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris},146 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris},147 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD},148 {"NetBSD", SchemaDefs_OSTypeId_NetBSD},149 {"Windows 95", SchemaDefs_OSTypeId_Windows95},150 {"Windows 98", SchemaDefs_OSTypeId_Windows98},151 {"Windows Me", SchemaDefs_OSTypeId_WindowsMe},152 {"Windows 3.", SchemaDefs_OSTypeId_Windows31},153 {"DOS", SchemaDefs_OSTypeId_DOS},154 {"OS2", SchemaDefs_OSTypeId_OS2}155 };156 157 /* These are the 64-Bit ones. They are sorted by priority. */158 static const osTypePattern g_osTypesPattern64[] =159 {160 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP_64},161 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003_64},162 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista_64},163 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008_64},164 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE_64},165 {"Novell", SchemaDefs_OSTypeId_OpenSUSE_64},166 {"Red Hat", SchemaDefs_OSTypeId_RedHat_64},167 {"Mandriva", SchemaDefs_OSTypeId_Mandriva_64},168 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu_64},169 {"Debian", SchemaDefs_OSTypeId_Debian_64},170 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24_64},171 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26_64},172 {"Linux", SchemaDefs_OSTypeId_Linux26_64},173 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris_64},174 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris_64},175 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD_64},176 };177 178 /**179 * Private helper func that suggests a VirtualBox guest OS type180 * for the given OVF operating system type.181 * @param osTypeVBox182 * @param c183 * @param cStr184 */185 void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c, const Utf8Str &cStr)186 {187 /* First check if the type is other/other_64 */188 if (c == CIMOSType_CIMOS_Other)189 {190 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern); ++i)191 if (cStr.contains (g_osTypesPattern[i].pcszPattern, Utf8Str::CaseInsensitive))192 {193 strType = g_osTypesPattern[i].pcszVbox;194 return;195 }196 }197 else if (c == CIMOSType_CIMOS_Other_64)198 {199 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern64); ++i)200 if (cStr.contains (g_osTypesPattern64[i].pcszPattern, Utf8Str::CaseInsensitive))201 {202 strType = g_osTypesPattern64[i].pcszVbox;203 return;204 }205 }206 207 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)208 {209 if (c == g_osTypes[i].cim)210 {211 strType = g_osTypes[i].pcszVbox;212 return;213 }214 }215 216 strType = SchemaDefs_OSTypeId_Other;217 }218 219 /**220 * Private helper func that suggests a VirtualBox guest OS type221 * for the given OVF operating system type.222 * @param osTypeVBox223 * @param c224 */225 CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)226 {227 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)228 {229 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))230 return g_osTypes[i].cim;231 }232 233 return CIMOSType_CIMOS_Other;234 }235 236 ////////////////////////////////////////////////////////////////////////////////237 //238 // IVirtualBox public methods239 //240 ////////////////////////////////////////////////////////////////////////////////241 242 // This code is here so we won't have to include the appliance headers in the243 // IVirtualBox implementation.244 245 /**246 * Implementation for IVirtualBox::createAppliance.247 *248 * @param anAppliance IAppliance object created if S_OK is returned.249 * @return S_OK or error.250 */251 STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)252 {253 HRESULT rc;254 255 ComObjPtr<Appliance> appliance;256 appliance.createObject();257 rc = appliance->init(this);258 259 if (SUCCEEDED(rc))260 appliance.queryInterfaceTo(anAppliance);261 262 return rc;263 }264 265 ////////////////////////////////////////////////////////////////////////////////266 //267 // Appliance constructor / destructor268 //269 ////////////////////////////////////////////////////////////////////////////////270 271 Appliance::Appliance()272 : mVirtualBox(NULL)273 {274 }275 276 Appliance::~Appliance()277 {278 }279 280 /**281 * Appliance COM initializer.282 * @param283 * @return284 */285 HRESULT Appliance::init(VirtualBox *aVirtualBox)286 {287 /* Enclose the state transition NotReady->InInit->Ready */288 AutoInitSpan autoInitSpan(this);289 AssertReturn(autoInitSpan.isOk(), E_FAIL);290 291 /* Weak reference to a VirtualBox object */292 unconst(mVirtualBox) = aVirtualBox;293 294 // initialize data295 m = new Data;296 297 /* Confirm a successful initialization */298 autoInitSpan.setSucceeded();299 300 return S_OK;301 }302 303 /**304 * Appliance COM uninitializer.305 * @return306 */307 void Appliance::uninit()308 {309 /* Enclose the state transition Ready->InUninit->NotReady */310 AutoUninitSpan autoUninitSpan(this);311 if (autoUninitSpan.uninitDone())312 return;313 314 delete m;315 m = NULL;316 }317 318 ////////////////////////////////////////////////////////////////////////////////319 //320 53 // IAppliance public methods 321 54 // 322 55 //////////////////////////////////////////////////////////////////////////////// 323 324 /**325 * Public method implementation.326 * @param327 * @return328 */329 STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)330 {331 if (!aPath)332 return E_POINTER;333 334 AutoCaller autoCaller(this);335 if (FAILED(autoCaller.rc())) return autoCaller.rc();336 337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);338 339 if (!isApplianceIdle())340 return E_ACCESSDENIED;341 342 Bstr bstrPath(m->locInfo.strPath);343 bstrPath.cloneTo(aPath);344 345 return S_OK;346 }347 348 /**349 * Public method implementation.350 * @param351 * @return352 */353 STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))354 {355 CheckComArgOutSafeArrayPointerValid(aDisks);356 357 AutoCaller autoCaller(this);358 if (FAILED(autoCaller.rc())) return autoCaller.rc();359 360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);361 362 if (!isApplianceIdle())363 return E_ACCESSDENIED;364 365 if (m->pReader) // OVFReader instantiated?366 {367 size_t c = m->pReader->m_mapDisks.size();368 com::SafeArray<BSTR> sfaDisks(c);369 370 DiskImagesMap::const_iterator it;371 size_t i = 0;372 for (it = m->pReader->m_mapDisks.begin();373 it != m->pReader->m_mapDisks.end();374 ++it, ++i)375 {376 // create a string representing this disk377 const DiskImage &d = it->second;378 char *psz = NULL;379 RTStrAPrintf(&psz,380 "%s\t"381 "%RI64\t"382 "%RI64\t"383 "%s\t"384 "%s\t"385 "%RI64\t"386 "%RI64\t"387 "%s",388 d.strDiskId.c_str(),389 d.iCapacity,390 d.iPopulatedSize,391 d.strFormat.c_str(),392 d.strHref.c_str(),393 d.iSize,394 d.iChunkSize,395 d.strCompression.c_str());396 Utf8Str utf(psz);397 Bstr bstr(utf);398 // push to safearray399 bstr.cloneTo(&sfaDisks[i]);400 RTStrFree(psz);401 }402 403 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));404 }405 406 return S_OK;407 }408 409 /**410 * Public method implementation.411 * @param412 * @return413 */414 STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))415 {416 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);417 418 AutoCaller autoCaller(this);419 if (FAILED(autoCaller.rc())) return autoCaller.rc();420 421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);422 423 if (!isApplianceIdle())424 return E_ACCESSDENIED;425 426 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);427 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));428 429 return S_OK;430 }431 432 /**433 * Public method implementation.434 * @param path435 * @return436 */437 STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress)438 {439 if (!path) return E_POINTER;440 CheckComArgOutPointerValid(aProgress);441 442 AutoCaller autoCaller(this);443 if (FAILED(autoCaller.rc())) return autoCaller.rc();444 445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);446 447 if (!isApplianceIdle())448 return E_ACCESSDENIED;449 450 if (m->pReader)451 {452 delete m->pReader;453 m->pReader = NULL;454 }455 456 // see if we can handle this file; for now we insist it has an ".ovf" extension457 Utf8Str strPath (path);458 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))459 return setError(VBOX_E_FILE_ERROR,460 tr("Appliance file must have .ovf extension"));461 462 ComObjPtr<Progress> progress;463 HRESULT rc = S_OK;464 try465 {466 /* Parse all necessary info out of the URI */467 parseURI(strPath, m->locInfo);468 rc = readImpl(m->locInfo, progress);469 }470 catch (HRESULT aRC)471 {472 rc = aRC;473 }474 475 if (SUCCEEDED(rc))476 /* Return progress to the caller */477 progress.queryInterfaceTo(aProgress);478 479 return S_OK;480 }481 56 482 57 /** … … 980 555 } 981 556 982 STDMETHODIMP Appliance::CreateVFSExplorer(IN_BSTR aURI, IVFSExplorer **aExplorer)983 {984 CheckComArgOutPointerValid(aExplorer);985 986 AutoCaller autoCaller(this);987 if (FAILED(autoCaller.rc())) return autoCaller.rc();988 989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);990 991 ComObjPtr<VFSExplorer> explorer;992 HRESULT rc = S_OK;993 try994 {995 Utf8Str uri(aURI);996 /* Check which kind of export the user has requested */997 LocationInfo li;998 parseURI(uri, li);999 /* Create the explorer object */1000 explorer.createObject();1001 rc = explorer->init(li.storageType, li.strPath, li.strHostname, li.strUsername, li.strPassword, mVirtualBox);1002 }1003 catch (HRESULT aRC)1004 {1005 rc = aRC;1006 }1007 1008 if (SUCCEEDED(rc))1009 /* Return explorer to the caller */1010 explorer.queryInterfaceTo(aExplorer);1011 1012 return rc;1013 }1014 1015 /**1016 * Public method implementation.1017 * @return1018 */1019 STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))1020 {1021 if (ComSafeArrayOutIsNull(aWarnings))1022 return E_POINTER;1023 1024 AutoCaller autoCaller(this);1025 if (FAILED(autoCaller.rc())) return autoCaller.rc();1026 1027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1028 1029 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());1030 1031 list<Utf8Str>::const_iterator it;1032 size_t i = 0;1033 for (it = m->llWarnings.begin();1034 it != m->llWarnings.end();1035 ++it, ++i)1036 {1037 Bstr bstr = *it;1038 bstr.cloneTo(&sfaWarnings[i]);1039 }1040 1041 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));1042 1043 return S_OK;1044 }1045 1046 557 //////////////////////////////////////////////////////////////////////////////// 1047 558 // … … 1051 562 1052 563 /** 1053 * Returns true if the appliance is in "idle" state. This should always be the1054 * case unless an import or export is currently in progress. Similar to machine1055 * states, this permits the Appliance implementation code to let go of the1056 * Appliance object lock while a time-consuming disk conversion is in progress1057 * without exposing the appliance to conflicting calls.1058 564 * 1059 * This sets an error on "this" (the appliance) and returns false if the appliance 1060 * is busy. The caller should then return E_ACCESSDENIED. 1061 * 1062 * Must be called from under the object lock! 1063 * 565 * @param aLocInfo 566 * @param aProgress 1064 567 * @return 1065 568 */ 1066 bool Appliance::isApplianceIdle() const1067 {1068 if (m->state == Data::ApplianceImporting)1069 setError(VBOX_E_INVALID_OBJECT_STATE, "The appliance is busy importing files");1070 else if (m->state == Data::ApplianceExporting)1071 setError(VBOX_E_INVALID_OBJECT_STATE, "The appliance is busy exporting files");1072 else1073 return true;1074 1075 return false;1076 }1077 1078 HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const1079 {1080 IMachine *machine = NULL;1081 char *tmpName = RTStrDup(aName.c_str());1082 int i = 1;1083 /* @todo: Maybe too cost-intensive; try to find a lighter way */1084 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)1085 {1086 RTStrFree(tmpName);1087 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);1088 ++i;1089 }1090 aName = tmpName;1091 RTStrFree(tmpName);1092 1093 return S_OK;1094 }1095 1096 HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const1097 {1098 IMedium *harddisk = NULL;1099 char *tmpName = RTStrDup(aName.c_str());1100 int i = 1;1101 /* Check if the file exists or if a file with this path is registered1102 * already */1103 /* @todo: Maybe too cost-intensive; try to find a lighter way */1104 while (RTPathExists(tmpName) ||1105 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)1106 {1107 RTStrFree(tmpName);1108 char *tmpDir = RTStrDup(aName.c_str());1109 RTPathStripFilename(tmpDir);;1110 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));1111 RTPathStripExt(tmpFile);1112 const char *tmpExt = RTPathExt(aName.c_str());1113 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);1114 RTStrFree(tmpFile);1115 RTStrFree(tmpDir);1116 ++i;1117 }1118 aName = tmpName;1119 RTStrFree(tmpName);1120 1121 return S_OK;1122 }1123 1124 /**1125 * Called from the import and export background threads to synchronize the second1126 * background disk thread's progress object with the current progress object so1127 * that the user interface sees progress correctly and that cancel signals are1128 * passed on to the second thread.1129 * @param pProgressThis Progress object of the current thread.1130 * @param pProgressAsync Progress object of asynchronous task running in background.1131 */1132 void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis,1133 ComPtr<IProgress> &pProgressAsync)1134 {1135 HRESULT rc;1136 1137 // now loop until the asynchronous operation completes and then report its result1138 BOOL fCompleted;1139 BOOL fCanceled;1140 ULONG currentPercent;1141 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted))))1142 {1143 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled);1144 if (FAILED(rc)) throw rc;1145 if (fCanceled)1146 {1147 pProgressAsync->Cancel();1148 break;1149 }1150 1151 rc = pProgressAsync->COMGETTER(Percent(¤tPercent));1152 if (FAILED(rc)) throw rc;1153 if (!pProgressThis.isNull())1154 pProgressThis->SetCurrentOperationProgress(currentPercent);1155 if (fCompleted)1156 break;1157 1158 /* Make sure the loop is not too tight */1159 rc = pProgressAsync->WaitForCompletion(100);1160 if (FAILED(rc)) throw rc;1161 }1162 // report result of asynchronous operation1163 LONG iRc;1164 rc = pProgressAsync->COMGETTER(ResultCode)(&iRc);1165 if (FAILED(rc)) throw rc;1166 1167 1168 // if the thread of the progress object has an error, then1169 // retrieve the error info from there, or it'll be lost1170 if (FAILED(iRc))1171 {1172 ProgressErrorInfo info(pProgressAsync);1173 Utf8Str str(info.getText());1174 const char *pcsz = str.c_str();1175 HRESULT rc2 = setError(iRc, pcsz);1176 throw rc2;1177 }1178 }1179 1180 void Appliance::addWarning(const char* aWarning, ...)1181 {1182 va_list args;1183 va_start(args, aWarning);1184 Utf8StrFmtVA str(aWarning, args);1185 va_end(args);1186 m->llWarnings.push_back(str);1187 }1188 1189 void Appliance::disksWeight(uint32_t &ulTotalMB, uint32_t &cDisks) const1190 {1191 ulTotalMB = 0;1192 cDisks = 0;1193 /* Weigh the disk images according to their sizes */1194 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;1195 for (it = m->virtualSystemDescriptions.begin();1196 it != m->virtualSystemDescriptions.end();1197 ++it)1198 {1199 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);1200 /* One for every hard disk of the Virtual System */1201 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);1202 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;1203 for (itH = avsdeHDs.begin();1204 itH != avsdeHDs.end();1205 ++itH)1206 {1207 const VirtualSystemDescriptionEntry *pHD = *itH;1208 ulTotalMB += pHD->ulSizeMB;1209 ++cDisks;1210 }1211 }1212 1213 }1214 1215 HRESULT Appliance::setUpProgressFS(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)1216 {1217 HRESULT rc;1218 1219 /* Create the progress object */1220 pProgress.createObject();1221 1222 /* Weigh the disk images according to their sizes */1223 uint32_t ulTotalMB;1224 uint32_t cDisks;1225 disksWeight(ulTotalMB, cDisks);1226 1227 ULONG cOperations = 1 + cDisks; // one op per disk plus 1 for the XML1228 1229 ULONG ulTotalOperationsWeight;1230 if (ulTotalMB)1231 {1232 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for the XML1233 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;1234 }1235 else1236 {1237 // no disks to export:1238 ulTotalOperationsWeight = 1;1239 m->ulWeightPerOperation = 1;1240 }1241 1242 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",1243 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));1244 1245 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),1246 bstrDescription,1247 TRUE /* aCancelable */,1248 cOperations, // ULONG cOperations,1249 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,1250 bstrDescription, // CBSTR bstrFirstOperationDescription,1251 m->ulWeightPerOperation); // ULONG ulFirstOperationWeight,1252 return rc;1253 }1254 1255 HRESULT Appliance::setUpProgressImportS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)1256 {1257 HRESULT rc;1258 1259 /* Create the progress object */1260 pProgress.createObject();1261 1262 /* Weigh the disk images according to their sizes */1263 uint32_t ulTotalMB;1264 uint32_t cDisks;1265 disksWeight(ulTotalMB, cDisks);1266 1267 ULONG cOperations = 1 + 1 + 1 + cDisks; // one op per disk plus 1 for init, plus 1 for the manifest file & 1 plus for the import */1268 1269 ULONG ulTotalOperationsWeight = ulTotalMB;1270 if (!ulTotalOperationsWeight)1271 // no disks to export:1272 ulTotalOperationsWeight = 1;1273 1274 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import1275 ulTotalOperationsWeight += ulImportWeight;1276 1277 m->ulWeightPerOperation = ulImportWeight; /* save for using later */1278 1279 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init1280 ulTotalOperationsWeight += ulInitWeight;1281 1282 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",1283 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));1284 1285 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),1286 bstrDescription,1287 TRUE /* aCancelable */,1288 cOperations, // ULONG cOperations,1289 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,1290 Bstr(tr("Init")), // CBSTR bstrFirstOperationDescription,1291 ulInitWeight); // ULONG ulFirstOperationWeight,1292 return rc;1293 }1294 1295 HRESULT Appliance::setUpProgressWriteS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)1296 {1297 HRESULT rc;1298 1299 /* Create the progress object */1300 pProgress.createObject();1301 1302 /* Weigh the disk images according to their sizes */1303 uint32_t ulTotalMB;1304 uint32_t cDisks;1305 disksWeight(ulTotalMB, cDisks);1306 1307 ULONG cOperations = 1 + 1 + 1 + cDisks; // one op per disk plus 1 for the OVF, plus 1 for the mf & 1 plus to the temporary creation */1308 1309 ULONG ulTotalOperationsWeight;1310 if (ulTotalMB)1311 {1312 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for OVF file upload (we didn't know the size at this point)1313 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;1314 }1315 else1316 {1317 // no disks to export:1318 ulTotalOperationsWeight = 1;1319 m->ulWeightPerOperation = 1;1320 }1321 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */1322 ulTotalOperationsWeight += ulOVFCreationWeight;1323 1324 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",1325 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));1326 1327 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),1328 bstrDescription,1329 TRUE /* aCancelable */,1330 cOperations, // ULONG cOperations,1331 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,1332 bstrDescription, // CBSTR bstrFirstOperationDescription,1333 ulOVFCreationWeight); // ULONG ulFirstOperationWeight,1334 return rc;1335 }1336 1337 void Appliance::parseURI(Utf8Str strUri, LocationInfo &locInfo) const1338 {1339 /* Check the URI for the protocol */1340 if (strUri.startsWith("file://", Utf8Str::CaseInsensitive)) /* File based */1341 {1342 locInfo.storageType = VFSType_File;1343 strUri = strUri.substr(sizeof("file://") - 1);1344 }1345 else if (strUri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */1346 {1347 locInfo.storageType = VFSType_S3;1348 strUri = strUri.substr(sizeof("SunCloud://") - 1);1349 }1350 else if (strUri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */1351 {1352 locInfo.storageType = VFSType_S3;1353 strUri = strUri.substr(sizeof("S3://") - 1);1354 }1355 else if (strUri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */1356 throw E_NOTIMPL;1357 1358 /* Not necessary on a file based URI */1359 if (locInfo.storageType != VFSType_File)1360 {1361 size_t uppos = strUri.find("@"); /* username:password combo */1362 if (uppos != Utf8Str::npos)1363 {1364 locInfo.strUsername = strUri.substr(0, uppos);1365 strUri = strUri.substr(uppos + 1);1366 size_t upos = locInfo.strUsername.find(":");1367 if (upos != Utf8Str::npos)1368 {1369 locInfo.strPassword = locInfo.strUsername.substr(upos + 1);1370 locInfo.strUsername = locInfo.strUsername.substr(0, upos);1371 }1372 }1373 size_t hpos = strUri.find("/"); /* hostname part */1374 if (hpos != Utf8Str::npos)1375 {1376 locInfo.strHostname = strUri.substr(0, hpos);1377 strUri = strUri.substr(hpos);1378 }1379 }1380 1381 locInfo.strPath = strUri;1382 }1383 1384 void Appliance::parseBucket(Utf8Str &aPath, Utf8Str &aBucket) const1385 {1386 /* Buckets are S3 specific. So parse the bucket out of the file path */1387 if (!aPath.startsWith("/"))1388 throw setError(E_INVALIDARG,1389 tr("The path '%s' must start with /"), aPath.c_str());1390 size_t bpos = aPath.find("/", 1);1391 if (bpos != Utf8Str::npos)1392 {1393 aBucket = aPath.substr(1, bpos - 1); /* The bucket without any slashes */1394 aPath = aPath.substr(bpos); /* The rest of the file path */1395 }1396 /* If there is no bucket name provided reject it */1397 if (aBucket.isEmpty())1398 throw setError(E_INVALIDARG,1399 tr("You doesn't provide a bucket name in the URI '%s'"), aPath.c_str());1400 }1401 1402 Utf8Str Appliance::manifestFileName(Utf8Str aPath) const1403 {1404 /* Get the name part */1405 char *pszMfName = RTStrDup(RTPathFilename(aPath.c_str()));1406 /* Strip any extensions */1407 RTPathStripExt(pszMfName);1408 /* Path without the filename */1409 aPath.stripFilename();1410 /* Format the manifest path */1411 Utf8StrFmt strMfFile("%s/%s.mf", aPath.c_str(), pszMfName);1412 RTStrFree(pszMfName);1413 return strMfFile;1414 }1415 1416 /* static */1417 int Appliance::TaskOVF::updateProgress(unsigned uPercent, void *pvUser)1418 {1419 Appliance::TaskOVF* pTask = *(Appliance::TaskOVF**)pvUser;1420 1421 if ( pTask1422 && !pTask->progress.isNull())1423 {1424 BOOL fCanceled;1425 pTask->progress->COMGETTER(Canceled)(&fCanceled);1426 if (fCanceled)1427 return -1;1428 pTask->progress->SetCurrentOperationProgress(uPercent);1429 }1430 return VINF_SUCCESS;1431 }1432 1433 569 HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress) 1434 570 { … … 1478 614 1479 615 /** 1480 * Implementation of the import code. This gets called from the public Appliance::ImportMachines() 1481 * method as well as Appliance::importS3(). 1482 * @param aLocInfo 1483 * @param aProgress 616 * 617 * @param pTask 1484 618 * @return 1485 619 */ 1486 HRESULT Appliance::importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)1487 {1488 /* Initialize our worker task */1489 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this));1490 /* What should the task do */1491 task->taskType = TaskImportOVF::Import;1492 /* Copy the current location info to the task */1493 task->locInfo = aLocInfo;1494 1495 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),1496 aLocInfo.strPath.c_str());1497 1498 HRESULT rc = S_OK;1499 1500 /* todo: This progress init stuff should be done a little bit more generic */1501 if (task->locInfo.storageType == VFSType_File)1502 rc = setUpProgressFS(aProgress, progressDesc);1503 else1504 rc = setUpProgressImportS3(aProgress, progressDesc);1505 if (FAILED(rc)) throw rc;1506 1507 task->progress = aProgress;1508 1509 rc = task->startThread();1510 if (FAILED(rc)) throw rc;1511 1512 /* Don't destruct on success */1513 task.release();1514 1515 return rc;1516 }1517 1518 /**1519 * Worker thread implementation for Read() (ovf reader).1520 * @param aThread1521 * @param pvUser1522 */1523 /* static */1524 DECLCALLBACK(int) Appliance::taskThreadImportOVF(RTTHREAD /* aThread */, void *pvUser)1525 {1526 std::auto_ptr<TaskImportOVF> task(static_cast<TaskImportOVF*>(pvUser));1527 AssertReturn(task.get(), VERR_GENERAL_FAILURE);1528 1529 Appliance *pAppliance = task->pAppliance;1530 1531 LogFlowFuncEnter();1532 LogFlowFunc(("Appliance %p\n", pAppliance));1533 1534 HRESULT rc = S_OK;1535 1536 switch (task->taskType)1537 {1538 case TaskImportOVF::Read:1539 {1540 if (task->locInfo.storageType == VFSType_File)1541 rc = pAppliance->readFS(task.get());1542 else if (task->locInfo.storageType == VFSType_S3)1543 rc = pAppliance->readS3(task.get());1544 break;1545 }1546 case TaskImportOVF::Import:1547 {1548 if (task->locInfo.storageType == VFSType_File)1549 rc = pAppliance->importFS(task.get());1550 else if (task->locInfo.storageType == VFSType_S3)1551 rc = pAppliance->importS3(task.get());1552 break;1553 }1554 }1555 1556 LogFlowFunc(("rc=%Rhrc\n", rc));1557 LogFlowFuncLeave();1558 1559 return VINF_SUCCESS;1560 }1561 1562 int Appliance::TaskImportOVF::startThread()1563 {1564 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportOVF, this,1565 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,1566 "Appliance::Task");1567 1568 ComAssertMsgRCRet(vrc,1569 ("Could not create taskThreadImportOVF (%Rrc)\n", vrc), E_FAIL);1570 1571 return S_OK;1572 }1573 1574 620 int Appliance::readFS(TaskImportOVF *pTask) 1575 621 { … … 1619 665 } 1620 666 667 /** 668 * 669 * @param pTask 670 * @return 671 */ 1621 672 int Appliance::readS3(TaskImportOVF *pTask) 1622 673 { … … 1743 794 } 1744 795 796 /** 797 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values. 798 * Throws HRESULT values on errors! 799 * 800 * @param hdc 801 * @param vd 802 * @param mhda 803 */ 804 void Appliance::convertDiskAttachmentValues(const HardDiskController &hdc, 805 uint32_t ulAddressOnParent, 806 Bstr &controllerType, 807 int32_t &lChannel, 808 int32_t &lDevice) 809 { 810 switch (hdc.system) 811 { 812 case HardDiskController::IDE: 813 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary 814 // or secondary IDE controller, respectively. For the primary controller of the IDE bus, 815 // the device number can be either 0 or 1, to specify the master or the slave device, 816 // respectively. For the secondary IDE controller, the device number is always 1 because 817 // the master device is reserved for the CD-ROM drive. 818 controllerType = Bstr("IDE Controller"); 819 switch (ulAddressOnParent) 820 { 821 case 0: // interpret this as primary master 822 lChannel = (long)0; 823 lDevice = (long)0; 824 break; 825 826 case 1: // interpret this as primary slave 827 lChannel = (long)0; 828 lDevice = (long)1; 829 break; 830 831 case 2: // interpret this as secondary master 832 lChannel = (long)1; 833 lDevice = (long)0; 834 break; 835 836 case 3: // interpret this as secondary slave 837 lChannel = (long)1; 838 lDevice = (long)1; 839 break; 840 841 default: 842 throw setError(VBOX_E_NOT_SUPPORTED, 843 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), ulAddressOnParent); 844 break; 845 } 846 break; 847 848 case HardDiskController::SATA: 849 controllerType = Bstr("SATA Controller"); 850 lChannel = (long)ulAddressOnParent; 851 lDevice = (long)0; 852 break; 853 854 case HardDiskController::SCSI: 855 controllerType = Bstr("SCSI Controller"); 856 lChannel = (long)ulAddressOnParent; 857 lDevice = (long)0; 858 break; 859 860 default: break; 861 } 862 } 863 864 /** 865 * Implementation of the import code. This gets called from the public Appliance::ImportMachines() 866 * method as well as Appliance::importS3(). 867 * @param aLocInfo 868 * @param aProgress 869 * @return 870 */ 871 HRESULT Appliance::importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress) 872 { 873 /* Initialize our worker task */ 874 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this)); 875 /* What should the task do */ 876 task->taskType = TaskImportOVF::Import; 877 /* Copy the current location info to the task */ 878 task->locInfo = aLocInfo; 879 880 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"), 881 aLocInfo.strPath.c_str()); 882 883 HRESULT rc = S_OK; 884 885 /* todo: This progress init stuff should be done a little bit more generic */ 886 if (task->locInfo.storageType == VFSType_File) 887 rc = setUpProgressFS(aProgress, progressDesc); 888 else 889 rc = setUpProgressImportS3(aProgress, progressDesc); 890 if (FAILED(rc)) throw rc; 891 892 task->progress = aProgress; 893 894 rc = task->startThread(); 895 if (FAILED(rc)) throw rc; 896 897 /* Don't destruct on success */ 898 task.release(); 899 900 return rc; 901 } 902 903 /** 904 * Worker thread implementation for Read() (ovf reader). 905 * @param aThread 906 * @param pvUser 907 */ 908 /* static */ 909 DECLCALLBACK(int) Appliance::taskThreadImportOVF(RTTHREAD /* aThread */, void *pvUser) 910 { 911 std::auto_ptr<TaskImportOVF> task(static_cast<TaskImportOVF*>(pvUser)); 912 AssertReturn(task.get(), VERR_GENERAL_FAILURE); 913 914 Appliance *pAppliance = task->pAppliance; 915 916 LogFlowFuncEnter(); 917 LogFlowFunc(("Appliance %p\n", pAppliance)); 918 919 switch (task->taskType) 920 { 921 case TaskImportOVF::Read: 922 { 923 if (task->locInfo.storageType == VFSType_File) 924 pAppliance->readFS(task.get()); 925 else if (task->locInfo.storageType == VFSType_S3) 926 pAppliance->readS3(task.get()); 927 break; 928 } 929 case TaskImportOVF::Import: 930 { 931 if (task->locInfo.storageType == VFSType_File) 932 pAppliance->importFS(task.get()); 933 else if (task->locInfo.storageType == VFSType_S3) 934 pAppliance->importS3(task.get()); 935 break; 936 } 937 } 938 939 LogFlowFuncLeave(); 940 941 return VINF_SUCCESS; 942 } 943 944 /** 945 * 946 * @return 947 */ 948 int Appliance::TaskImportOVF::startThread() 949 { 950 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportOVF, this, 951 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, 952 "Appliance::Task"); 953 954 ComAssertMsgRCRet(vrc, 955 ("Could not create taskThreadImportOVF (%Rrc)\n", vrc), E_FAIL); 956 957 return S_OK; 958 } 959 960 /** 961 * 962 * @param pTask 963 * @return 964 */ 1745 965 int Appliance::importFS(TaskImportOVF *pTask) 1746 966 { … … 2271 1491 mhda.pMachine = pNewMachine; 2272 1492 2273 ConvertDiskAttachmentValues(*pController,1493 convertDiskAttachmentValues(*pController, 2274 1494 2, // interpreted as secondary master 2275 1495 mhda.controllerType, // Bstr … … 2461 1681 mhda.pMachine = pNewMachine; 2462 1682 2463 ConvertDiskAttachmentValues(hdc,1683 convertDiskAttachmentValues(hdc, 2464 1684 vd.ulAddressOnParent, 2465 1685 mhda.controllerType, // Bstr … … 2580 1800 2581 1801 /** 2582 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values.2583 * Throws HRESULT values on errors!2584 1802 * 2585 * @param hdc 2586 * @param vd 2587 * @param mhda 1803 * @param pTask 1804 * @return 2588 1805 */ 2589 void Appliance::ConvertDiskAttachmentValues(const HardDiskController &hdc,2590 uint32_t ulAddressOnParent,2591 Bstr &controllerType,2592 int32_t &lChannel,2593 int32_t &lDevice)2594 {2595 switch (hdc.system)2596 {2597 case HardDiskController::IDE:2598 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary2599 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,2600 // the device number can be either 0 or 1, to specify the master or the slave device,2601 // respectively. For the secondary IDE controller, the device number is always 1 because2602 // the master device is reserved for the CD-ROM drive.2603 controllerType = Bstr("IDE Controller");2604 switch (ulAddressOnParent)2605 {2606 case 0: // interpret this as primary master2607 lChannel = (long)0;2608 lDevice = (long)0;2609 break;2610 2611 case 1: // interpret this as primary slave2612 lChannel = (long)0;2613 lDevice = (long)1;2614 break;2615 2616 case 2: // interpret this as secondary master2617 lChannel = (long)1;2618 lDevice = (long)0;2619 break;2620 2621 case 3: // interpret this as secondary slave2622 lChannel = (long)1;2623 lDevice = (long)1;2624 break;2625 2626 default:2627 throw setError(VBOX_E_NOT_SUPPORTED,2628 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), ulAddressOnParent);2629 break;2630 }2631 break;2632 2633 case HardDiskController::SATA:2634 controllerType = Bstr("SATA Controller");2635 lChannel = (long)ulAddressOnParent;2636 lDevice = (long)0;2637 break;2638 2639 case HardDiskController::SCSI:2640 controllerType = Bstr("SCSI Controller");2641 lChannel = (long)ulAddressOnParent;2642 lDevice = (long)0;2643 break;2644 2645 default: break;2646 }2647 }2648 2649 1806 int Appliance::importS3(TaskImportOVF *pTask) 2650 1807 { … … 2830 1987 } 2831 1988 2832 int Appliance::TaskExportOVF::startThread()2833 {2834 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,2835 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,2836 "Appliance::Task");2837 2838 ComAssertMsgRCRet(vrc,2839 ("Could not create taskThreadWriteOVF (%Rrc)\n", vrc), E_FAIL);2840 2841 return S_OK;2842 }2843 2844 ////////////////////////////////////////////////////////////////////////////////2845 //2846 // IVirtualSystemDescription constructor / destructor2847 //2848 ////////////////////////////////////////////////////////////////////////////////2849 2850 DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)2851 2852 /**2853 * COM initializer.2854 * @return2855 */2856 HRESULT VirtualSystemDescription::init()2857 {2858 /* Enclose the state transition NotReady->InInit->Ready */2859 AutoInitSpan autoInitSpan(this);2860 AssertReturn(autoInitSpan.isOk(), E_FAIL);2861 2862 /* Initialize data */2863 m = new Data();2864 2865 /* Confirm a successful initialization */2866 autoInitSpan.setSucceeded();2867 return S_OK;2868 }2869 2870 /**2871 * COM uninitializer.2872 */2873 2874 void VirtualSystemDescription::uninit()2875 {2876 delete m;2877 m = NULL;2878 }2879 2880 ////////////////////////////////////////////////////////////////////////////////2881 //2882 // IVirtualSystemDescription public methods2883 //2884 ////////////////////////////////////////////////////////////////////////////////2885 2886 /**2887 * Public method implementation.2888 * @param2889 * @return2890 */2891 STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)2892 {2893 if (!aCount)2894 return E_POINTER;2895 2896 AutoCaller autoCaller(this);2897 if (FAILED(autoCaller.rc())) return autoCaller.rc();2898 2899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2900 2901 *aCount = (ULONG)m->llDescriptions.size();2902 2903 return S_OK;2904 }2905 2906 /**2907 * Public method implementation.2908 * @return2909 */2910 STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),2911 ComSafeArrayOut(BSTR, aRefs),2912 ComSafeArrayOut(BSTR, aOrigValues),2913 ComSafeArrayOut(BSTR, aVboxValues),2914 ComSafeArrayOut(BSTR, aExtraConfigValues))2915 {2916 if (ComSafeArrayOutIsNull(aTypes) ||2917 ComSafeArrayOutIsNull(aRefs) ||2918 ComSafeArrayOutIsNull(aOrigValues) ||2919 ComSafeArrayOutIsNull(aVboxValues) ||2920 ComSafeArrayOutIsNull(aExtraConfigValues))2921 return E_POINTER;2922 2923 AutoCaller autoCaller(this);2924 if (FAILED(autoCaller.rc())) return autoCaller.rc();2925 2926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2927 2928 ULONG c = (ULONG)m->llDescriptions.size();2929 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);2930 com::SafeArray<BSTR> sfaRefs(c);2931 com::SafeArray<BSTR> sfaOrigValues(c);2932 com::SafeArray<BSTR> sfaVboxValues(c);2933 com::SafeArray<BSTR> sfaExtraConfigValues(c);2934 2935 list<VirtualSystemDescriptionEntry>::const_iterator it;2936 size_t i = 0;2937 for (it = m->llDescriptions.begin();2938 it != m->llDescriptions.end();2939 ++it, ++i)2940 {2941 const VirtualSystemDescriptionEntry &vsde = (*it);2942 2943 sfaTypes[i] = vsde.type;2944 2945 Bstr bstr = vsde.strRef;2946 bstr.cloneTo(&sfaRefs[i]);2947 2948 bstr = vsde.strOvf;2949 bstr.cloneTo(&sfaOrigValues[i]);2950 2951 bstr = vsde.strVbox;2952 bstr.cloneTo(&sfaVboxValues[i]);2953 2954 bstr = vsde.strExtraConfig;2955 bstr.cloneTo(&sfaExtraConfigValues[i]);2956 }2957 2958 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));2959 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));2960 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));2961 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));2962 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));2963 2964 return S_OK;2965 }2966 2967 /**2968 * Public method implementation.2969 * @return2970 */2971 STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescriptionType_T aType,2972 ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),2973 ComSafeArrayOut(BSTR, aRefs),2974 ComSafeArrayOut(BSTR, aOrigValues),2975 ComSafeArrayOut(BSTR, aVboxValues),2976 ComSafeArrayOut(BSTR, aExtraConfigValues))2977 {2978 if (ComSafeArrayOutIsNull(aTypes) ||2979 ComSafeArrayOutIsNull(aRefs) ||2980 ComSafeArrayOutIsNull(aOrigValues) ||2981 ComSafeArrayOutIsNull(aVboxValues) ||2982 ComSafeArrayOutIsNull(aExtraConfigValues))2983 return E_POINTER;2984 2985 AutoCaller autoCaller(this);2986 if (FAILED(autoCaller.rc())) return autoCaller.rc();2987 2988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2989 2990 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);2991 ULONG c = (ULONG)vsd.size();2992 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);2993 com::SafeArray<BSTR> sfaRefs(c);2994 com::SafeArray<BSTR> sfaOrigValues(c);2995 com::SafeArray<BSTR> sfaVboxValues(c);2996 com::SafeArray<BSTR> sfaExtraConfigValues(c);2997 2998 list<VirtualSystemDescriptionEntry*>::const_iterator it;2999 size_t i = 0;3000 for (it = vsd.begin();3001 it != vsd.end();3002 ++it, ++i)3003 {3004 const VirtualSystemDescriptionEntry *vsde = (*it);3005 3006 sfaTypes[i] = vsde->type;3007 3008 Bstr bstr = vsde->strRef;3009 bstr.cloneTo(&sfaRefs[i]);3010 3011 bstr = vsde->strOvf;3012 bstr.cloneTo(&sfaOrigValues[i]);3013 3014 bstr = vsde->strVbox;3015 bstr.cloneTo(&sfaVboxValues[i]);3016 3017 bstr = vsde->strExtraConfig;3018 bstr.cloneTo(&sfaExtraConfigValues[i]);3019 }3020 3021 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));3022 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));3023 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));3024 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));3025 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));3026 3027 return S_OK;3028 }3029 3030 /**3031 * Public method implementation.3032 * @return3033 */3034 STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionType_T aType,3035 VirtualSystemDescriptionValueType_T aWhich,3036 ComSafeArrayOut(BSTR, aValues))3037 {3038 if (ComSafeArrayOutIsNull(aValues))3039 return E_POINTER;3040 3041 AutoCaller autoCaller(this);3042 if (FAILED(autoCaller.rc())) return autoCaller.rc();3043 3044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);3045 3046 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);3047 com::SafeArray<BSTR> sfaValues((ULONG)vsd.size());3048 3049 list<VirtualSystemDescriptionEntry*>::const_iterator it;3050 size_t i = 0;3051 for (it = vsd.begin();3052 it != vsd.end();3053 ++it, ++i)3054 {3055 const VirtualSystemDescriptionEntry *vsde = (*it);3056 3057 Bstr bstr;3058 switch (aWhich)3059 {3060 case VirtualSystemDescriptionValueType_Reference: bstr = vsde->strRef; break;3061 case VirtualSystemDescriptionValueType_Original: bstr = vsde->strOvf; break;3062 case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVbox; break;3063 case VirtualSystemDescriptionValueType_ExtraConfig: bstr = vsde->strExtraConfig; break;3064 }3065 3066 bstr.cloneTo(&sfaValues[i]);3067 }3068 3069 sfaValues.detachTo(ComSafeArrayOutArg(aValues));3070 3071 return S_OK;3072 }3073 3074 /**3075 * Public method implementation.3076 * @return3077 */3078 STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),3079 ComSafeArrayIn(IN_BSTR, argVboxValues),3080 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))3081 {3082 #ifndef RT_OS_WINDOWS3083 NOREF(aEnabledSize);3084 #endif /* RT_OS_WINDOWS */3085 3086 CheckComArgSafeArrayNotNull(aEnabled);3087 CheckComArgSafeArrayNotNull(argVboxValues);3088 CheckComArgSafeArrayNotNull(argExtraConfigValues);3089 3090 AutoCaller autoCaller(this);3091 if (FAILED(autoCaller.rc())) return autoCaller.rc();3092 3093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);3094 3095 com::SafeArray<BOOL> sfaEnabled(ComSafeArrayInArg(aEnabled));3096 com::SafeArray<IN_BSTR> sfaVboxValues(ComSafeArrayInArg(argVboxValues));3097 com::SafeArray<IN_BSTR> sfaExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));3098 3099 if ( (sfaEnabled.size() != m->llDescriptions.size())3100 || (sfaVboxValues.size() != m->llDescriptions.size())3101 || (sfaExtraConfigValues.size() != m->llDescriptions.size())3102 )3103 return E_INVALIDARG;3104 3105 list<VirtualSystemDescriptionEntry>::iterator it;3106 size_t i = 0;3107 for (it = m->llDescriptions.begin();3108 it != m->llDescriptions.end();3109 ++it, ++i)3110 {3111 VirtualSystemDescriptionEntry& vsde = *it;3112 3113 if (sfaEnabled[i])3114 {3115 vsde.strVbox = sfaVboxValues[i];3116 vsde.strExtraConfig = sfaExtraConfigValues[i];3117 }3118 else3119 vsde.type = VirtualSystemDescriptionType_Ignore;3120 }3121 3122 return S_OK;3123 }3124 3125 /**3126 * Public method implementation.3127 * @return3128 */3129 STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionType_T aType,3130 IN_BSTR aVboxValue,3131 IN_BSTR aExtraConfigValue)3132 {3133 AutoCaller autoCaller(this);3134 if (FAILED(autoCaller.rc())) return autoCaller.rc();3135 3136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);3137 3138 addEntry(aType, "", aVboxValue, aVboxValue, 0, aExtraConfigValue);3139 3140 return S_OK;3141 }3142 3143 /**3144 * Internal method; adds a new description item to the member list.3145 * @param aType Type of description for the new item.3146 * @param strRef Reference item; only used with hard disk controllers.3147 * @param aOrigValue Corresponding original value from OVF.3148 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).3149 * @param ulSizeMB Weight for IProgress3150 * @param strExtraConfig Extra configuration; meaning dependent on type.3151 */3152 void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,3153 const Utf8Str &strRef,3154 const Utf8Str &aOrigValue,3155 const Utf8Str &aAutoValue,3156 uint32_t ulSizeMB,3157 const Utf8Str &strExtraConfig /*= ""*/)3158 {3159 VirtualSystemDescriptionEntry vsde;3160 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them3161 vsde.type = aType;3162 vsde.strRef = strRef;3163 vsde.strOvf = aOrigValue;3164 vsde.strVbox = aAutoValue;3165 vsde.strExtraConfig = strExtraConfig;3166 vsde.ulSizeMB = ulSizeMB;3167 3168 m->llDescriptions.push_back(vsde);3169 }3170 3171 /**3172 * Private method; returns a list of description items containing all the items from the member3173 * description items of this virtual system that match the given type.3174 * @param aType3175 * @return3176 */3177 std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)3178 {3179 std::list<VirtualSystemDescriptionEntry*> vsd;3180 3181 list<VirtualSystemDescriptionEntry>::iterator it;3182 for (it = m->llDescriptions.begin();3183 it != m->llDescriptions.end();3184 ++it)3185 {3186 if (it->type == aType)3187 vsd.push_back(&(*it));3188 }3189 3190 return vsd;3191 }3192 3193 /**3194 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with3195 * the given reference ID. Useful when needing the controller for a particular3196 * virtual disk.3197 * @param id3198 * @return3199 */3200 const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)3201 {3202 Utf8Str strRef = Utf8StrFmt("%RI32", id);3203 list<VirtualSystemDescriptionEntry>::const_iterator it;3204 for (it = m->llDescriptions.begin();3205 it != m->llDescriptions.end();3206 ++it)3207 {3208 const VirtualSystemDescriptionEntry &d = *it;3209 switch (d.type)3210 {3211 case VirtualSystemDescriptionType_HardDiskControllerIDE:3212 case VirtualSystemDescriptionType_HardDiskControllerSATA:3213 case VirtualSystemDescriptionType_HardDiskControllerSCSI:3214 if (d.strRef == strRef)3215 return &d;3216 break;3217 }3218 }3219 3220 return NULL;3221 }3222 -
trunk/src/VBox/Main/Makefile.kmk
r27857 r27882 292 292 ApplianceImpl.cpp \ 293 293 ApplianceImplExport.cpp \ 294 ApplianceImplImport.cpp \ 294 295 xml/ovfreader.cpp \ 295 296 VFSExplorerImpl.cpp \ -
trunk/src/VBox/Main/include/ApplianceImpl.h
r27835 r27882 116 116 117 117 HRESULT readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress); 118 HRESULT importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress);119 118 120 119 struct TaskOVF; 120 121 121 struct TaskImportOVF; /* Worker threads for import */ 122 static DECLCALLBACK(int) taskThreadImportOVF(RTTHREAD aThread, void *pvUser);123 124 122 int readFS(TaskImportOVF *pTask); 125 123 int readS3(TaskImportOVF *pTask); 126 124 127 int importFS(TaskImportOVF *pTask); 128 int importS3(TaskImportOVF *pTask); 129 130 void ConvertDiskAttachmentValues(const HardDiskController &hdc, 125 void convertDiskAttachmentValues(const HardDiskController &hdc, 131 126 uint32_t ulAddressOnParent, 132 127 Bstr &controllerType, 133 128 int32_t &lChannel, 134 129 int32_t &lDevice); 130 131 HRESULT importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress); 132 133 static DECLCALLBACK(int) taskThreadImportOVF(RTTHREAD aThread, void *pvUser); 134 135 int importFS(TaskImportOVF *pTask); 136 int importS3(TaskImportOVF *pTask); 135 137 136 138 HRESULT writeImpl(OVFFormat aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress);
Note:
See TracChangeset
for help on using the changeset viewer.