- Timestamp:
- Mar 30, 2010 1:54:07 PM (15 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 2 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/ApplianceImpl.cpp
r27796 r27829 45 45 #include "Logging.h" 46 46 47 #include "ApplianceImplPrivate.h" 48 47 49 using namespace std; 48 50 49 51 //////////////////////////////////////////////////////////////////////////////// 50 52 // 51 // Appliance data definition 52 // 53 //////////////////////////////////////////////////////////////////////////////// 54 55 /* Describe a location for the import/export. The location could be a file on a 56 * local hard disk or a remote target based on the supported inet protocols. */ 57 struct Appliance::LocationInfo 58 { 59 LocationInfo() 60 : storageType(VFSType_File) {} 61 VFSType_T storageType; /* Which type of storage should be handled */ 62 Utf8Str strPath; /* File path for the import/export */ 63 Utf8Str strHostname; /* Hostname on remote storage locations (could be empty) */ 64 Utf8Str strUsername; /* Username on remote storage locations (could be empty) */ 65 Utf8Str strPassword; /* Password on remote storage locations (could be empty) */ 66 }; 67 68 // opaque private instance data of Appliance class 69 struct Appliance::Data 70 { 71 enum ApplianceState { ApplianceIdle, ApplianceImporting, ApplianceExporting }; 72 73 Data() 74 : state(ApplianceIdle), 75 pReader(NULL) 76 { 77 } 78 79 ~Data() 80 { 81 if (pReader) 82 { 83 delete pReader; 84 pReader = NULL; 85 } 86 } 87 88 ApplianceState state; 89 90 LocationInfo locInfo; // location info for the currently processed OVF 91 92 OVFReader *pReader; 93 94 bool fBusyWriting; // state protection; while this is true nobody else can call methods 95 96 list< ComObjPtr<VirtualSystemDescription> > 97 virtualSystemDescriptions; 98 99 list<Utf8Str> llWarnings; 100 101 ULONG ulWeightPerOperation; 102 Utf8Str strOVFSHA1Digest; 103 }; 104 105 struct VirtualSystemDescription::Data 106 { 107 list<VirtualSystemDescriptionEntry> llDescriptions; 108 }; 109 110 //////////////////////////////////////////////////////////////////////////////// 111 // 112 // internal helpers 53 // Internal helpers 113 54 // 114 55 //////////////////////////////////////////////////////////////////////////////// … … 242 183 * @param cStr 243 184 */ 244 staticvoid convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c, const Utf8Str &cStr)185 void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c, const Utf8Str &cStr) 245 186 { 246 187 /* First check if the type is other/other_64 */ … … 282 223 * @param c 283 224 */ 284 staticCIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)225 CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox) 285 226 { 286 227 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i) … … 373 314 delete m; 374 315 m = NULL; 316 } 317 318 //////////////////////////////////////////////////////////////////////////////// 319 // 320 // IAppliance public methods 321 // 322 //////////////////////////////////////////////////////////////////////////////// 323 324 /** 325 * Public method implementation. 326 * @param 327 * @return 328 */ 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 * @param 351 * @return 352 */ 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 disk 377 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 safearray 399 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 * @param 412 * @return 413 */ 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 path 435 * @return 436 */ 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" extension 457 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 try 465 { 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 482 /** 483 * Public method implementation. 484 * @return 485 */ 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 formats 491 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-consuming 517 // disk imports; also the below method calls do all kinds of locking which conflicts with 518 // the appliance object lock 519 m->state = Data::ApplianceImporting; 520 alock.release(); 521 522 /* Try/catch so we can clean up on error */ 523 try 524 { 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 of 550 * 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_USB 665 /* 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 network 686 * 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 to 693 Utf8Str strNetwork = ea.strNetworkName; 694 // make sure it's one of these two 695 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 apparently 702 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 two 708 * PCNet adapters use the default one. If not use the 709 * Am79C970A as fallback. */ 710 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A || 711 defaultAdapterVBox == NetworkAdapterType_Am79C973)) 712 nwAdapterVBox = NetworkAdapterType_Am79C970A; 713 } 714 #ifdef VBOX_WITH_E1000 715 /* 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 three 725 * E1000 adapters use the default one. If not use the 726 * I82545EM as fallback. */ 727 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM || 728 defaultAdapterVBox == NetworkAdapterType_I82543GC || 729 defaultAdapterVBox == NetworkAdapterType_I82545EM)) 730 nwAdapterVBox = NetworkAdapterType_I82540EM; 731 } 732 else 733 /* 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 "", // ref 740 ea.strNetworkName, // orig 741 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf 742 0, 743 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf 744 } 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 or 774 is every port/bus declared as an extra controller. */ 775 if (cIDEused < 4) 776 { 777 // @todo: figure out the IDE types 778 /* 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 else 790 { 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_AHCI 804 /* Check for the constrains */ 805 if (cSATAused < 1) 806 { 807 // @todo: figure out the SATA types 808 /* 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 else 815 { 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_LSILOGIC 833 /* 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 else 845 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 support 874 // - figure out if there is a url specifier for vhd already 875 // - we need a url specifier for the vdi format 876 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 controller 891 * 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 anyway 912 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage, 913 hd.strDiskId, 914 di.strHref, 915 strPath, 916 ulSize, 917 strExtraConfig); 918 } 919 else 920 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 state 936 alock.acquire(); 937 m->state = Data::ApplianceIdle; 938 939 return rc; 940 } 941 942 /** 943 * Public method implementation. 944 * @param aProgress 945 * @return 946 */ 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 writing 957 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 try 967 { 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 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 try 994 { 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 * @return 1018 */ 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; 375 1044 } 376 1045 … … 744 1413 return strMfFile; 745 1414 } 746 747 struct Appliance::TaskOVF748 {749 TaskOVF(Appliance *aThat)750 : pAppliance(aThat),751 rc(S_OK)752 {}753 754 static int updateProgress(unsigned uPercent, void *pvUser);755 756 LocationInfo locInfo;757 Appliance *pAppliance;758 ComObjPtr<Progress> progress;759 HRESULT rc;760 };761 762 struct Appliance::TaskImportOVF : Appliance::TaskOVF763 {764 enum TaskType765 {766 Read,767 Import768 };769 770 TaskImportOVF(Appliance *aThat)771 : TaskOVF(aThat),772 taskType(Read)773 {}774 775 int startThread();776 777 TaskType taskType;778 };779 780 struct Appliance::TaskExportOVF : Appliance::TaskOVF781 {782 enum OVFFormat783 {784 unspecified,785 OVF_0_9,786 OVF_1_0787 };788 enum TaskType789 {790 Write791 };792 793 TaskExportOVF(Appliance *aThat)794 : TaskOVF(aThat),795 taskType(Write)796 {}797 798 int startThread();799 800 TaskType taskType;801 OVFFormat enFormat;802 };803 804 struct MyHardDiskAttachment805 {806 Bstr bstrUuid;807 ComPtr<IMachine> pMachine;808 Bstr controllerType;809 int32_t lChannel;810 int32_t lDevice;811 };812 1415 813 1416 /* static */ … … 2227 2830 } 2228 2831 2229 HRESULT Appliance::writeImpl(int aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)2230 {2231 HRESULT rc = S_OK;2232 try2233 {2234 /* Initialize our worker task */2235 std::auto_ptr<TaskExportOVF> task(new TaskExportOVF(this));2236 /* What should the task do */2237 task->taskType = TaskExportOVF::Write;2238 /* The OVF version to write */2239 task->enFormat = (TaskExportOVF::OVFFormat)aFormat;2240 /* Copy the current location info to the task */2241 task->locInfo = aLocInfo;2242 2243 Bstr progressDesc = BstrFmt(tr("Export appliance '%s'"),2244 task->locInfo.strPath.c_str());2245 2246 /* todo: This progress init stuff should be done a little bit more generic */2247 if (task->locInfo.storageType == VFSType_File)2248 rc = setUpProgressFS(aProgress, progressDesc);2249 else2250 rc = setUpProgressWriteS3(aProgress, progressDesc);2251 2252 task->progress = aProgress;2253 2254 rc = task->startThread();2255 if (FAILED(rc)) throw rc;2256 2257 /* Don't destruct on success */2258 task.release();2259 }2260 catch (HRESULT aRC)2261 {2262 rc = aRC;2263 }2264 2265 return rc;2266 }2267 2268 DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)2269 {2270 std::auto_ptr<TaskExportOVF> task(static_cast<TaskExportOVF*>(pvUser));2271 AssertReturn(task.get(), VERR_GENERAL_FAILURE);2272 2273 Appliance *pAppliance = task->pAppliance;2274 2275 LogFlowFuncEnter();2276 LogFlowFunc(("Appliance %p\n", pAppliance));2277 2278 HRESULT rc = S_OK;2279 2280 switch(task->taskType)2281 {2282 case TaskExportOVF::Write:2283 {2284 if (task->locInfo.storageType == VFSType_File)2285 rc = pAppliance->writeFS(task.get());2286 else if (task->locInfo.storageType == VFSType_S3)2287 rc = pAppliance->writeS3(task.get());2288 break;2289 }2290 }2291 2292 LogFlowFunc(("rc=%Rhrc\n", rc));2293 LogFlowFuncLeave();2294 2295 return VINF_SUCCESS;2296 }2297 2298 2832 int Appliance::TaskExportOVF::startThread() 2299 2833 { … … 2304 2838 ComAssertMsgRCRet(vrc, 2305 2839 ("Could not create taskThreadWriteOVF (%Rrc)\n", vrc), E_FAIL); 2306 2307 return S_OK;2308 }2309 2310 int Appliance::writeFS(TaskExportOVF *pTask)2311 {2312 LogFlowFuncEnter();2313 LogFlowFunc(("Appliance %p\n", this));2314 2315 AutoCaller autoCaller(this);2316 if (FAILED(autoCaller.rc())) return autoCaller.rc();2317 2318 HRESULT rc = S_OK;2319 2320 try2321 {2322 AutoMultiWriteLock2 multiLock(&mVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);2323 2324 xml::Document doc;2325 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");2326 2327 pelmRoot->setAttribute("ovf:version", (pTask->enFormat == TaskExportOVF::OVF_1_0) ? "1.0" : "0.9");2328 pelmRoot->setAttribute("xml:lang", "en-US");2329 2330 Utf8Str strNamespace = (pTask->enFormat == TaskExportOVF::OVF_0_9)2331 ? "http://www.vmware.com/schema/ovf/1/envelope" // 0.92332 : "http://schemas.dmtf.org/ovf/envelope/1"; // 1.02333 pelmRoot->setAttribute("xmlns", strNamespace);2334 pelmRoot->setAttribute("xmlns:ovf", strNamespace);2335 2336 // pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");2337 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");2338 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");2339 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");2340 // pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");2341 2342 // <Envelope>/<References>2343 xml::ElementNode *pelmReferences = pelmRoot->createChild("References"); // 0.9 and 1.02344 2345 /* <Envelope>/<DiskSection>:2346 <DiskSection>2347 <Info>List of the virtual disks used in the package</Info>2348 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>2349 </DiskSection> */2350 xml::ElementNode *pelmDiskSection;2351 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2352 {2353 // <Section xsi:type="ovf:DiskSection_Type">2354 pelmDiskSection = pelmRoot->createChild("Section");2355 pelmDiskSection->setAttribute("xsi:type", "ovf:DiskSection_Type");2356 }2357 else2358 pelmDiskSection = pelmRoot->createChild("DiskSection");2359 2360 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");2361 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");2362 // for now, set up a map so we have a list of unique disk names (to make2363 // sure the same disk name is only added once)2364 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks;2365 2366 /* <Envelope>/<NetworkSection>:2367 <NetworkSection>2368 <Info>Logical networks used in the package</Info>2369 <Network ovf:name="VM Network">2370 <Description>The network that the LAMP Service will be available on</Description>2371 </Network>2372 </NetworkSection> */2373 xml::ElementNode *pelmNetworkSection;2374 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2375 {2376 // <Section xsi:type="ovf:NetworkSection_Type">2377 pelmNetworkSection = pelmRoot->createChild("Section");2378 pelmNetworkSection->setAttribute("xsi:type", "ovf:NetworkSection_Type");2379 }2380 else2381 pelmNetworkSection = pelmRoot->createChild("NetworkSection");2382 2383 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");2384 pelmNetworkSectionInfo->addContent("Logical networks used in the package");2385 // for now, set up a map so we have a list of unique network names (to make2386 // sure the same network name is only added once)2387 map<Utf8Str, bool> mapNetworks;2388 // we fill this later below when we iterate over the networks2389 2390 // and here come the virtual systems:2391 2392 // write a collection if we have more than one virtual system _and_ we're2393 // writing OVF 1.0; otherwise fail since ovftool can't import more than2394 // one machine, it seems2395 xml::ElementNode *pelmToAddVirtualSystemsTo;2396 if (m->virtualSystemDescriptions.size() > 1)2397 {2398 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2399 throw setError(VBOX_E_FILE_ERROR,2400 tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0"));2401 2402 pelmToAddVirtualSystemsTo = pelmRoot->createChild("VirtualSystemCollection");2403 /* xml::AttributeNode *pattrVirtualSystemCollectionId = */ pelmToAddVirtualSystemsTo->setAttribute("ovf:name", "ExportedVirtualBoxMachines"); // whatever2404 }2405 else2406 pelmToAddVirtualSystemsTo = pelmRoot; // add virtual system directly under root element2407 2408 uint32_t cDisks = 0;2409 2410 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;2411 /* Iterate through all virtual systems of that appliance */2412 for (it = m->virtualSystemDescriptions.begin();2413 it != m->virtualSystemDescriptions.end();2414 ++it)2415 {2416 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);2417 2418 xml::ElementNode *pelmVirtualSystem;2419 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2420 {2421 // <Section xsi:type="ovf:NetworkSection_Type">2422 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("Content");2423 pelmVirtualSystem->setAttribute("xsi:type", "ovf:VirtualSystem_Type");2424 }2425 else2426 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("VirtualSystem");2427 2428 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");2429 2430 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);2431 if (llName.size() != 1)2432 throw setError(VBOX_E_NOT_SUPPORTED,2433 tr("Missing VM name"));2434 Utf8Str &strVMName = llName.front()->strVbox;2435 pelmVirtualSystem->setAttribute("ovf:id", strVMName);2436 2437 // product info2438 std::list<VirtualSystemDescriptionEntry*> llProduct = vsdescThis->findByType(VirtualSystemDescriptionType_Product);2439 std::list<VirtualSystemDescriptionEntry*> llProductUrl = vsdescThis->findByType(VirtualSystemDescriptionType_ProductUrl);2440 std::list<VirtualSystemDescriptionEntry*> llVendor = vsdescThis->findByType(VirtualSystemDescriptionType_Vendor);2441 std::list<VirtualSystemDescriptionEntry*> llVendorUrl = vsdescThis->findByType(VirtualSystemDescriptionType_VendorUrl);2442 std::list<VirtualSystemDescriptionEntry*> llVersion = vsdescThis->findByType(VirtualSystemDescriptionType_Version);2443 bool fProduct = llProduct.size() && !llProduct.front()->strVbox.isEmpty();2444 bool fProductUrl = llProductUrl.size() && !llProductUrl.front()->strVbox.isEmpty();2445 bool fVendor = llVendor.size() && !llVendor.front()->strVbox.isEmpty();2446 bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.front()->strVbox.isEmpty();2447 bool fVersion = llVersion.size() && !llVersion.front()->strVbox.isEmpty();2448 if (fProduct ||2449 fProductUrl ||2450 fVersion ||2451 fVendorUrl ||2452 fVersion)2453 {2454 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">2455 <Info>Meta-information about the installed software</Info>2456 <Product>VAtest</Product>2457 <Vendor>SUN Microsystems</Vendor>2458 <Version>10.0</Version>2459 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>2460 <VendorUrl>http://www.sun.com</VendorUrl>2461 </Section> */2462 xml::ElementNode *pelmAnnotationSection;2463 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2464 {2465 // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">2466 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");2467 pelmAnnotationSection->setAttribute("xsi:type", "ovf:ProductSection_Type");2468 }2469 else2470 pelmAnnotationSection = pelmVirtualSystem->createChild("ProductSection");2471 2472 pelmAnnotationSection->createChild("Info")->addContent("Meta-information about the installed software");2473 if (fProduct)2474 pelmAnnotationSection->createChild("Product")->addContent(llProduct.front()->strVbox);2475 if (fVendor)2476 pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.front()->strVbox);2477 if (fVersion)2478 pelmAnnotationSection->createChild("Version")->addContent(llVersion.front()->strVbox);2479 if (fProductUrl)2480 pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.front()->strVbox);2481 if (fVendorUrl)2482 pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.front()->strVbox);2483 }2484 2485 // description2486 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);2487 if (llDescription.size() &&2488 !llDescription.front()->strVbox.isEmpty())2489 {2490 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">2491 <Info>A human-readable annotation</Info>2492 <Annotation>Plan 9</Annotation>2493 </Section> */2494 xml::ElementNode *pelmAnnotationSection;2495 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2496 {2497 // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">2498 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");2499 pelmAnnotationSection->setAttribute("xsi:type", "ovf:AnnotationSection_Type");2500 }2501 else2502 pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");2503 2504 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");2505 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVbox);2506 }2507 2508 // license2509 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License);2510 if (llLicense.size() &&2511 !llLicense.front()->strVbox.isEmpty())2512 {2513 /* <EulaSection>2514 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>2515 <License ovf:msgid="1">License terms can go in here.</License>2516 </EulaSection> */2517 xml::ElementNode *pelmEulaSection;2518 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2519 {2520 pelmEulaSection = pelmVirtualSystem->createChild("Section");2521 pelmEulaSection->setAttribute("xsi:type", "ovf:EulaSection_Type");2522 }2523 else2524 pelmEulaSection = pelmVirtualSystem->createChild("EulaSection");2525 2526 pelmEulaSection->createChild("Info")->addContent("License agreement for the virtual system");2527 pelmEulaSection->createChild("License")->addContent(llLicense.front()->strVbox);2528 }2529 2530 // operating system2531 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);2532 if (llOS.size() != 1)2533 throw setError(VBOX_E_NOT_SUPPORTED,2534 tr("Missing OS type"));2535 /* <OperatingSystemSection ovf:id="82">2536 <Info>Guest Operating System</Info>2537 <Description>Linux 2.6.x</Description>2538 </OperatingSystemSection> */2539 xml::ElementNode *pelmOperatingSystemSection;2540 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2541 {2542 pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section");2543 pelmOperatingSystemSection->setAttribute("xsi:type", "ovf:OperatingSystemSection_Type");2544 }2545 else2546 pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");2547 2548 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);2549 pelmOperatingSystemSection->createChild("Info")->addContent("The kind of installed guest operating system");2550 Utf8Str strOSDesc;2551 convertCIMOSType2VBoxOSType(strOSDesc, (CIMOSType_T)llOS.front()->strOvf.toInt32(), "");2552 pelmOperatingSystemSection->createChild("Description")->addContent(strOSDesc);2553 2554 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">2555 xml::ElementNode *pelmVirtualHardwareSection;2556 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2557 {2558 // <Section xsi:type="ovf:VirtualHardwareSection_Type">2559 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("Section");2560 pelmVirtualHardwareSection->setAttribute("xsi:type", "ovf:VirtualHardwareSection_Type");2561 }2562 else2563 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");2564 2565 pelmVirtualHardwareSection->createChild("Info")->addContent("Virtual hardware requirements for a virtual machine");2566 2567 /* <System>2568 <vssd:Description>Description of the virtual hardware section.</vssd:Description>2569 <vssd:ElementName>vmware</vssd:ElementName>2570 <vssd:InstanceID>1</vssd:InstanceID>2571 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>2572 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>2573 </System> */2574 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");2575 2576 pelmSystem->createChild("vssd:ElementName")->addContent("Virtual Hardware Family"); // required OVF 1.02577 2578 // <vssd:InstanceId>0</vssd:InstanceId>2579 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2580 pelmSystem->createChild("vssd:InstanceId")->addContent("0");2581 else // capitalization changed...2582 pelmSystem->createChild("vssd:InstanceID")->addContent("0");2583 2584 // <vssd:VirtualSystemIdentifier>VAtest</vssd:VirtualSystemIdentifier>2585 pelmSystem->createChild("vssd:VirtualSystemIdentifier")->addContent(strVMName);2586 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>2587 const char *pcszHardware = "virtualbox-2.2";2588 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2589 // pretend to be vmware compatible then2590 pcszHardware = "vmx-6";2591 pelmSystem->createChild("vssd:VirtualSystemType")->addContent(pcszHardware);2592 2593 // loop thru all description entries twice; once to write out all2594 // devices _except_ disk images, and a second time to assign the2595 // disk images; this is because disk images need to reference2596 // IDE controllers, and we can't know their instance IDs without2597 // assigning them first2598 2599 uint32_t idIDEController = 0;2600 int32_t lIDEControllerIndex = 0;2601 uint32_t idSATAController = 0;2602 int32_t lSATAControllerIndex = 0;2603 uint32_t idSCSIController = 0;2604 int32_t lSCSIControllerIndex = 0;2605 2606 uint32_t ulInstanceID = 1;2607 2608 for (size_t uLoop = 1;2609 uLoop <= 2;2610 ++uLoop)2611 {2612 int32_t lIndexThis = 0;2613 list<VirtualSystemDescriptionEntry>::const_iterator itD;2614 for (itD = vsdescThis->m->llDescriptions.begin();2615 itD != vsdescThis->m->llDescriptions.end();2616 ++itD, ++lIndexThis)2617 {2618 const VirtualSystemDescriptionEntry &desc = *itD;2619 2620 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff2621 Utf8Str strResourceSubType;2622 2623 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block2624 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block2625 2626 uint32_t ulParent = 0;2627 2628 int32_t lVirtualQuantity = -1;2629 Utf8Str strAllocationUnits;2630 2631 int32_t lAddress = -1;2632 int32_t lBusNumber = -1;2633 int32_t lAddressOnParent = -1;2634 2635 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"2636 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block2637 Utf8Str strHostResource;2638 2639 uint64_t uTemp;2640 2641 switch (desc.type)2642 {2643 case VirtualSystemDescriptionType_CPU:2644 /* <Item>2645 <rasd:Caption>1 virtual CPU</rasd:Caption>2646 <rasd:Description>Number of virtual CPUs</rasd:Description>2647 <rasd:ElementName>virtual CPU</rasd:ElementName>2648 <rasd:InstanceID>1</rasd:InstanceID>2649 <rasd:ResourceType>3</rasd:ResourceType>2650 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>2651 </Item> */2652 if (uLoop == 1)2653 {2654 strDescription = "Number of virtual CPUs";2655 type = OVFResourceType_Processor; // 32656 desc.strVbox.toInt(uTemp);2657 lVirtualQuantity = (int32_t)uTemp;2658 strCaption = Utf8StrFmt("%d virtual CPU", lVirtualQuantity); // without this ovftool won't eat the item2659 }2660 break;2661 2662 case VirtualSystemDescriptionType_Memory:2663 /* <Item>2664 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>2665 <rasd:Caption>256 MB of memory</rasd:Caption>2666 <rasd:Description>Memory Size</rasd:Description>2667 <rasd:ElementName>Memory</rasd:ElementName>2668 <rasd:InstanceID>2</rasd:InstanceID>2669 <rasd:ResourceType>4</rasd:ResourceType>2670 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>2671 </Item> */2672 if (uLoop == 1)2673 {2674 strDescription = "Memory Size";2675 type = OVFResourceType_Memory; // 42676 desc.strVbox.toInt(uTemp);2677 lVirtualQuantity = (int32_t)(uTemp / _1M);2678 strAllocationUnits = "MegaBytes";2679 strCaption = Utf8StrFmt("%d MB of memory", lVirtualQuantity); // without this ovftool won't eat the item2680 }2681 break;2682 2683 case VirtualSystemDescriptionType_HardDiskControllerIDE:2684 /* <Item>2685 <rasd:Caption>ideController1</rasd:Caption>2686 <rasd:Description>IDE Controller</rasd:Description>2687 <rasd:InstanceId>5</rasd:InstanceId>2688 <rasd:ResourceType>5</rasd:ResourceType>2689 <rasd:Address>1</rasd:Address>2690 <rasd:BusNumber>1</rasd:BusNumber>2691 </Item> */2692 if (uLoop == 1)2693 {2694 strDescription = "IDE Controller";2695 strCaption = "ideController0";2696 type = OVFResourceType_IDEController; // 52697 strResourceSubType = desc.strVbox;2698 // it seems that OVFTool always writes these two, and since we can only2699 // have one IDE controller, we'll use this as well2700 lAddress = 1;2701 lBusNumber = 1;2702 2703 // remember this ID2704 idIDEController = ulInstanceID;2705 lIDEControllerIndex = lIndexThis;2706 }2707 break;2708 2709 case VirtualSystemDescriptionType_HardDiskControllerSATA:2710 /* <Item>2711 <rasd:Caption>sataController0</rasd:Caption>2712 <rasd:Description>SATA Controller</rasd:Description>2713 <rasd:InstanceId>4</rasd:InstanceId>2714 <rasd:ResourceType>20</rasd:ResourceType>2715 <rasd:ResourceSubType>ahci</rasd:ResourceSubType>2716 <rasd:Address>0</rasd:Address>2717 <rasd:BusNumber>0</rasd:BusNumber>2718 </Item>2719 */2720 if (uLoop == 1)2721 {2722 strDescription = "SATA Controller";2723 strCaption = "sataController0";2724 type = OVFResourceType_OtherStorageDevice; // 202725 // it seems that OVFTool always writes these two, and since we can only2726 // have one SATA controller, we'll use this as well2727 lAddress = 0;2728 lBusNumber = 0;2729 2730 if ( desc.strVbox.isEmpty() // AHCI is the default in VirtualBox2731 || (!desc.strVbox.compare("ahci", Utf8Str::CaseInsensitive))2732 )2733 strResourceSubType = "AHCI";2734 else2735 throw setError(VBOX_E_NOT_SUPPORTED,2736 tr("Invalid config string \"%s\" in SATA controller"), desc.strVbox.c_str());2737 2738 // remember this ID2739 idSATAController = ulInstanceID;2740 lSATAControllerIndex = lIndexThis;2741 }2742 break;2743 2744 case VirtualSystemDescriptionType_HardDiskControllerSCSI:2745 /* <Item>2746 <rasd:Caption>scsiController0</rasd:Caption>2747 <rasd:Description>SCSI Controller</rasd:Description>2748 <rasd:InstanceId>4</rasd:InstanceId>2749 <rasd:ResourceType>6</rasd:ResourceType>2750 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>2751 <rasd:Address>0</rasd:Address>2752 <rasd:BusNumber>0</rasd:BusNumber>2753 </Item>2754 */2755 if (uLoop == 1)2756 {2757 strDescription = "SCSI Controller";2758 strCaption = "scsiController0";2759 type = OVFResourceType_ParallelSCSIHBA; // 62760 // it seems that OVFTool always writes these two, and since we can only2761 // have one SATA controller, we'll use this as well2762 lAddress = 0;2763 lBusNumber = 0;2764 2765 if ( desc.strVbox.isEmpty() // LsiLogic is the default in VirtualBox2766 || (!desc.strVbox.compare("lsilogic", Utf8Str::CaseInsensitive))2767 )2768 strResourceSubType = "lsilogic";2769 else if (!desc.strVbox.compare("buslogic", Utf8Str::CaseInsensitive))2770 strResourceSubType = "buslogic";2771 else2772 throw setError(VBOX_E_NOT_SUPPORTED,2773 tr("Invalid config string \"%s\" in SCSI controller"), desc.strVbox.c_str());2774 2775 // remember this ID2776 idSCSIController = ulInstanceID;2777 lSCSIControllerIndex = lIndexThis;2778 }2779 break;2780 2781 case VirtualSystemDescriptionType_HardDiskImage:2782 /* <Item>2783 <rasd:Caption>disk1</rasd:Caption>2784 <rasd:InstanceId>8</rasd:InstanceId>2785 <rasd:ResourceType>17</rasd:ResourceType>2786 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>2787 <rasd:Parent>4</rasd:Parent>2788 <rasd:AddressOnParent>0</rasd:AddressOnParent>2789 </Item> */2790 if (uLoop == 2)2791 {2792 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);2793 2794 strDescription = "Disk Image";2795 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else2796 type = OVFResourceType_HardDisk; // 172797 2798 // the following references the "<Disks>" XML block2799 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());2800 2801 // controller=<index>;channel=<c>2802 size_t pos1 = desc.strExtraConfig.find("controller=");2803 size_t pos2 = desc.strExtraConfig.find("channel=");2804 if (pos1 != Utf8Str::npos)2805 {2806 int32_t lControllerIndex = -1;2807 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);2808 if (lControllerIndex == lIDEControllerIndex)2809 ulParent = idIDEController;2810 else if (lControllerIndex == lSCSIControllerIndex)2811 ulParent = idSCSIController;2812 else if (lControllerIndex == lSATAControllerIndex)2813 ulParent = idSATAController;2814 }2815 if (pos2 != Utf8Str::npos)2816 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);2817 2818 if ( !ulParent2819 || lAddressOnParent == -12820 )2821 throw setError(VBOX_E_NOT_SUPPORTED,2822 tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfig.c_str());2823 2824 mapDisks[strDiskID] = &desc;2825 }2826 break;2827 2828 case VirtualSystemDescriptionType_Floppy:2829 if (uLoop == 1)2830 {2831 strDescription = "Floppy Drive";2832 strCaption = "floppy0"; // this is what OVFTool writes2833 type = OVFResourceType_FloppyDrive; // 142834 lAutomaticAllocation = 0;2835 lAddressOnParent = 0; // this is what OVFTool writes2836 }2837 break;2838 2839 case VirtualSystemDescriptionType_CDROM:2840 if (uLoop == 2)2841 {2842 // we can't have a CD without an IDE controller2843 if (!idIDEController)2844 throw setError(VBOX_E_NOT_SUPPORTED,2845 tr("Can't have CD-ROM without IDE controller"));2846 2847 strDescription = "CD-ROM Drive";2848 strCaption = "cdrom1"; // this is what OVFTool writes2849 type = OVFResourceType_CDDrive; // 152850 lAutomaticAllocation = 1;2851 ulParent = idIDEController;2852 lAddressOnParent = 0; // this is what OVFTool writes2853 }2854 break;2855 2856 case VirtualSystemDescriptionType_NetworkAdapter:2857 /* <Item>2858 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>2859 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>2860 <rasd:Connection>VM Network</rasd:Connection>2861 <rasd:ElementName>VM network</rasd:ElementName>2862 <rasd:InstanceID>3</rasd:InstanceID>2863 <rasd:ResourceType>10</rasd:ResourceType>2864 </Item> */2865 if (uLoop == 1)2866 {2867 lAutomaticAllocation = 1;2868 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());2869 type = OVFResourceType_EthernetAdapter; // 102870 /* Set the hardware type to something useful.2871 * To be compatible with vmware & others we set2872 * PCNet32 for our PCNet types & E1000 for the2873 * E1000 cards. */2874 switch (desc.strVbox.toInt32())2875 {2876 case NetworkAdapterType_Am79C970A:2877 case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break;2878 #ifdef VBOX_WITH_E10002879 case NetworkAdapterType_I82540EM:2880 case NetworkAdapterType_I82545EM:2881 case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break;2882 #endif /* VBOX_WITH_E1000 */2883 }2884 strConnection = desc.strOvf;2885 2886 mapNetworks[desc.strOvf] = true;2887 }2888 break;2889 2890 case VirtualSystemDescriptionType_USBController:2891 /* <Item ovf:required="false">2892 <rasd:Caption>usb</rasd:Caption>2893 <rasd:Description>USB Controller</rasd:Description>2894 <rasd:InstanceId>3</rasd:InstanceId>2895 <rasd:ResourceType>23</rasd:ResourceType>2896 <rasd:Address>0</rasd:Address>2897 <rasd:BusNumber>0</rasd:BusNumber>2898 </Item> */2899 if (uLoop == 1)2900 {2901 strDescription = "USB Controller";2902 strCaption = "usb";2903 type = OVFResourceType_USBController; // 232904 lAddress = 0; // this is what OVFTool writes2905 lBusNumber = 0; // this is what OVFTool writes2906 }2907 break;2908 2909 case VirtualSystemDescriptionType_SoundCard:2910 /* <Item ovf:required="false">2911 <rasd:Caption>sound</rasd:Caption>2912 <rasd:Description>Sound Card</rasd:Description>2913 <rasd:InstanceId>10</rasd:InstanceId>2914 <rasd:ResourceType>35</rasd:ResourceType>2915 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>2916 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>2917 <rasd:AddressOnParent>3</rasd:AddressOnParent>2918 </Item> */2919 if (uLoop == 1)2920 {2921 strDescription = "Sound Card";2922 strCaption = "sound";2923 type = OVFResourceType_SoundCard; // 352924 strResourceSubType = desc.strOvf; // e.g. ensoniq13712925 lAutomaticAllocation = 0;2926 lAddressOnParent = 3; // what gives? this is what OVFTool writes2927 }2928 break;2929 }2930 2931 if (type)2932 {2933 xml::ElementNode *pItem;2934 2935 pItem = pelmVirtualHardwareSection->createChild("Item");2936 2937 // NOTE: do not change the order of these items without good reason! While we don't care2938 // about ordering, VMware's ovftool does and fails if the items are not written in2939 // exactly this order, as stupid as it seems.2940 2941 if (!strCaption.isEmpty())2942 {2943 pItem->createChild("rasd:Caption")->addContent(strCaption);2944 if (pTask->enFormat == TaskExportOVF::OVF_1_0)2945 pItem->createChild("rasd:ElementName")->addContent(strCaption);2946 }2947 2948 if (!strDescription.isEmpty())2949 pItem->createChild("rasd:Description")->addContent(strDescription);2950 2951 // <rasd:InstanceID>1</rasd:InstanceID>2952 xml::ElementNode *pelmInstanceID;2953 if (pTask->enFormat == TaskExportOVF::OVF_0_9)2954 pelmInstanceID = pItem->createChild("rasd:InstanceId");2955 else2956 pelmInstanceID = pItem->createChild("rasd:InstanceID"); // capitalization changed...2957 pelmInstanceID->addContent(Utf8StrFmt("%d", ulInstanceID++));2958 2959 // <rasd:ResourceType>3</rasd:ResourceType>2960 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));2961 if (!strResourceSubType.isEmpty())2962 pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);2963 2964 if (!strHostResource.isEmpty())2965 pItem->createChild("rasd:HostResource")->addContent(strHostResource);2966 2967 if (!strAllocationUnits.isEmpty())2968 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);2969 2970 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>2971 if (lVirtualQuantity != -1)2972 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));2973 2974 if (lAutomaticAllocation != -1)2975 pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );2976 2977 if (!strConnection.isEmpty())2978 pItem->createChild("rasd:Connection")->addContent(strConnection);2979 2980 if (lAddress != -1)2981 pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));2982 2983 if (lBusNumber != -1)2984 if (pTask->enFormat == TaskExportOVF::OVF_0_9) // BusNumber is invalid OVF 1.0 so only write it in 0.9 mode for OVFTool compatibility2985 pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));2986 2987 if (ulParent)2988 pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));2989 if (lAddressOnParent != -1)2990 pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));2991 }2992 }2993 } // for (size_t uLoop = 0; ...2994 }2995 2996 // now, fill in the network section we set up empty above according2997 // to the networks we found with the hardware items2998 map<Utf8Str, bool>::const_iterator itN;2999 for (itN = mapNetworks.begin();3000 itN != mapNetworks.end();3001 ++itN)3002 {3003 const Utf8Str &strNetwork = itN->first;3004 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");3005 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());3006 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");3007 }3008 3009 // Finally, write out the disks!3010 3011 // This can take a very long time so leave the locks; in particular, we have the media tree3012 // lock which Medium::CloneTo() will request, and that would deadlock. Instead, protect3013 // the appliance by resetting its state so we can safely leave the lock3014 m->state = Data::ApplianceExporting;3015 multiLock.release();3016 3017 list<Utf8Str> diskList;3018 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;3019 uint32_t ulFile = 1;3020 for (itS = mapDisks.begin();3021 itS != mapDisks.end();3022 ++itS)3023 {3024 const Utf8Str &strDiskID = itS->first;3025 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;3026 3027 // source path: where the VBox image is3028 const Utf8Str &strSrcFilePath = pDiskEntry->strVbox;3029 Bstr bstrSrcFilePath(strSrcFilePath);3030 if (!RTPathExists(strSrcFilePath.c_str()))3031 /* This isn't allowed */3032 throw setError(VBOX_E_FILE_ERROR,3033 tr("Source virtual disk image file '%s' doesn't exist"),3034 strSrcFilePath.c_str());3035 3036 // output filename3037 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;3038 // target path needs to be composed from where the output OVF is3039 Utf8Str strTargetFilePath(pTask->locInfo.strPath);3040 strTargetFilePath.stripFilename();3041 strTargetFilePath.append("/");3042 strTargetFilePath.append(strTargetFileNameOnly);3043 3044 // clone the disk:3045 ComPtr<IMedium> pSourceDisk;3046 ComPtr<IMedium> pTargetDisk;3047 ComPtr<IProgress> pProgress2;3048 3049 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));3050 rc = mVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());3051 if (FAILED(rc)) throw rc;3052 3053 /* We are always exporting to vmdfk stream optimized for now */3054 Bstr bstrSrcFormat = L"VMDK";3055 3056 // create a new hard disk interface for the destination disk image3057 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));3058 rc = mVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());3059 if (FAILED(rc)) throw rc;3060 3061 // the target disk is now registered and needs to be removed again,3062 // both after successful cloning or if anything goes bad!3063 try3064 {3065 // create a flat copy of the source disk image3066 rc = pSourceDisk->CloneTo(pTargetDisk, MediumVariant_VmdkStreamOptimized, NULL, pProgress2.asOutParam());3067 if (FAILED(rc)) throw rc;3068 3069 // advance to the next operation3070 if (!pTask->progress.isNull())3071 pTask->progress->SetNextOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()),3072 pDiskEntry->ulSizeMB); // operation's weight, as set up with the IProgress originally);3073 3074 // now wait for the background disk operation to complete; this throws HRESULTs on error3075 waitForAsyncProgress(pTask->progress, pProgress2);3076 }3077 catch (HRESULT rc3)3078 {3079 // upon error after registering, close the disk or3080 // it'll stick in the registry forever3081 pTargetDisk->Close();3082 throw rc3;3083 }3084 diskList.push_back(strTargetFilePath);3085 3086 // we need the following for the XML3087 uint64_t cbFile = 0; // actual file size3088 rc = pTargetDisk->COMGETTER(Size)(&cbFile);3089 if (FAILED(rc)) throw rc;3090 3091 ULONG64 cbCapacity = 0; // size reported to guest3092 rc = pTargetDisk->COMGETTER(LogicalSize)(&cbCapacity);3093 if (FAILED(rc)) throw rc;3094 // capacity is reported in megabytes, so...3095 cbCapacity *= _1M;3096 3097 // upon success, close the disk as well3098 rc = pTargetDisk->Close();3099 if (FAILED(rc)) throw rc;3100 3101 // now handle the XML for the disk:3102 Utf8StrFmt strFileRef("file%RI32", ulFile++);3103 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>3104 xml::ElementNode *pelmFile = pelmReferences->createChild("File");3105 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);3106 pelmFile->setAttribute("ovf:id", strFileRef);3107 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());3108 3109 // add disk to XML Disks section3110 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/>3111 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");3112 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());3113 pelmDisk->setAttribute("ovf:diskId", strDiskID);3114 pelmDisk->setAttribute("ovf:fileRef", strFileRef);3115 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#sparse"); // must be sparse or ovftool chokes3116 }3117 3118 // now go write the XML3119 xml::XmlFileWriter writer(doc);3120 writer.write(pTask->locInfo.strPath.c_str());3121 3122 /* Create & write the manifest file */3123 const char** ppManifestFiles = (const char**)RTMemAlloc(sizeof(char*)*diskList.size() + 1);3124 ppManifestFiles[0] = pTask->locInfo.strPath.c_str();3125 list<Utf8Str>::const_iterator it1;3126 size_t i = 1;3127 for (it1 = diskList.begin();3128 it1 != diskList.end();3129 ++it1, ++i)3130 ppManifestFiles[i] = (*it1).c_str();3131 Utf8Str strMfFile = manifestFileName(pTask->locInfo.strPath.c_str());3132 int vrc = RTManifestWriteFiles(strMfFile.c_str(), ppManifestFiles, diskList.size()+1);3133 if (RT_FAILURE(vrc))3134 throw setError(VBOX_E_FILE_ERROR,3135 tr("Couldn't create manifest file '%s' (%Rrc)"),3136 RTPathFilename(strMfFile.c_str()), vrc);3137 RTMemFree(ppManifestFiles);3138 }3139 catch(xml::Error &x)3140 {3141 rc = setError(VBOX_E_FILE_ERROR,3142 x.what());3143 }3144 catch(HRESULT aRC)3145 {3146 rc = aRC;3147 }3148 3149 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);3150 // reset the state so others can call methods again3151 m->state = Data::ApplianceIdle;3152 3153 pTask->rc = rc;3154 3155 if (!pTask->progress.isNull())3156 pTask->progress->notifyComplete(rc);3157 3158 LogFlowFunc(("rc=%Rhrc\n", rc));3159 LogFlowFuncLeave();3160 3161 return VINF_SUCCESS;3162 }3163 3164 int Appliance::writeS3(TaskExportOVF *pTask)3165 {3166 LogFlowFuncEnter();3167 LogFlowFunc(("Appliance %p\n", this));3168 3169 AutoCaller autoCaller(this);3170 if (FAILED(autoCaller.rc())) return autoCaller.rc();3171 3172 HRESULT rc = S_OK;3173 3174 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);3175 3176 int vrc = VINF_SUCCESS;3177 RTS3 hS3 = NIL_RTS3;3178 char szOSTmpDir[RTPATH_MAX];3179 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));3180 /* The template for the temporary directory created below */3181 char *pszTmpDir;3182 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);3183 list< pair<Utf8Str, ULONG> > filesList;3184 3185 // todo:3186 // - usable error codes3187 // - seems snapshot filenames are problematic {uuid}.vdi3188 try3189 {3190 /* Extract the bucket */3191 Utf8Str tmpPath = pTask->locInfo.strPath;3192 Utf8Str bucket;3193 parseBucket(tmpPath, bucket);3194 3195 /* We need a temporary directory which we can put the OVF file & all3196 * disk images in */3197 vrc = RTDirCreateTemp(pszTmpDir);3198 if (RT_FAILURE(vrc))3199 throw setError(VBOX_E_FILE_ERROR,3200 tr("Cannot create temporary directory '%s'"), pszTmpDir);3201 3202 /* The temporary name of the target OVF file */3203 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));3204 3205 /* Prepare the temporary writing of the OVF */3206 ComObjPtr<Progress> progress;3207 /* Create a temporary file based location info for the sub task */3208 LocationInfo li;3209 li.strPath = strTmpOvf;3210 rc = writeImpl(pTask->enFormat, li, progress);3211 if (FAILED(rc)) throw rc;3212 3213 /* Unlock the appliance for the writing thread */3214 appLock.release();3215 /* Wait until the writing is done, but report the progress back to the3216 caller */3217 ComPtr<IProgress> progressInt(progress);3218 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */3219 3220 /* Again lock the appliance for the next steps */3221 appLock.acquire();3222 3223 vrc = RTPathExists(strTmpOvf.c_str()); /* Paranoid check */3224 if(RT_FAILURE(vrc))3225 throw setError(VBOX_E_FILE_ERROR,3226 tr("Cannot find source file '%s'"), strTmpOvf.c_str());3227 /* Add the OVF file */3228 filesList.push_back(pair<Utf8Str, ULONG>(strTmpOvf, m->ulWeightPerOperation)); /* Use 1% of the total for the OVF file upload */3229 Utf8Str strMfFile = manifestFileName(strTmpOvf);3230 filesList.push_back(pair<Utf8Str, ULONG>(strMfFile , m->ulWeightPerOperation)); /* Use 1% of the total for the manifest file upload */3231 3232 /* Now add every disks of every virtual system */3233 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;3234 for (it = m->virtualSystemDescriptions.begin();3235 it != m->virtualSystemDescriptions.end();3236 ++it)3237 {3238 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);3239 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);3240 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;3241 for (itH = avsdeHDs.begin();3242 itH != avsdeHDs.end();3243 ++itH)3244 {3245 const Utf8Str &strTargetFileNameOnly = (*itH)->strOvf;3246 /* Target path needs to be composed from where the output OVF is */3247 Utf8Str strTargetFilePath(strTmpOvf);3248 strTargetFilePath.stripFilename();3249 strTargetFilePath.append("/");3250 strTargetFilePath.append(strTargetFileNameOnly);3251 vrc = RTPathExists(strTargetFilePath.c_str()); /* Paranoid check */3252 if(RT_FAILURE(vrc))3253 throw setError(VBOX_E_FILE_ERROR,3254 tr("Cannot find source file '%s'"), strTargetFilePath.c_str());3255 filesList.push_back(pair<Utf8Str, ULONG>(strTargetFilePath, (*itH)->ulSizeMB));3256 }3257 }3258 /* Next we have to upload the OVF & all disk images */3259 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);3260 if(RT_FAILURE(vrc))3261 throw setError(VBOX_E_IPRT_ERROR,3262 tr("Cannot create S3 service handler"));3263 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);3264 3265 /* Upload all files */3266 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)3267 {3268 const pair<Utf8Str, ULONG> &s = (*it1);3269 char *pszFilename = RTPathFilename(s.first.c_str());3270 /* Advance to the next operation */3271 if (!pTask->progress.isNull())3272 pTask->progress->SetNextOperation(BstrFmt(tr("Uploading file '%s'"), pszFilename), s.second);3273 vrc = RTS3PutKey(hS3, bucket.c_str(), pszFilename, s.first.c_str());3274 if (RT_FAILURE(vrc))3275 {3276 if(vrc == VERR_S3_CANCELED)3277 break;3278 else if(vrc == VERR_S3_ACCESS_DENIED)3279 throw setError(E_ACCESSDENIED,3280 tr("Cannot upload file '%s' to S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);3281 else if(vrc == VERR_S3_NOT_FOUND)3282 throw setError(VBOX_E_FILE_ERROR,3283 tr("Cannot upload file '%s' to S3 storage server (File not found)"), pszFilename);3284 else3285 throw setError(VBOX_E_IPRT_ERROR,3286 tr("Cannot upload file '%s' to S3 storage server (%Rrc)"), pszFilename, vrc);3287 }3288 }3289 }3290 catch(HRESULT aRC)3291 {3292 rc = aRC;3293 }3294 /* Cleanup */3295 RTS3Destroy(hS3);3296 /* Delete all files which where temporary created */3297 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)3298 {3299 const char *pszFilePath = (*it1).first.c_str();3300 if (RTPathExists(pszFilePath))3301 {3302 vrc = RTFileDelete(pszFilePath);3303 if(RT_FAILURE(vrc))3304 rc = setError(VBOX_E_FILE_ERROR,3305 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);3306 }3307 }3308 /* Delete the temporary directory */3309 if (RTPathExists(pszTmpDir))3310 {3311 vrc = RTDirRemove(pszTmpDir);3312 if(RT_FAILURE(vrc))3313 rc = setError(VBOX_E_FILE_ERROR,3314 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);3315 }3316 if (pszTmpDir)3317 RTStrFree(pszTmpDir);3318 3319 pTask->rc = rc;3320 3321 if (!pTask->progress.isNull())3322 pTask->progress->notifyComplete(rc);3323 3324 LogFlowFunc(("rc=%Rhrc\n", rc));3325 LogFlowFuncLeave();3326 3327 return VINF_SUCCESS;3328 }3329 3330 ////////////////////////////////////////////////////////////////////////////////3331 //3332 // IAppliance public methods3333 //3334 ////////////////////////////////////////////////////////////////////////////////3335 3336 /**3337 * Public method implementation.3338 * @param3339 * @return3340 */3341 STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)3342 {3343 if (!aPath)3344 return E_POINTER;3345 3346 AutoCaller autoCaller(this);3347 if (FAILED(autoCaller.rc())) return autoCaller.rc();3348 3349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);3350 3351 if (!isApplianceIdle())3352 return E_ACCESSDENIED;3353 3354 Bstr bstrPath(m->locInfo.strPath);3355 bstrPath.cloneTo(aPath);3356 3357 return S_OK;3358 }3359 3360 /**3361 * Public method implementation.3362 * @param3363 * @return3364 */3365 STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))3366 {3367 CheckComArgOutSafeArrayPointerValid(aDisks);3368 3369 AutoCaller autoCaller(this);3370 if (FAILED(autoCaller.rc())) return autoCaller.rc();3371 3372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);3373 3374 if (!isApplianceIdle())3375 return E_ACCESSDENIED;3376 3377 if (m->pReader) // OVFReader instantiated?3378 {3379 size_t c = m->pReader->m_mapDisks.size();3380 com::SafeArray<BSTR> sfaDisks(c);3381 3382 DiskImagesMap::const_iterator it;3383 size_t i = 0;3384 for (it = m->pReader->m_mapDisks.begin();3385 it != m->pReader->m_mapDisks.end();3386 ++it, ++i)3387 {3388 // create a string representing this disk3389 const DiskImage &d = it->second;3390 char *psz = NULL;3391 RTStrAPrintf(&psz,3392 "%s\t"3393 "%RI64\t"3394 "%RI64\t"3395 "%s\t"3396 "%s\t"3397 "%RI64\t"3398 "%RI64\t"3399 "%s",3400 d.strDiskId.c_str(),3401 d.iCapacity,3402 d.iPopulatedSize,3403 d.strFormat.c_str(),3404 d.strHref.c_str(),3405 d.iSize,3406 d.iChunkSize,3407 d.strCompression.c_str());3408 Utf8Str utf(psz);3409 Bstr bstr(utf);3410 // push to safearray3411 bstr.cloneTo(&sfaDisks[i]);3412 RTStrFree(psz);3413 }3414 3415 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));3416 }3417 3418 return S_OK;3419 }3420 3421 /**3422 * Public method implementation.3423 * @param3424 * @return3425 */3426 STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))3427 {3428 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);3429 3430 AutoCaller autoCaller(this);3431 if (FAILED(autoCaller.rc())) return autoCaller.rc();3432 3433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);3434 3435 if (!isApplianceIdle())3436 return E_ACCESSDENIED;3437 3438 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);3439 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));3440 3441 return S_OK;3442 }3443 3444 /**3445 * Public method implementation.3446 * @param path3447 * @return3448 */3449 STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress)3450 {3451 if (!path) return E_POINTER;3452 CheckComArgOutPointerValid(aProgress);3453 3454 AutoCaller autoCaller(this);3455 if (FAILED(autoCaller.rc())) return autoCaller.rc();3456 3457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);3458 3459 if (!isApplianceIdle())3460 return E_ACCESSDENIED;3461 3462 if (m->pReader)3463 {3464 delete m->pReader;3465 m->pReader = NULL;3466 }3467 3468 // see if we can handle this file; for now we insist it has an ".ovf" extension3469 Utf8Str strPath (path);3470 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))3471 return setError(VBOX_E_FILE_ERROR,3472 tr("Appliance file must have .ovf extension"));3473 3474 ComObjPtr<Progress> progress;3475 HRESULT rc = S_OK;3476 try3477 {3478 /* Parse all necessary info out of the URI */3479 parseURI(strPath, m->locInfo);3480 rc = readImpl(m->locInfo, progress);3481 }3482 catch (HRESULT aRC)3483 {3484 rc = aRC;3485 }3486 3487 if (SUCCEEDED(rc))3488 /* Return progress to the caller */3489 progress.queryInterfaceTo(aProgress);3490 3491 return S_OK;3492 }3493 3494 /**3495 * Public method implementation.3496 * @return3497 */3498 STDMETHODIMP Appliance::Interpret()3499 {3500 // @todo:3501 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))3502 // - Appropriate handle errors like not supported file formats3503 AutoCaller autoCaller(this);3504 if (FAILED(autoCaller.rc())) return autoCaller.rc();3505 3506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);3507 3508 if (!isApplianceIdle())3509 return E_ACCESSDENIED;3510 3511 HRESULT rc = S_OK;3512 3513 /* Clear any previous virtual system descriptions */3514 m->virtualSystemDescriptions.clear();3515 3516 /* We need the default path for storing disk images */3517 ComPtr<ISystemProperties> systemProps;3518 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());3519 if (FAILED(rc)) return rc;3520 Bstr bstrDefaultHardDiskLocation;3521 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());3522 if (FAILED(rc)) return rc;3523 3524 if (!m->pReader)3525 return setError(E_FAIL,3526 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));3527 3528 // Change the appliance state so we can safely leave the lock while doing time-consuming3529 // disk imports; also the below method calls do all kinds of locking which conflicts with3530 // the appliance object lock3531 m->state = Data::ApplianceImporting;3532 alock.release();3533 3534 /* Try/catch so we can clean up on error */3535 try3536 {3537 list<VirtualSystem>::const_iterator it;3538 /* Iterate through all virtual systems */3539 for (it = m->pReader->m_llVirtualSystems.begin();3540 it != m->pReader->m_llVirtualSystems.end();3541 ++it)3542 {3543 const VirtualSystem &vsysThis = *it;3544 3545 ComObjPtr<VirtualSystemDescription> pNewDesc;3546 rc = pNewDesc.createObject();3547 if (FAILED(rc)) throw rc;3548 rc = pNewDesc->init();3549 if (FAILED(rc)) throw rc;3550 3551 /* Guest OS type */3552 Utf8Str strOsTypeVBox,3553 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);3554 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);3555 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,3556 "",3557 strCIMOSType,3558 strOsTypeVBox);3559 3560 /* VM name */3561 /* If the there isn't any name specified create a default one out of3562 * the OS type */3563 Utf8Str nameVBox = vsysThis.strName;3564 if (nameVBox.isEmpty())3565 nameVBox = strOsTypeVBox;3566 searchUniqueVMName(nameVBox);3567 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,3568 "",3569 vsysThis.strName,3570 nameVBox);3571 3572 /* VM Product */3573 if (!vsysThis.strProduct.isEmpty())3574 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,3575 "",3576 vsysThis.strProduct,3577 vsysThis.strProduct);3578 3579 /* VM Vendor */3580 if (!vsysThis.strVendor.isEmpty())3581 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,3582 "",3583 vsysThis.strVendor,3584 vsysThis.strVendor);3585 3586 /* VM Version */3587 if (!vsysThis.strVersion.isEmpty())3588 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,3589 "",3590 vsysThis.strVersion,3591 vsysThis.strVersion);3592 3593 /* VM ProductUrl */3594 if (!vsysThis.strProductUrl.isEmpty())3595 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,3596 "",3597 vsysThis.strProductUrl,3598 vsysThis.strProductUrl);3599 3600 /* VM VendorUrl */3601 if (!vsysThis.strVendorUrl.isEmpty())3602 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,3603 "",3604 vsysThis.strVendorUrl,3605 vsysThis.strVendorUrl);3606 3607 /* VM description */3608 if (!vsysThis.strDescription.isEmpty())3609 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,3610 "",3611 vsysThis.strDescription,3612 vsysThis.strDescription);3613 3614 /* VM license */3615 if (!vsysThis.strLicenseText.isEmpty())3616 pNewDesc->addEntry(VirtualSystemDescriptionType_License,3617 "",3618 vsysThis.strLicenseText,3619 vsysThis.strLicenseText);3620 3621 /* Now that we know the OS type, get our internal defaults based on that. */3622 ComPtr<IGuestOSType> pGuestOSType;3623 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());3624 if (FAILED(rc)) throw rc;3625 3626 /* CPU count */3627 ULONG cpuCountVBox = vsysThis.cCPUs;3628 /* Check for the constrains */3629 if (cpuCountVBox > SchemaDefs::MaxCPUCount)3630 {3631 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),3632 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);3633 cpuCountVBox = SchemaDefs::MaxCPUCount;3634 }3635 if (vsysThis.cCPUs == 0)3636 cpuCountVBox = 1;3637 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,3638 "",3639 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),3640 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));3641 3642 /* RAM */3643 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;3644 /* Check for the constrains */3645 if (ullMemSizeVBox != 0 &&3646 (ullMemSizeVBox < MM_RAM_MIN_IN_MB ||3647 ullMemSizeVBox > MM_RAM_MAX_IN_MB))3648 {3649 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."),3650 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);3651 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);3652 }3653 if (vsysThis.ullMemorySize == 0)3654 {3655 /* If the RAM of the OVF is zero, use our predefined values */3656 ULONG memSizeVBox2;3657 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);3658 if (FAILED(rc)) throw rc;3659 /* VBox stores that in MByte */3660 ullMemSizeVBox = (uint64_t)memSizeVBox2;3661 }3662 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,3663 "",3664 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),3665 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));3666 3667 /* Audio */3668 if (!vsysThis.strSoundCardType.isEmpty())3669 /* Currently we set the AC97 always.3670 @todo: figure out the hardware which could be possible */3671 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,3672 "",3673 vsysThis.strSoundCardType,3674 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));3675 3676 #ifdef VBOX_WITH_USB3677 /* USB Controller */3678 if (vsysThis.fHasUsbController)3679 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");3680 #endif /* VBOX_WITH_USB */3681 3682 /* Network Controller */3683 size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();3684 if (cEthernetAdapters > 0)3685 {3686 /* Check for the constrains */3687 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)3688 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),3689 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);3690 3691 /* Get the default network adapter type for the selected guest OS */3692 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;3693 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);3694 if (FAILED(rc)) throw rc;3695 3696 EthernetAdaptersList::const_iterator itEA;3697 /* Iterate through all abstract networks. We support 8 network3698 * adapters at the maximum, so the first 8 will be added only. */3699 size_t a = 0;3700 for (itEA = vsysThis.llEthernetAdapters.begin();3701 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;3702 ++itEA, ++a)3703 {3704 const EthernetAdapter &ea = *itEA; // logical network to connect to3705 Utf8Str strNetwork = ea.strNetworkName;3706 // make sure it's one of these two3707 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))3708 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))3709 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))3710 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))3711 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))3712 )3713 strNetwork = "Bridged"; // VMware assumes this is the default apparently3714 3715 /* Figure out the hardware type */3716 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;3717 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))3718 {3719 /* If the default adapter is already one of the two3720 * PCNet adapters use the default one. If not use the3721 * Am79C970A as fallback. */3722 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||3723 defaultAdapterVBox == NetworkAdapterType_Am79C973))3724 nwAdapterVBox = NetworkAdapterType_Am79C970A;3725 }3726 #ifdef VBOX_WITH_E10003727 /* VMWare accidentally write this with VirtualCenter 3.5,3728 so make sure in this case always to use the VMWare one */3729 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))3730 nwAdapterVBox = NetworkAdapterType_I82545EM;3731 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))3732 {3733 /* Check if this OVF was written by VirtualBox */3734 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))3735 {3736 /* If the default adapter is already one of the three3737 * E1000 adapters use the default one. If not use the3738 * I82545EM as fallback. */3739 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||3740 defaultAdapterVBox == NetworkAdapterType_I82543GC ||3741 defaultAdapterVBox == NetworkAdapterType_I82545EM))3742 nwAdapterVBox = NetworkAdapterType_I82540EM;3743 }3744 else3745 /* Always use this one since it's what VMware uses */3746 nwAdapterVBox = NetworkAdapterType_I82545EM;3747 }3748 #endif /* VBOX_WITH_E1000 */3749 3750 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,3751 "", // ref3752 ea.strNetworkName, // orig3753 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf3754 0,3755 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf3756 }3757 }3758 3759 /* Floppy Drive */3760 if (vsysThis.fHasFloppyDrive)3761 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");3762 3763 /* CD Drive */3764 if (vsysThis.fHasCdromDrive)3765 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");3766 3767 /* Hard disk Controller */3768 uint16_t cIDEused = 0;3769 uint16_t cSATAused = 0; NOREF(cSATAused);3770 uint16_t cSCSIused = 0; NOREF(cSCSIused);3771 ControllersMap::const_iterator hdcIt;3772 /* Iterate through all hard disk controllers */3773 for (hdcIt = vsysThis.mapControllers.begin();3774 hdcIt != vsysThis.mapControllers.end();3775 ++hdcIt)3776 {3777 const HardDiskController &hdc = hdcIt->second;3778 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);3779 3780 switch (hdc.system)3781 {3782 case HardDiskController::IDE:3783 {3784 /* Check for the constrains */3785 /* @todo: I'm very confused! Are these bits *one* controller or3786 is every port/bus declared as an extra controller. */3787 if (cIDEused < 4)3788 {3789 // @todo: figure out the IDE types3790 /* Use PIIX4 as default */3791 Utf8Str strType = "PIIX4";3792 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))3793 strType = "PIIX3";3794 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))3795 strType = "ICH6";3796 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,3797 strControllerID,3798 hdc.strControllerType,3799 strType);3800 }3801 else3802 {3803 /* Warn only once */3804 if (cIDEused == 1)3805 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),3806 vsysThis.strName.c_str());3807 3808 }3809 ++cIDEused;3810 break;3811 }3812 3813 case HardDiskController::SATA:3814 {3815 #ifdef VBOX_WITH_AHCI3816 /* Check for the constrains */3817 if (cSATAused < 1)3818 {3819 // @todo: figure out the SATA types3820 /* We only support a plain AHCI controller, so use them always */3821 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,3822 strControllerID,3823 hdc.strControllerType,3824 "AHCI");3825 }3826 else3827 {3828 /* Warn only once */3829 if (cSATAused == 1)3830 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),3831 vsysThis.strName.c_str());3832 3833 }3834 ++cSATAused;3835 break;3836 #else /* !VBOX_WITH_AHCI */3837 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SATA controller emulation"),3838 vsysThis.strName.c_str());3839 #endif /* !VBOX_WITH_AHCI */3840 }3841 3842 case HardDiskController::SCSI:3843 {3844 #ifdef VBOX_WITH_LSILOGIC3845 /* Check for the constrains */3846 if (cSCSIused < 1)3847 {3848 Utf8Str hdcController = "LsiLogic";3849 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))3850 hdcController = "BusLogic";3851 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,3852 strControllerID,3853 hdc.strControllerType,3854 hdcController);3855 }3856 else3857 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."),3858 vsysThis.strName.c_str(),3859 hdc.strControllerType.c_str(),3860 strControllerID.c_str());3861 ++cSCSIused;3862 break;3863 #else /* !VBOX_WITH_LSILOGIC */3864 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SCSI controller emulation"),3865 vsysThis.strName.c_str());3866 #endif /* !VBOX_WITH_LSILOGIC */3867 }3868 }3869 }3870 3871 /* Hard disks */3872 if (vsysThis.mapVirtualDisks.size() > 0)3873 {3874 VirtualDisksMap::const_iterator itVD;3875 /* Iterate through all hard disks ()*/3876 for (itVD = vsysThis.mapVirtualDisks.begin();3877 itVD != vsysThis.mapVirtualDisks.end();3878 ++itVD)3879 {3880 const VirtualDisk &hd = itVD->second;3881 /* Get the associated disk image */3882 const DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId];3883 3884 // @todo:3885 // - figure out all possible vmdk formats we also support3886 // - figure out if there is a url specifier for vhd already3887 // - we need a url specifier for the vdi format3888 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)3889 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))3890 {3891 /* If the href is empty use the VM name as filename */3892 Utf8Str strFilename = di.strHref;3893 if (!strFilename.length())3894 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());3895 /* Construct a unique target path */3896 Utf8StrFmt strPath("%ls%c%s",3897 bstrDefaultHardDiskLocation.raw(),3898 RTPATH_DELIMITER,3899 strFilename.c_str());3900 searchUniqueDiskImageFilePath(strPath);3901 3902 /* find the description for the hard disk controller3903 * that has the same ID as hd.idController */3904 const VirtualSystemDescriptionEntry *pController;3905 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))3906 throw setError(E_FAIL,3907 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),3908 hd.idController,3909 di.strHref.c_str());3910 3911 /* controller to attach to, and the bus within that controller */3912 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",3913 pController->ulIndex,3914 hd.ulAddressOnParent);3915 ULONG ulSize = 0;3916 if (di.iCapacity != -1)3917 ulSize = (ULONG)(di.iCapacity / _1M);3918 else if (di.iPopulatedSize != -1)3919 ulSize = (ULONG)(di.iPopulatedSize / _1M);3920 else if (di.iSize != -1)3921 ulSize = (ULONG)(di.iSize / _1M);3922 if (ulSize == 0)3923 ulSize = 10000; // assume 10 GB, this is for the progress bar only anyway3924 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,3925 hd.strDiskId,3926 di.strHref,3927 strPath,3928 ulSize,3929 strExtraConfig);3930 }3931 else3932 throw setError(VBOX_E_FILE_ERROR,3933 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));3934 }3935 }3936 3937 m->virtualSystemDescriptions.push_back(pNewDesc);3938 }3939 }3940 catch (HRESULT aRC)3941 {3942 /* On error we clear the list & return */3943 m->virtualSystemDescriptions.clear();3944 rc = aRC;3945 }3946 3947 // reset the appliance state3948 alock.acquire();3949 m->state = Data::ApplianceIdle;3950 3951 return rc;3952 }3953 3954 /**3955 * Public method implementation.3956 * @param aProgress3957 * @return3958 */3959 STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)3960 {3961 CheckComArgOutPointerValid(aProgress);3962 3963 AutoCaller autoCaller(this);3964 if (FAILED(autoCaller.rc())) return autoCaller.rc();3965 3966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);3967 3968 // do not allow entering this method if the appliance is busy reading or writing3969 if (!isApplianceIdle())3970 return E_ACCESSDENIED;3971 3972 if (!m->pReader)3973 return setError(E_FAIL,3974 tr("Cannot import machines without reading it first (call read() before importMachines())"));3975 3976 ComObjPtr<Progress> progress;3977 HRESULT rc = S_OK;3978 try3979 {3980 rc = importImpl(m->locInfo, progress);3981 }3982 catch (HRESULT aRC)3983 {3984 rc = aRC;3985 }3986 3987 if (SUCCEEDED(rc))3988 /* Return progress to the caller */3989 progress.queryInterfaceTo(aProgress);3990 3991 return rc;3992 }3993 3994 STDMETHODIMP Appliance::CreateVFSExplorer(IN_BSTR aURI, IVFSExplorer **aExplorer)3995 {3996 CheckComArgOutPointerValid(aExplorer);3997 3998 AutoCaller autoCaller(this);3999 if (FAILED(autoCaller.rc())) return autoCaller.rc();4000 4001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4002 4003 ComObjPtr<VFSExplorer> explorer;4004 HRESULT rc = S_OK;4005 try4006 {4007 Utf8Str uri(aURI);4008 /* Check which kind of export the user has requested */4009 LocationInfo li;4010 parseURI(uri, li);4011 /* Create the explorer object */4012 explorer.createObject();4013 rc = explorer->init(li.storageType, li.strPath, li.strHostname, li.strUsername, li.strPassword, mVirtualBox);4014 }4015 catch (HRESULT aRC)4016 {4017 rc = aRC;4018 }4019 4020 if (SUCCEEDED(rc))4021 /* Return explorer to the caller */4022 explorer.queryInterfaceTo(aExplorer);4023 4024 return rc;4025 }4026 4027 STDMETHODIMP Appliance::Write(IN_BSTR format, IN_BSTR path, IProgress **aProgress)4028 {4029 if (!path) return E_POINTER;4030 CheckComArgOutPointerValid(aProgress);4031 4032 AutoCaller autoCaller(this);4033 if (FAILED(autoCaller.rc())) return autoCaller.rc();4034 4035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);4036 4037 // do not allow entering this method if the appliance is busy reading or writing4038 if (!isApplianceIdle())4039 return E_ACCESSDENIED;4040 4041 // see if we can handle this file; for now we insist it has an ".ovf" extension4042 Utf8Str strPath = path;4043 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))4044 return setError(VBOX_E_FILE_ERROR,4045 tr("Appliance file must have .ovf extension"));4046 4047 Utf8Str strFormat(format);4048 TaskExportOVF::OVFFormat ovfF;4049 if (strFormat == "ovf-0.9")4050 ovfF = TaskExportOVF::OVF_0_9;4051 else if (strFormat == "ovf-1.0")4052 ovfF = TaskExportOVF::OVF_1_0;4053 else4054 return setError(VBOX_E_FILE_ERROR,4055 tr("Invalid format \"%s\" specified"), strFormat.c_str());4056 4057 ComObjPtr<Progress> progress;4058 HRESULT rc = S_OK;4059 try4060 {4061 /* Parse all necessary info out of the URI */4062 parseURI(strPath, m->locInfo);4063 rc = writeImpl(ovfF, m->locInfo, progress);4064 }4065 catch (HRESULT aRC)4066 {4067 rc = aRC;4068 }4069 4070 if (SUCCEEDED(rc))4071 /* Return progress to the caller */4072 progress.queryInterfaceTo(aProgress);4073 4074 return rc;4075 }4076 4077 /**4078 * Public method implementation.4079 * @return4080 */4081 STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))4082 {4083 if (ComSafeArrayOutIsNull(aWarnings))4084 return E_POINTER;4085 4086 AutoCaller autoCaller(this);4087 if (FAILED(autoCaller.rc())) return autoCaller.rc();4088 4089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4090 4091 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());4092 4093 list<Utf8Str>::const_iterator it;4094 size_t i = 0;4095 for (it = m->llWarnings.begin();4096 it != m->llWarnings.end();4097 ++it, ++i)4098 {4099 Bstr bstr = *it;4100 bstr.cloneTo(&sfaWarnings[i]);4101 }4102 4103 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));4104 2840 4105 2841 return S_OK; … … 4485 3221 } 4486 3222 4487 ////////////////////////////////////////////////////////////////////////////////4488 //4489 // IMachine public methods4490 //4491 ////////////////////////////////////////////////////////////////////////////////4492 4493 // This code is here so we won't have to include the appliance headers in the4494 // IMachine implementation, and we also need to access private appliance data.4495 4496 /**4497 * Public method implementation.4498 * @param appliance4499 * @return4500 */4501 4502 STDMETHODIMP Machine::Export(IAppliance *aAppliance, IVirtualSystemDescription **aDescription)4503 {4504 HRESULT rc = S_OK;4505 4506 if (!aAppliance)4507 return E_POINTER;4508 4509 AutoCaller autoCaller(this);4510 if (FAILED(autoCaller.rc())) return autoCaller.rc();4511 4512 ComObjPtr<VirtualSystemDescription> pNewDesc;4513 4514 try4515 {4516 Bstr bstrName1;4517 Bstr bstrDescription;4518 Bstr bstrGuestOSType;4519 uint32_t cCPUs;4520 uint32_t ulMemSizeMB;4521 BOOL fUSBEnabled;4522 BOOL fAudioEnabled;4523 AudioControllerType_T audioController;4524 4525 ComPtr<IUSBController> pUsbController;4526 ComPtr<IAudioAdapter> pAudioAdapter;4527 4528 // first, call the COM methods, as they request locks4529 rc = COMGETTER(USBController)(pUsbController.asOutParam());4530 if (FAILED(rc))4531 fUSBEnabled = false;4532 else4533 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);4534 4535 // request the machine lock while acessing internal members4536 AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS);4537 4538 pAudioAdapter = mAudioAdapter;4539 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);4540 if (FAILED(rc)) throw rc;4541 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);4542 if (FAILED(rc)) throw rc;4543 4544 // get name4545 bstrName1 = mUserData->mName;4546 // get description4547 bstrDescription = mUserData->mDescription;4548 // get guest OS4549 bstrGuestOSType = mUserData->mOSTypeId;4550 // CPU count4551 cCPUs = mHWData->mCPUCount;4552 // memory size in MB4553 ulMemSizeMB = mHWData->mMemorySize;4554 // VRAM size?4555 // BIOS settings?4556 // 3D acceleration enabled?4557 // hardware virtualization enabled?4558 // nested paging enabled?4559 // HWVirtExVPIDEnabled?4560 // PAEEnabled?4561 // snapshotFolder?4562 // VRDPServer?4563 4564 // create a new virtual system4565 rc = pNewDesc.createObject();4566 if (FAILED(rc)) throw rc;4567 rc = pNewDesc->init();4568 if (FAILED(rc)) throw rc;4569 4570 /* Guest OS type */4571 Utf8Str strOsTypeVBox(bstrGuestOSType);4572 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());4573 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,4574 "",4575 Utf8StrFmt("%RI32", cim),4576 strOsTypeVBox);4577 4578 /* VM name */4579 Utf8Str strVMName(bstrName1);4580 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,4581 "",4582 strVMName,4583 strVMName);4584 4585 // description4586 Utf8Str strDescription(bstrDescription);4587 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,4588 "",4589 strDescription,4590 strDescription);4591 4592 /* CPU count*/4593 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);4594 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,4595 "",4596 strCpuCount,4597 strCpuCount);4598 4599 /* Memory */4600 Utf8Str strMemory = Utf8StrFmt("%RI64", (uint64_t)ulMemSizeMB * _1M);4601 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,4602 "",4603 strMemory,4604 strMemory);4605 4606 int32_t lIDEControllerIndex = 0;4607 int32_t lSATAControllerIndex = 0;4608 int32_t lSCSIControllerIndex = 0;4609 4610 /* Fetch all available storage controllers */4611 com::SafeIfaceArray<IStorageController> nwControllers;4612 rc = COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(nwControllers));4613 if (FAILED(rc)) throw rc;4614 4615 ComPtr<IStorageController> pIDEController;4616 #ifdef VBOX_WITH_AHCI4617 ComPtr<IStorageController> pSATAController;4618 #endif /* VBOX_WITH_AHCI */4619 #ifdef VBOX_WITH_LSILOGIC4620 ComPtr<IStorageController> pSCSIController;4621 #endif /* VBOX_WITH_LSILOGIC */4622 for (size_t j = 0; j < nwControllers.size(); ++j)4623 {4624 StorageBus_T eType;4625 rc = nwControllers[j]->COMGETTER(Bus)(&eType);4626 if (FAILED(rc)) throw rc;4627 if ( eType == StorageBus_IDE4628 && pIDEController.isNull())4629 pIDEController = nwControllers[j];4630 #ifdef VBOX_WITH_AHCI4631 else if ( eType == StorageBus_SATA4632 && pSATAController.isNull())4633 pSATAController = nwControllers[j];4634 #endif /* VBOX_WITH_AHCI */4635 #ifdef VBOX_WITH_LSILOGIC4636 else if ( eType == StorageBus_SCSI4637 && pSATAController.isNull())4638 pSCSIController = nwControllers[j];4639 #endif /* VBOX_WITH_LSILOGIC */4640 }4641 4642 // <const name="HardDiskControllerIDE" value="6" />4643 if (!pIDEController.isNull())4644 {4645 Utf8Str strVbox;4646 StorageControllerType_T ctlr;4647 rc = pIDEController->COMGETTER(ControllerType)(&ctlr);4648 if (FAILED(rc)) throw rc;4649 switch(ctlr)4650 {4651 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;4652 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;4653 case StorageControllerType_ICH6: strVbox = "ICH6"; break;4654 }4655 4656 if (strVbox.length())4657 {4658 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();4659 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,4660 Utf8StrFmt("%d", lIDEControllerIndex),4661 strVbox,4662 strVbox);4663 }4664 }4665 4666 #ifdef VBOX_WITH_AHCI4667 // <const name="HardDiskControllerSATA" value="7" />4668 if (!pSATAController.isNull())4669 {4670 Utf8Str strVbox = "AHCI";4671 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();4672 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,4673 Utf8StrFmt("%d", lSATAControllerIndex),4674 strVbox,4675 strVbox);4676 }4677 #endif // VBOX_WITH_AHCI4678 4679 #ifdef VBOX_WITH_LSILOGIC4680 // <const name="HardDiskControllerSCSI" value="8" />4681 if (!pSCSIController.isNull())4682 {4683 StorageControllerType_T ctlr;4684 rc = pSCSIController->COMGETTER(ControllerType)(&ctlr);4685 if (SUCCEEDED(rc))4686 {4687 Utf8Str strVbox = "LsiLogic"; // the default in VBox4688 switch(ctlr)4689 {4690 case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break;4691 case StorageControllerType_BusLogic: strVbox = "BusLogic"; break;4692 }4693 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();4694 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,4695 Utf8StrFmt("%d", lSCSIControllerIndex),4696 strVbox,4697 strVbox);4698 }4699 else4700 throw rc;4701 }4702 #endif // VBOX_WITH_LSILOGIC4703 4704 // <const name="HardDiskImage" value="9" />4705 // <const name="Floppy" value="18" />4706 // <const name="CDROM" value="19" />4707 4708 MediaData::AttachmentList::iterator itA;4709 for (itA = mMediaData->mAttachments.begin();4710 itA != mMediaData->mAttachments.end();4711 ++itA)4712 {4713 ComObjPtr<MediumAttachment> pHDA = *itA;4714 4715 // the attachment's data4716 ComPtr<IMedium> pMedium;4717 ComPtr<IStorageController> ctl;4718 Bstr controllerName;4719 4720 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());4721 if (FAILED(rc)) throw rc;4722 4723 rc = GetStorageControllerByName(controllerName, ctl.asOutParam());4724 if (FAILED(rc)) throw rc;4725 4726 StorageBus_T storageBus;4727 DeviceType_T deviceType;4728 LONG lChannel;4729 LONG lDevice;4730 4731 rc = ctl->COMGETTER(Bus)(&storageBus);4732 if (FAILED(rc)) throw rc;4733 4734 rc = pHDA->COMGETTER(Type)(&deviceType);4735 if (FAILED(rc)) throw rc;4736 4737 rc = pHDA->COMGETTER(Medium)(pMedium.asOutParam());4738 if (FAILED(rc)) throw rc;4739 4740 rc = pHDA->COMGETTER(Port)(&lChannel);4741 if (FAILED(rc)) throw rc;4742 4743 rc = pHDA->COMGETTER(Device)(&lDevice);4744 if (FAILED(rc)) throw rc;4745 4746 Utf8Str strTargetVmdkName;4747 Utf8Str strLocation;4748 ULONG64 ullSize = 0;4749 4750 if ( deviceType == DeviceType_HardDisk4751 && pMedium4752 )4753 {4754 Bstr bstrLocation;4755 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());4756 if (FAILED(rc)) throw rc;4757 strLocation = bstrLocation;4758 4759 Bstr bstrName;4760 rc = pMedium->COMGETTER(Name)(bstrName.asOutParam());4761 if (FAILED(rc)) throw rc;4762 4763 strTargetVmdkName = bstrName;4764 strTargetVmdkName.stripExt();4765 strTargetVmdkName.append(".vmdk");4766 4767 // we need the size of the image so we can give it to addEntry();4768 // later, on export, the progress weight will be based on this.4769 // pMedium can be a differencing image though; in that case, we4770 // need to use the size of the base instead.4771 ComPtr<IMedium> pBaseMedium;4772 rc = pMedium->COMGETTER(Base)(pBaseMedium.asOutParam());4773 // returns pMedium if there are no diff images4774 if (FAILED(rc)) throw rc;4775 4776 // force reading state, or else size will be returned as 04777 MediumState_T ms;4778 rc = pBaseMedium->RefreshState(&ms);4779 if (FAILED(rc)) throw rc;4780 4781 rc = pBaseMedium->COMGETTER(Size)(&ullSize);4782 if (FAILED(rc)) throw rc;4783 }4784 4785 // and how this translates to the virtual system4786 int32_t lControllerVsys = 0;4787 LONG lChannelVsys;4788 4789 switch (storageBus)4790 {4791 case StorageBus_IDE:4792 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,4793 // and it must be updated when that is changed!4794 4795 if (lChannel == 0 && lDevice == 0) // primary master4796 lChannelVsys = 0;4797 else if (lChannel == 0 && lDevice == 1) // primary slave4798 lChannelVsys = 1;4799 else if (lChannel == 1 && lDevice == 0) // secondary master; by default this is the CD-ROM but as of VirtualBox 3.1 that can change4800 lChannelVsys = 2;4801 else if (lChannel == 1 && lDevice == 1) // secondary slave4802 lChannelVsys = 3;4803 else4804 throw setError(VBOX_E_NOT_SUPPORTED,4805 tr("Cannot handle medium attachment: channel is %d, device is %d"), lChannel, lDevice);4806 4807 lControllerVsys = lIDEControllerIndex;4808 break;4809 4810 case StorageBus_SATA:4811 lChannelVsys = lChannel; // should be between 0 and 294812 lControllerVsys = lSATAControllerIndex;4813 break;4814 4815 case StorageBus_SCSI:4816 lChannelVsys = lChannel; // should be between 0 and 154817 lControllerVsys = lSCSIControllerIndex;4818 break;4819 4820 case StorageBus_Floppy:4821 lChannelVsys = 0;4822 lControllerVsys = 0;4823 break;4824 4825 default:4826 throw setError(VBOX_E_NOT_SUPPORTED,4827 tr("Cannot handle medium attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);4828 break;4829 }4830 4831 Utf8StrFmt strExtra("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys);4832 Utf8Str strEmpty;4833 4834 switch (deviceType)4835 {4836 case DeviceType_HardDisk:4837 Log(("Adding VirtualSystemDescriptionType_HardDiskImage, disk size: %RI64\n", ullSize));4838 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,4839 strTargetVmdkName, // disk ID: let's use the name4840 strTargetVmdkName, // OVF value:4841 strLocation, // vbox value: media path4842 (uint32_t)(ullSize / _1M),4843 strExtra);4844 break;4845 4846 case DeviceType_DVD:4847 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM,4848 strEmpty, // disk ID4849 strEmpty, // OVF value4850 strEmpty, // vbox value4851 1, // ulSize4852 strExtra);4853 break;4854 4855 case DeviceType_Floppy:4856 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy,4857 strEmpty, // disk ID4858 strEmpty, // OVF value4859 strEmpty, // vbox value4860 1, // ulSize4861 strExtra);4862 break;4863 }4864 }4865 4866 // <const name="NetworkAdapter" />4867 size_t a;4868 for (a = 0;4869 a < SchemaDefs::NetworkAdapterCount;4870 ++a)4871 {4872 ComPtr<INetworkAdapter> pNetworkAdapter;4873 BOOL fEnabled;4874 NetworkAdapterType_T adapterType;4875 NetworkAttachmentType_T attachmentType;4876 4877 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());4878 if (FAILED(rc)) throw rc;4879 /* Enable the network card & set the adapter type */4880 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);4881 if (FAILED(rc)) throw rc;4882 4883 if (fEnabled)4884 {4885 Utf8Str strAttachmentType;4886 4887 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);4888 if (FAILED(rc)) throw rc;4889 4890 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);4891 if (FAILED(rc)) throw rc;4892 4893 switch (attachmentType)4894 {4895 case NetworkAttachmentType_Null:4896 strAttachmentType = "Null";4897 break;4898 4899 case NetworkAttachmentType_NAT:4900 strAttachmentType = "NAT";4901 break;4902 4903 case NetworkAttachmentType_Bridged:4904 strAttachmentType = "Bridged";4905 break;4906 4907 case NetworkAttachmentType_Internal:4908 strAttachmentType = "Internal";4909 break;4910 4911 case NetworkAttachmentType_HostOnly:4912 strAttachmentType = "HostOnly";4913 break;4914 }4915 4916 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,4917 "", // ref4918 strAttachmentType, // orig4919 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf4920 0,4921 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf4922 }4923 }4924 4925 // <const name="USBController" />4926 #ifdef VBOX_WITH_USB4927 if (fUSBEnabled)4928 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");4929 #endif /* VBOX_WITH_USB */4930 4931 // <const name="SoundCard" />4932 if (fAudioEnabled)4933 {4934 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,4935 "",4936 "ensoniq1371", // this is what OVFTool writes and VMware supports4937 Utf8StrFmt("%RI32", audioController));4938 }4939 4940 // finally, add the virtual system to the appliance4941 Appliance *pAppliance = static_cast<Appliance*>(aAppliance);4942 AutoCaller autoCaller1(pAppliance);4943 if (FAILED(autoCaller1.rc())) return autoCaller1.rc();4944 4945 /* We return the new description to the caller */4946 ComPtr<IVirtualSystemDescription> copy(pNewDesc);4947 copy.queryInterfaceTo(aDescription);4948 4949 AutoWriteLock alock(pAppliance COMMA_LOCKVAL_SRC_POS);4950 4951 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);4952 }4953 catch(HRESULT arc)4954 {4955 rc = arc;4956 }4957 4958 return rc;4959 }4960 4961 /* vi: set tabstop=4 shiftwidth=4 expandtab: */ -
trunk/src/VBox/Main/ApplianceImplExport.cpp
r27797 r27829 45 45 #include "Logging.h" 46 46 47 #include "ApplianceImplPrivate.h" 48 47 49 using namespace std; 48 49 ////////////////////////////////////////////////////////////////////////////////50 //51 // Appliance data definition52 //53 ////////////////////////////////////////////////////////////////////////////////54 55 /* Describe a location for the import/export. The location could be a file on a56 * local hard disk or a remote target based on the supported inet protocols. */57 struct Appliance::LocationInfo58 {59 LocationInfo()60 : storageType(VFSType_File) {}61 VFSType_T storageType; /* Which type of storage should be handled */62 Utf8Str strPath; /* File path for the import/export */63 Utf8Str strHostname; /* Hostname on remote storage locations (could be empty) */64 Utf8Str strUsername; /* Username on remote storage locations (could be empty) */65 Utf8Str strPassword; /* Password on remote storage locations (could be empty) */66 };67 68 // opaque private instance data of Appliance class69 struct Appliance::Data70 {71 enum ApplianceState { ApplianceIdle, ApplianceImporting, ApplianceExporting };72 73 Data()74 : state(ApplianceIdle),75 pReader(NULL)76 {77 }78 79 ~Data()80 {81 if (pReader)82 {83 delete pReader;84 pReader = NULL;85 }86 }87 88 ApplianceState state;89 90 LocationInfo locInfo; // location info for the currently processed OVF91 92 OVFReader *pReader;93 94 bool fBusyWriting; // state protection; while this is true nobody else can call methods95 96 list< ComObjPtr<VirtualSystemDescription> >97 virtualSystemDescriptions;98 99 list<Utf8Str> llWarnings;100 101 ULONG ulWeightPerOperation;102 Utf8Str strOVFSHA1Digest;103 };104 105 struct VirtualSystemDescription::Data106 {107 list<VirtualSystemDescriptionEntry> llDescriptions;108 };109 50 110 51 //////////////////////////////////////////////////////////////////////////////// … … 114 55 //////////////////////////////////////////////////////////////////////////////// 115 56 116 static const struct117 {118 CIMOSType_T cim;119 const char *pcszVbox;120 }121 g_osTypes[] =122 {123 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },124 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },125 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },126 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },127 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },128 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },129 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },130 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },131 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },132 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris },133 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_OpenSolaris },134 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },135 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },136 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },137 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },138 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },139 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },140 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },141 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },142 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },143 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },144 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },145 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },146 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },147 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },148 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },149 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },150 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },151 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },152 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },153 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 },154 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },155 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },156 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },157 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },158 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },159 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },160 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },161 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},162 163 // { CIMOSType_CIMOS_TurboLinux_64, },164 165 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },166 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },167 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },168 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },169 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },170 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },171 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },172 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },173 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },174 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 },175 { CIMOSType_CIMOS_Linux_64, SchemaDefs_OSTypeId_Linux26_64 }176 };177 178 /* Pattern structure for matching the OS type description field */179 struct osTypePattern180 {181 const char *pcszPattern;182 const char *pcszVbox;183 };184 185 /* These are the 32-Bit ones. They are sorted by priority. */186 static const osTypePattern g_osTypesPattern[] =187 {188 {"Windows NT", SchemaDefs_OSTypeId_WindowsNT4},189 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP},190 {"Windows 2000", SchemaDefs_OSTypeId_Windows2000},191 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003},192 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista},193 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008},194 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE},195 {"Novell", SchemaDefs_OSTypeId_OpenSUSE},196 {"Red Hat", SchemaDefs_OSTypeId_RedHat},197 {"Mandriva", SchemaDefs_OSTypeId_Mandriva},198 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu},199 {"Debian", SchemaDefs_OSTypeId_Debian},200 {"QNX", SchemaDefs_OSTypeId_QNX},201 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24},202 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26},203 {"Linux", SchemaDefs_OSTypeId_Linux},204 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris},205 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris},206 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD},207 {"NetBSD", SchemaDefs_OSTypeId_NetBSD},208 {"Windows 95", SchemaDefs_OSTypeId_Windows95},209 {"Windows 98", SchemaDefs_OSTypeId_Windows98},210 {"Windows Me", SchemaDefs_OSTypeId_WindowsMe},211 {"Windows 3.", SchemaDefs_OSTypeId_Windows31},212 {"DOS", SchemaDefs_OSTypeId_DOS},213 {"OS2", SchemaDefs_OSTypeId_OS2}214 };215 216 /* These are the 64-Bit ones. They are sorted by priority. */217 static const osTypePattern g_osTypesPattern64[] =218 {219 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP_64},220 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003_64},221 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista_64},222 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008_64},223 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE_64},224 {"Novell", SchemaDefs_OSTypeId_OpenSUSE_64},225 {"Red Hat", SchemaDefs_OSTypeId_RedHat_64},226 {"Mandriva", SchemaDefs_OSTypeId_Mandriva_64},227 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu_64},228 {"Debian", SchemaDefs_OSTypeId_Debian_64},229 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24_64},230 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26_64},231 {"Linux", SchemaDefs_OSTypeId_Linux26_64},232 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris_64},233 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris_64},234 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD_64},235 };236 237 /**238 * Private helper func that suggests a VirtualBox guest OS type239 * for the given OVF operating system type.240 * @param osTypeVBox241 * @param c242 * @param cStr243 */244 static void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c, const Utf8Str &cStr)245 {246 /* First check if the type is other/other_64 */247 if (c == CIMOSType_CIMOS_Other)248 {249 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern); ++i)250 if (cStr.contains (g_osTypesPattern[i].pcszPattern, Utf8Str::CaseInsensitive))251 {252 strType = g_osTypesPattern[i].pcszVbox;253 return;254 }255 }256 else if (c == CIMOSType_CIMOS_Other_64)257 {258 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern64); ++i)259 if (cStr.contains (g_osTypesPattern64[i].pcszPattern, Utf8Str::CaseInsensitive))260 {261 strType = g_osTypesPattern64[i].pcszVbox;262 return;263 }264 }265 266 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)267 {268 if (c == g_osTypes[i].cim)269 {270 strType = g_osTypes[i].pcszVbox;271 return;272 }273 }274 275 strType = SchemaDefs_OSTypeId_Other;276 }277 278 /**279 * Private helper func that suggests a VirtualBox guest OS type280 * for the given OVF operating system type.281 * @param osTypeVBox282 * @param c283 */284 static CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)285 {286 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)287 {288 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))289 return g_osTypes[i].cim;290 }291 292 return CIMOSType_CIMOS_Other;293 }294 295 57 //////////////////////////////////////////////////////////////////////////////// 296 58 // 297 // I VirtualBoxpublic methods59 // IMachine public methods 298 60 // 299 61 //////////////////////////////////////////////////////////////////////////////// 300 62 301 63 // This code is here so we won't have to include the appliance headers in the 302 // I VirtualBox implementation.64 // IMachine implementation, and we also need to access private appliance data. 303 65 304 66 /** 305 * Implementation for IVirtualBox::createAppliance.306 * 307 * @param anAppliance IAppliance object created if S_OK is returned. 308 * @return S_OK or error. 309 */ 310 STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)67 * Public method implementation. 68 * @param appliance 69 * @return 70 */ 71 72 STDMETHODIMP Machine::Export(IAppliance *aAppliance, IVirtualSystemDescription **aDescription) 311 73 { 312 HRESULT rc; 313 314 ComObjPtr<Appliance> appliance; 315 appliance.createObject(); 316 rc = appliance->init(this); 317 318 if (SUCCEEDED(rc)) 319 appliance.queryInterfaceTo(anAppliance); 74 HRESULT rc = S_OK; 75 76 if (!aAppliance) 77 return E_POINTER; 78 79 AutoCaller autoCaller(this); 80 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 81 82 ComObjPtr<VirtualSystemDescription> pNewDesc; 83 84 try 85 { 86 Bstr bstrName1; 87 Bstr bstrDescription; 88 Bstr bstrGuestOSType; 89 uint32_t cCPUs; 90 uint32_t ulMemSizeMB; 91 BOOL fUSBEnabled; 92 BOOL fAudioEnabled; 93 AudioControllerType_T audioController; 94 95 ComPtr<IUSBController> pUsbController; 96 ComPtr<IAudioAdapter> pAudioAdapter; 97 98 // first, call the COM methods, as they request locks 99 rc = COMGETTER(USBController)(pUsbController.asOutParam()); 100 if (FAILED(rc)) 101 fUSBEnabled = false; 102 else 103 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled); 104 105 // request the machine lock while acessing internal members 106 AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS); 107 108 pAudioAdapter = mAudioAdapter; 109 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled); 110 if (FAILED(rc)) throw rc; 111 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController); 112 if (FAILED(rc)) throw rc; 113 114 // get name 115 bstrName1 = mUserData->mName; 116 // get description 117 bstrDescription = mUserData->mDescription; 118 // get guest OS 119 bstrGuestOSType = mUserData->mOSTypeId; 120 // CPU count 121 cCPUs = mHWData->mCPUCount; 122 // memory size in MB 123 ulMemSizeMB = mHWData->mMemorySize; 124 // VRAM size? 125 // BIOS settings? 126 // 3D acceleration enabled? 127 // hardware virtualization enabled? 128 // nested paging enabled? 129 // HWVirtExVPIDEnabled? 130 // PAEEnabled? 131 // snapshotFolder? 132 // VRDPServer? 133 134 // create a new virtual system 135 rc = pNewDesc.createObject(); 136 if (FAILED(rc)) throw rc; 137 rc = pNewDesc->init(); 138 if (FAILED(rc)) throw rc; 139 140 /* Guest OS type */ 141 Utf8Str strOsTypeVBox(bstrGuestOSType); 142 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str()); 143 pNewDesc->addEntry(VirtualSystemDescriptionType_OS, 144 "", 145 Utf8StrFmt("%RI32", cim), 146 strOsTypeVBox); 147 148 /* VM name */ 149 Utf8Str strVMName(bstrName1); 150 pNewDesc->addEntry(VirtualSystemDescriptionType_Name, 151 "", 152 strVMName, 153 strVMName); 154 155 // description 156 Utf8Str strDescription(bstrDescription); 157 pNewDesc->addEntry(VirtualSystemDescriptionType_Description, 158 "", 159 strDescription, 160 strDescription); 161 162 /* CPU count*/ 163 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs); 164 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU, 165 "", 166 strCpuCount, 167 strCpuCount); 168 169 /* Memory */ 170 Utf8Str strMemory = Utf8StrFmt("%RI64", (uint64_t)ulMemSizeMB * _1M); 171 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory, 172 "", 173 strMemory, 174 strMemory); 175 176 int32_t lIDEControllerIndex = 0; 177 int32_t lSATAControllerIndex = 0; 178 int32_t lSCSIControllerIndex = 0; 179 180 /* Fetch all available storage controllers */ 181 com::SafeIfaceArray<IStorageController> nwControllers; 182 rc = COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(nwControllers)); 183 if (FAILED(rc)) throw rc; 184 185 ComPtr<IStorageController> pIDEController; 186 #ifdef VBOX_WITH_AHCI 187 ComPtr<IStorageController> pSATAController; 188 #endif /* VBOX_WITH_AHCI */ 189 #ifdef VBOX_WITH_LSILOGIC 190 ComPtr<IStorageController> pSCSIController; 191 #endif /* VBOX_WITH_LSILOGIC */ 192 for (size_t j = 0; j < nwControllers.size(); ++j) 193 { 194 StorageBus_T eType; 195 rc = nwControllers[j]->COMGETTER(Bus)(&eType); 196 if (FAILED(rc)) throw rc; 197 if ( eType == StorageBus_IDE 198 && pIDEController.isNull()) 199 pIDEController = nwControllers[j]; 200 #ifdef VBOX_WITH_AHCI 201 else if ( eType == StorageBus_SATA 202 && pSATAController.isNull()) 203 pSATAController = nwControllers[j]; 204 #endif /* VBOX_WITH_AHCI */ 205 #ifdef VBOX_WITH_LSILOGIC 206 else if ( eType == StorageBus_SCSI 207 && pSATAController.isNull()) 208 pSCSIController = nwControllers[j]; 209 #endif /* VBOX_WITH_LSILOGIC */ 210 } 211 212 // <const name="HardDiskControllerIDE" value="6" /> 213 if (!pIDEController.isNull()) 214 { 215 Utf8Str strVbox; 216 StorageControllerType_T ctlr; 217 rc = pIDEController->COMGETTER(ControllerType)(&ctlr); 218 if (FAILED(rc)) throw rc; 219 switch(ctlr) 220 { 221 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break; 222 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break; 223 case StorageControllerType_ICH6: strVbox = "ICH6"; break; 224 } 225 226 if (strVbox.length()) 227 { 228 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size(); 229 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE, 230 Utf8StrFmt("%d", lIDEControllerIndex), 231 strVbox, 232 strVbox); 233 } 234 } 235 236 #ifdef VBOX_WITH_AHCI 237 // <const name="HardDiskControllerSATA" value="7" /> 238 if (!pSATAController.isNull()) 239 { 240 Utf8Str strVbox = "AHCI"; 241 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size(); 242 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA, 243 Utf8StrFmt("%d", lSATAControllerIndex), 244 strVbox, 245 strVbox); 246 } 247 #endif // VBOX_WITH_AHCI 248 249 #ifdef VBOX_WITH_LSILOGIC 250 // <const name="HardDiskControllerSCSI" value="8" /> 251 if (!pSCSIController.isNull()) 252 { 253 StorageControllerType_T ctlr; 254 rc = pSCSIController->COMGETTER(ControllerType)(&ctlr); 255 if (SUCCEEDED(rc)) 256 { 257 Utf8Str strVbox = "LsiLogic"; // the default in VBox 258 switch(ctlr) 259 { 260 case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break; 261 case StorageControllerType_BusLogic: strVbox = "BusLogic"; break; 262 } 263 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size(); 264 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI, 265 Utf8StrFmt("%d", lSCSIControllerIndex), 266 strVbox, 267 strVbox); 268 } 269 else 270 throw rc; 271 } 272 #endif // VBOX_WITH_LSILOGIC 273 274 // <const name="HardDiskImage" value="9" /> 275 // <const name="Floppy" value="18" /> 276 // <const name="CDROM" value="19" /> 277 278 MediaData::AttachmentList::iterator itA; 279 for (itA = mMediaData->mAttachments.begin(); 280 itA != mMediaData->mAttachments.end(); 281 ++itA) 282 { 283 ComObjPtr<MediumAttachment> pHDA = *itA; 284 285 // the attachment's data 286 ComPtr<IMedium> pMedium; 287 ComPtr<IStorageController> ctl; 288 Bstr controllerName; 289 290 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam()); 291 if (FAILED(rc)) throw rc; 292 293 rc = GetStorageControllerByName(controllerName, ctl.asOutParam()); 294 if (FAILED(rc)) throw rc; 295 296 StorageBus_T storageBus; 297 DeviceType_T deviceType; 298 LONG lChannel; 299 LONG lDevice; 300 301 rc = ctl->COMGETTER(Bus)(&storageBus); 302 if (FAILED(rc)) throw rc; 303 304 rc = pHDA->COMGETTER(Type)(&deviceType); 305 if (FAILED(rc)) throw rc; 306 307 rc = pHDA->COMGETTER(Medium)(pMedium.asOutParam()); 308 if (FAILED(rc)) throw rc; 309 310 rc = pHDA->COMGETTER(Port)(&lChannel); 311 if (FAILED(rc)) throw rc; 312 313 rc = pHDA->COMGETTER(Device)(&lDevice); 314 if (FAILED(rc)) throw rc; 315 316 Utf8Str strTargetVmdkName; 317 Utf8Str strLocation; 318 ULONG64 ullSize = 0; 319 320 if ( deviceType == DeviceType_HardDisk 321 && pMedium 322 ) 323 { 324 Bstr bstrLocation; 325 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam()); 326 if (FAILED(rc)) throw rc; 327 strLocation = bstrLocation; 328 329 Bstr bstrName; 330 rc = pMedium->COMGETTER(Name)(bstrName.asOutParam()); 331 if (FAILED(rc)) throw rc; 332 333 strTargetVmdkName = bstrName; 334 strTargetVmdkName.stripExt(); 335 strTargetVmdkName.append(".vmdk"); 336 337 // we need the size of the image so we can give it to addEntry(); 338 // later, on export, the progress weight will be based on this. 339 // pMedium can be a differencing image though; in that case, we 340 // need to use the size of the base instead. 341 ComPtr<IMedium> pBaseMedium; 342 rc = pMedium->COMGETTER(Base)(pBaseMedium.asOutParam()); 343 // returns pMedium if there are no diff images 344 if (FAILED(rc)) throw rc; 345 346 // force reading state, or else size will be returned as 0 347 MediumState_T ms; 348 rc = pBaseMedium->RefreshState(&ms); 349 if (FAILED(rc)) throw rc; 350 351 rc = pBaseMedium->COMGETTER(Size)(&ullSize); 352 if (FAILED(rc)) throw rc; 353 } 354 355 // and how this translates to the virtual system 356 int32_t lControllerVsys = 0; 357 LONG lChannelVsys; 358 359 switch (storageBus) 360 { 361 case StorageBus_IDE: 362 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines, 363 // and it must be updated when that is changed! 364 365 if (lChannel == 0 && lDevice == 0) // primary master 366 lChannelVsys = 0; 367 else if (lChannel == 0 && lDevice == 1) // primary slave 368 lChannelVsys = 1; 369 else if (lChannel == 1 && lDevice == 0) // secondary master; by default this is the CD-ROM but as of VirtualBox 3.1 that can change 370 lChannelVsys = 2; 371 else if (lChannel == 1 && lDevice == 1) // secondary slave 372 lChannelVsys = 3; 373 else 374 throw setError(VBOX_E_NOT_SUPPORTED, 375 tr("Cannot handle medium attachment: channel is %d, device is %d"), lChannel, lDevice); 376 377 lControllerVsys = lIDEControllerIndex; 378 break; 379 380 case StorageBus_SATA: 381 lChannelVsys = lChannel; // should be between 0 and 29 382 lControllerVsys = lSATAControllerIndex; 383 break; 384 385 case StorageBus_SCSI: 386 lChannelVsys = lChannel; // should be between 0 and 15 387 lControllerVsys = lSCSIControllerIndex; 388 break; 389 390 case StorageBus_Floppy: 391 lChannelVsys = 0; 392 lControllerVsys = 0; 393 break; 394 395 default: 396 throw setError(VBOX_E_NOT_SUPPORTED, 397 tr("Cannot handle medium attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice); 398 break; 399 } 400 401 Utf8StrFmt strExtra("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys); 402 Utf8Str strEmpty; 403 404 switch (deviceType) 405 { 406 case DeviceType_HardDisk: 407 Log(("Adding VirtualSystemDescriptionType_HardDiskImage, disk size: %RI64\n", ullSize)); 408 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage, 409 strTargetVmdkName, // disk ID: let's use the name 410 strTargetVmdkName, // OVF value: 411 strLocation, // vbox value: media path 412 (uint32_t)(ullSize / _1M), 413 strExtra); 414 break; 415 416 case DeviceType_DVD: 417 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, 418 strEmpty, // disk ID 419 strEmpty, // OVF value 420 strEmpty, // vbox value 421 1, // ulSize 422 strExtra); 423 break; 424 425 case DeviceType_Floppy: 426 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, 427 strEmpty, // disk ID 428 strEmpty, // OVF value 429 strEmpty, // vbox value 430 1, // ulSize 431 strExtra); 432 break; 433 } 434 } 435 436 // <const name="NetworkAdapter" /> 437 size_t a; 438 for (a = 0; 439 a < SchemaDefs::NetworkAdapterCount; 440 ++a) 441 { 442 ComPtr<INetworkAdapter> pNetworkAdapter; 443 BOOL fEnabled; 444 NetworkAdapterType_T adapterType; 445 NetworkAttachmentType_T attachmentType; 446 447 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam()); 448 if (FAILED(rc)) throw rc; 449 /* Enable the network card & set the adapter type */ 450 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled); 451 if (FAILED(rc)) throw rc; 452 453 if (fEnabled) 454 { 455 Utf8Str strAttachmentType; 456 457 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType); 458 if (FAILED(rc)) throw rc; 459 460 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType); 461 if (FAILED(rc)) throw rc; 462 463 switch (attachmentType) 464 { 465 case NetworkAttachmentType_Null: 466 strAttachmentType = "Null"; 467 break; 468 469 case NetworkAttachmentType_NAT: 470 strAttachmentType = "NAT"; 471 break; 472 473 case NetworkAttachmentType_Bridged: 474 strAttachmentType = "Bridged"; 475 break; 476 477 case NetworkAttachmentType_Internal: 478 strAttachmentType = "Internal"; 479 break; 480 481 case NetworkAttachmentType_HostOnly: 482 strAttachmentType = "HostOnly"; 483 break; 484 } 485 486 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter, 487 "", // ref 488 strAttachmentType, // orig 489 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf 490 0, 491 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf 492 } 493 } 494 495 // <const name="USBController" /> 496 #ifdef VBOX_WITH_USB 497 if (fUSBEnabled) 498 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", ""); 499 #endif /* VBOX_WITH_USB */ 500 501 // <const name="SoundCard" /> 502 if (fAudioEnabled) 503 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard, 504 "", 505 "ensoniq1371", // this is what OVFTool writes and VMware supports 506 Utf8StrFmt("%RI32", audioController)); 507 508 // finally, add the virtual system to the appliance 509 Appliance *pAppliance = static_cast<Appliance*>(aAppliance); 510 AutoCaller autoCaller1(pAppliance); 511 if (FAILED(autoCaller1.rc())) return autoCaller1.rc(); 512 513 /* We return the new description to the caller */ 514 ComPtr<IVirtualSystemDescription> copy(pNewDesc); 515 copy.queryInterfaceTo(aDescription); 516 517 AutoWriteLock alock(pAppliance COMMA_LOCKVAL_SRC_POS); 518 519 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc); 520 } 521 catch(HRESULT arc) 522 { 523 rc = arc; 524 } 320 525 321 526 return rc; … … 324 529 //////////////////////////////////////////////////////////////////////////////// 325 530 // 326 // Appliance constructor / destructor531 // IAppliance public methods 327 532 // 328 533 //////////////////////////////////////////////////////////////////////////////// 329 534 330 Appliance::Appliance()331 : mVirtualBox(NULL)332 {333 }334 335 Appliance::~Appliance()336 {337 }338 339 535 /** 340 * Appliance COM initializer. 341 * @param 536 * Public method implementation. 537 * @param format 538 * @param path 539 * @param aProgress 342 540 * @return 343 541 */ 344 HRESULT Appliance::init(VirtualBox *aVirtualBox)542 STDMETHODIMP Appliance::Write(IN_BSTR format, IN_BSTR path, IProgress **aProgress) 345 543 { 346 /* Enclose the state transition NotReady->InInit->Ready */ 347 AutoInitSpan autoInitSpan(this); 348 AssertReturn(autoInitSpan.isOk(), E_FAIL); 349 350 /* Weak reference to a VirtualBox object */ 351 unconst(mVirtualBox) = aVirtualBox; 352 353 // initialize data 354 m = new Data; 355 356 /* Confirm a successful initialization */ 357 autoInitSpan.setSucceeded(); 358 359 return S_OK; 360 } 361 362 /** 363 * Appliance COM uninitializer. 364 * @return 365 */ 366 void Appliance::uninit() 367 { 368 /* Enclose the state transition Ready->InUninit->NotReady */ 369 AutoUninitSpan autoUninitSpan(this); 370 if (autoUninitSpan.uninitDone()) 371 return; 372 373 delete m; 374 m = NULL; 544 if (!path) return E_POINTER; 545 CheckComArgOutPointerValid(aProgress); 546 547 AutoCaller autoCaller(this); 548 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 549 550 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 551 552 // do not allow entering this method if the appliance is busy reading or writing 553 if (!isApplianceIdle()) 554 return E_ACCESSDENIED; 555 556 // see if we can handle this file; for now we insist it has an ".ovf" extension 557 Utf8Str strPath = path; 558 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)) 559 return setError(VBOX_E_FILE_ERROR, 560 tr("Appliance file must have .ovf extension")); 561 562 Utf8Str strFormat(format); 563 TaskExportOVF::OVFFormat ovfF; 564 if (strFormat == "ovf-0.9") 565 ovfF = TaskExportOVF::OVF_0_9; 566 else if (strFormat == "ovf-1.0") 567 ovfF = TaskExportOVF::OVF_1_0; 568 else 569 return setError(VBOX_E_FILE_ERROR, 570 tr("Invalid format \"%s\" specified"), strFormat.c_str()); 571 572 ComObjPtr<Progress> progress; 573 HRESULT rc = S_OK; 574 try 575 { 576 /* Parse all necessary info out of the URI */ 577 parseURI(strPath, m->locInfo); 578 rc = writeImpl(ovfF, m->locInfo, progress); 579 } 580 catch (HRESULT aRC) 581 { 582 rc = aRC; 583 } 584 585 if (SUCCEEDED(rc)) 586 /* Return progress to the caller */ 587 progress.queryInterfaceTo(aProgress); 588 589 return rc; 375 590 } 376 591 … … 382 597 383 598 /** 384 * Returns true if the appliance is in "idle" state. This should always be the 385 * case unless an import or export is currently in progress. Similar to machine 386 * states, this permits the Appliance implementation code to let go of the 387 * Appliance object lock while a time-consuming disk conversion is in progress 388 * without exposing the appliance to conflicting calls. 599 * Implementation for writing out the OVF to disk. This starts a new thread which will call 600 * Appliance::taskThreadWriteOVF(). This is in a separate private method because it is used 601 * from two locations: 389 602 * 390 * This sets an error on "this" (the appliance) and returns false if the appliance391 * is busy. The caller should then return E_ACCESSDENIED.603 * 1) from the public Appliance::Write(). 604 * 2) from Appliance::writeS3(), which got called from a previous instance of Appliance::taskThreadWriteOVF(). 392 605 * 393 * Must be called from under the object lock! 394 * 395 * @return 396 */ 397 bool Appliance::isApplianceIdle() const 398 { 399 if (m->state == Data::ApplianceImporting) 400 setError(VBOX_E_INVALID_OBJECT_STATE, "The appliance is busy importing files"); 401 else if (m->state == Data::ApplianceExporting) 402 setError(VBOX_E_INVALID_OBJECT_STATE, "The appliance is busy exporting files"); 403 else 404 return true; 405 406 return false; 407 } 408 409 HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const 410 { 411 IMachine *machine = NULL; 412 char *tmpName = RTStrDup(aName.c_str()); 413 int i = 1; 414 /* @todo: Maybe too cost-intensive; try to find a lighter way */ 415 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND) 416 { 417 RTStrFree(tmpName); 418 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i); 419 ++i; 420 } 421 aName = tmpName; 422 RTStrFree(tmpName); 423 424 return S_OK; 425 } 426 427 HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const 428 { 429 IMedium *harddisk = NULL; 430 char *tmpName = RTStrDup(aName.c_str()); 431 int i = 1; 432 /* Check if the file exists or if a file with this path is registered 433 * already */ 434 /* @todo: Maybe too cost-intensive; try to find a lighter way */ 435 while (RTPathExists(tmpName) || 436 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND) 437 { 438 RTStrFree(tmpName); 439 char *tmpDir = RTStrDup(aName.c_str()); 440 RTPathStripFilename(tmpDir);; 441 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str())); 442 RTPathStripExt(tmpFile); 443 const char *tmpExt = RTPathExt(aName.c_str()); 444 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt); 445 RTStrFree(tmpFile); 446 RTStrFree(tmpDir); 447 ++i; 448 } 449 aName = tmpName; 450 RTStrFree(tmpName); 451 452 return S_OK; 453 } 454 455 /** 456 * Called from the import and export background threads to synchronize the second 457 * background disk thread's progress object with the current progress object so 458 * that the user interface sees progress correctly and that cancel signals are 459 * passed on to the second thread. 460 * @param pProgressThis Progress object of the current thread. 461 * @param pProgressAsync Progress object of asynchronous task running in background. 462 */ 463 void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis, 464 ComPtr<IProgress> &pProgressAsync) 465 { 466 HRESULT rc; 467 468 // now loop until the asynchronous operation completes and then report its result 469 BOOL fCompleted; 470 BOOL fCanceled; 471 ULONG currentPercent; 472 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted)))) 473 { 474 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled); 475 if (FAILED(rc)) throw rc; 476 if (fCanceled) 477 { 478 pProgressAsync->Cancel(); 479 break; 480 } 481 482 rc = pProgressAsync->COMGETTER(Percent(¤tPercent)); 483 if (FAILED(rc)) throw rc; 484 if (!pProgressThis.isNull()) 485 pProgressThis->SetCurrentOperationProgress(currentPercent); 486 if (fCompleted) 487 break; 488 489 /* Make sure the loop is not too tight */ 490 rc = pProgressAsync->WaitForCompletion(100); 491 if (FAILED(rc)) throw rc; 492 } 493 // report result of asynchronous operation 494 LONG iRc; 495 rc = pProgressAsync->COMGETTER(ResultCode)(&iRc); 496 if (FAILED(rc)) throw rc; 497 498 499 // if the thread of the progress object has an error, then 500 // retrieve the error info from there, or it'll be lost 501 if (FAILED(iRc)) 502 { 503 ProgressErrorInfo info(pProgressAsync); 504 Utf8Str str(info.getText()); 505 const char *pcsz = str.c_str(); 506 HRESULT rc2 = setError(iRc, pcsz); 507 throw rc2; 508 } 509 } 510 511 void Appliance::addWarning(const char* aWarning, ...) 512 { 513 va_list args; 514 va_start(args, aWarning); 515 Utf8StrFmtVA str(aWarning, args); 516 va_end(args); 517 m->llWarnings.push_back(str); 518 } 519 520 void Appliance::disksWeight(uint32_t &ulTotalMB, uint32_t &cDisks) const 521 { 522 ulTotalMB = 0; 523 cDisks = 0; 524 /* Weigh the disk images according to their sizes */ 525 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; 526 for (it = m->virtualSystemDescriptions.begin(); 527 it != m->virtualSystemDescriptions.end(); 528 ++it) 529 { 530 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it); 531 /* One for every hard disk of the Virtual System */ 532 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 533 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH; 534 for (itH = avsdeHDs.begin(); 535 itH != avsdeHDs.end(); 536 ++itH) 537 { 538 const VirtualSystemDescriptionEntry *pHD = *itH; 539 ulTotalMB += pHD->ulSizeMB; 540 ++cDisks; 541 } 542 } 543 544 } 545 546 HRESULT Appliance::setUpProgressFS(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription) 547 { 548 HRESULT rc; 549 550 /* Create the progress object */ 551 pProgress.createObject(); 552 553 /* Weigh the disk images according to their sizes */ 554 uint32_t ulTotalMB; 555 uint32_t cDisks; 556 disksWeight(ulTotalMB, cDisks); 557 558 ULONG cOperations = 1 + cDisks; // one op per disk plus 1 for the XML 559 560 ULONG ulTotalOperationsWeight; 561 if (ulTotalMB) 562 { 563 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for the XML 564 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation; 565 } 566 else 567 { 568 // no disks to export: 569 ulTotalOperationsWeight = 1; 570 m->ulWeightPerOperation = 1; 571 } 572 573 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n", 574 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation)); 575 576 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 577 bstrDescription, 578 TRUE /* aCancelable */, 579 cOperations, // ULONG cOperations, 580 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight, 581 bstrDescription, // CBSTR bstrFirstOperationDescription, 582 m->ulWeightPerOperation); // ULONG ulFirstOperationWeight, 583 return rc; 584 } 585 586 HRESULT Appliance::setUpProgressImportS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription) 587 { 588 HRESULT rc; 589 590 /* Create the progress object */ 591 pProgress.createObject(); 592 593 /* Weigh the disk images according to their sizes */ 594 uint32_t ulTotalMB; 595 uint32_t cDisks; 596 disksWeight(ulTotalMB, cDisks); 597 598 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 */ 599 600 ULONG ulTotalOperationsWeight = ulTotalMB; 601 if (!ulTotalOperationsWeight) 602 // no disks to export: 603 ulTotalOperationsWeight = 1; 604 605 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import 606 ulTotalOperationsWeight += ulImportWeight; 607 608 m->ulWeightPerOperation = ulImportWeight; /* save for using later */ 609 610 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init 611 ulTotalOperationsWeight += ulInitWeight; 612 613 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n", 614 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation)); 615 616 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 617 bstrDescription, 618 TRUE /* aCancelable */, 619 cOperations, // ULONG cOperations, 620 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight, 621 Bstr(tr("Init")), // CBSTR bstrFirstOperationDescription, 622 ulInitWeight); // ULONG ulFirstOperationWeight, 623 return rc; 624 } 625 626 HRESULT Appliance::setUpProgressWriteS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription) 627 { 628 HRESULT rc; 629 630 /* Create the progress object */ 631 pProgress.createObject(); 632 633 /* Weigh the disk images according to their sizes */ 634 uint32_t ulTotalMB; 635 uint32_t cDisks; 636 disksWeight(ulTotalMB, cDisks); 637 638 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 */ 639 640 ULONG ulTotalOperationsWeight; 641 if (ulTotalMB) 642 { 643 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) 644 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation; 645 } 646 else 647 { 648 // no disks to export: 649 ulTotalOperationsWeight = 1; 650 m->ulWeightPerOperation = 1; 651 } 652 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */ 653 ulTotalOperationsWeight += ulOVFCreationWeight; 654 655 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n", 656 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation)); 657 658 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 659 bstrDescription, 660 TRUE /* aCancelable */, 661 cOperations, // ULONG cOperations, 662 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight, 663 bstrDescription, // CBSTR bstrFirstOperationDescription, 664 ulOVFCreationWeight); // ULONG ulFirstOperationWeight, 665 return rc; 666 } 667 668 void Appliance::parseURI(Utf8Str strUri, LocationInfo &locInfo) const 669 { 670 /* Check the URI for the protocol */ 671 if (strUri.startsWith("file://", Utf8Str::CaseInsensitive)) /* File based */ 672 { 673 locInfo.storageType = VFSType_File; 674 strUri = strUri.substr(sizeof("file://") - 1); 675 } 676 else if (strUri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */ 677 { 678 locInfo.storageType = VFSType_S3; 679 strUri = strUri.substr(sizeof("SunCloud://") - 1); 680 } 681 else if (strUri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */ 682 { 683 locInfo.storageType = VFSType_S3; 684 strUri = strUri.substr(sizeof("S3://") - 1); 685 } 686 else if (strUri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */ 687 throw E_NOTIMPL; 688 689 /* Not necessary on a file based URI */ 690 if (locInfo.storageType != VFSType_File) 691 { 692 size_t uppos = strUri.find("@"); /* username:password combo */ 693 if (uppos != Utf8Str::npos) 694 { 695 locInfo.strUsername = strUri.substr(0, uppos); 696 strUri = strUri.substr(uppos + 1); 697 size_t upos = locInfo.strUsername.find(":"); 698 if (upos != Utf8Str::npos) 699 { 700 locInfo.strPassword = locInfo.strUsername.substr(upos + 1); 701 locInfo.strUsername = locInfo.strUsername.substr(0, upos); 702 } 703 } 704 size_t hpos = strUri.find("/"); /* hostname part */ 705 if (hpos != Utf8Str::npos) 706 { 707 locInfo.strHostname = strUri.substr(0, hpos); 708 strUri = strUri.substr(hpos); 709 } 710 } 711 712 locInfo.strPath = strUri; 713 } 714 715 void Appliance::parseBucket(Utf8Str &aPath, Utf8Str &aBucket) const 716 { 717 /* Buckets are S3 specific. So parse the bucket out of the file path */ 718 if (!aPath.startsWith("/")) 719 throw setError(E_INVALIDARG, 720 tr("The path '%s' must start with /"), aPath.c_str()); 721 size_t bpos = aPath.find("/", 1); 722 if (bpos != Utf8Str::npos) 723 { 724 aBucket = aPath.substr(1, bpos - 1); /* The bucket without any slashes */ 725 aPath = aPath.substr(bpos); /* The rest of the file path */ 726 } 727 /* If there is no bucket name provided reject it */ 728 if (aBucket.isEmpty()) 729 throw setError(E_INVALIDARG, 730 tr("You doesn't provide a bucket name in the URI '%s'"), aPath.c_str()); 731 } 732 733 Utf8Str Appliance::manifestFileName(Utf8Str aPath) const 734 { 735 /* Get the name part */ 736 char *pszMfName = RTStrDup(RTPathFilename(aPath.c_str())); 737 /* Strip any extensions */ 738 RTPathStripExt(pszMfName); 739 /* Path without the filename */ 740 aPath.stripFilename(); 741 /* Format the manifest path */ 742 Utf8StrFmt strMfFile("%s/%s.mf", aPath.c_str(), pszMfName); 743 RTStrFree(pszMfName); 744 return strMfFile; 745 } 746 747 struct Appliance::TaskOVF 748 { 749 TaskOVF(Appliance *aThat) 750 : pAppliance(aThat), 751 rc(S_OK) 752 {} 753 754 static int updateProgress(unsigned uPercent, void *pvUser); 755 756 LocationInfo locInfo; 757 Appliance *pAppliance; 758 ComObjPtr<Progress> progress; 759 HRESULT rc; 760 }; 761 762 struct Appliance::TaskImportOVF : Appliance::TaskOVF 763 { 764 enum TaskType 765 { 766 Read, 767 Import 768 }; 769 770 TaskImportOVF(Appliance *aThat) 771 : TaskOVF(aThat), 772 taskType(Read) 773 {} 774 775 int startThread(); 776 777 TaskType taskType; 778 }; 779 780 struct Appliance::TaskExportOVF : Appliance::TaskOVF 781 { 782 enum OVFFormat 783 { 784 unspecified, 785 OVF_0_9, 786 OVF_1_0 787 }; 788 enum TaskType 789 { 790 Write 791 }; 792 793 TaskExportOVF(Appliance *aThat) 794 : TaskOVF(aThat), 795 taskType(Write) 796 {} 797 798 int startThread(); 799 800 TaskType taskType; 801 OVFFormat enFormat; 802 }; 803 804 struct MyHardDiskAttachment 805 { 806 Bstr bstrUuid; 807 ComPtr<IMachine> pMachine; 808 Bstr controllerType; 809 int32_t lChannel; 810 int32_t lDevice; 811 }; 812 813 /* static */ 814 int Appliance::TaskOVF::updateProgress(unsigned uPercent, void *pvUser) 815 { 816 Appliance::TaskOVF* pTask = *(Appliance::TaskOVF**)pvUser; 817 818 if ( pTask 819 && !pTask->progress.isNull()) 820 { 821 BOOL fCanceled; 822 pTask->progress->COMGETTER(Canceled)(&fCanceled); 823 if (fCanceled) 824 return -1; 825 pTask->progress->SetCurrentOperationProgress(uPercent); 826 } 827 return VINF_SUCCESS; 828 } 829 830 HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress) 831 { 832 /* Initialize our worker task */ 833 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this)); 834 /* What should the task do */ 835 task->taskType = TaskImportOVF::Read; 836 /* Copy the current location info to the task */ 837 task->locInfo = aLocInfo; 838 839 BstrFmt bstrDesc = BstrFmt(tr("Read appliance '%s'"), 840 aLocInfo.strPath.c_str()); 841 HRESULT rc; 842 /* Create the progress object */ 843 aProgress.createObject(); 844 if (task->locInfo.storageType == VFSType_File) 845 { 846 /* 1 operation only */ 847 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 848 bstrDesc, 849 TRUE /* aCancelable */); 850 } 851 else 852 { 853 /* 4/5 is downloading, 1/5 is reading */ 854 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 855 bstrDesc, 856 TRUE /* aCancelable */, 857 2, // ULONG cOperations, 858 5, // ULONG ulTotalOperationsWeight, 859 BstrFmt(tr("Download appliance '%s'"), 860 aLocInfo.strPath.c_str()), // CBSTR bstrFirstOperationDescription, 861 4); // ULONG ulFirstOperationWeight, 862 } 863 if (FAILED(rc)) throw rc; 864 865 task->progress = aProgress; 866 867 rc = task->startThread(); 868 if (FAILED(rc)) throw rc; 869 870 /* Don't destruct on success */ 871 task.release(); 872 873 return rc; 874 } 875 876 /** 877 * Implementation of the import code. This gets called from the public Appliance::ImportMachines() 878 * method as well as Appliance::importS3(). 606 * @param aFormat 879 607 * @param aLocInfo 880 608 * @param aProgress 881 609 * @return 882 610 */ 883 HRESULT Appliance::importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)884 {885 /* Initialize our worker task */886 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this));887 /* What should the task do */888 task->taskType = TaskImportOVF::Import;889 /* Copy the current location info to the task */890 task->locInfo = aLocInfo;891 892 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),893 aLocInfo.strPath.c_str());894 895 HRESULT rc = S_OK;896 897 /* todo: This progress init stuff should be done a little bit more generic */898 if (task->locInfo.storageType == VFSType_File)899 rc = setUpProgressFS(aProgress, progressDesc);900 else901 rc = setUpProgressImportS3(aProgress, progressDesc);902 if (FAILED(rc)) throw rc;903 904 task->progress = aProgress;905 906 rc = task->startThread();907 if (FAILED(rc)) throw rc;908 909 /* Don't destruct on success */910 task.release();911 912 return rc;913 }914 915 /**916 * Worker thread implementation for Read() (ovf reader).917 * @param aThread918 * @param pvUser919 */920 /* static */921 DECLCALLBACK(int) Appliance::taskThreadImportOVF(RTTHREAD /* aThread */, void *pvUser)922 {923 std::auto_ptr<TaskImportOVF> task(static_cast<TaskImportOVF*>(pvUser));924 AssertReturn(task.get(), VERR_GENERAL_FAILURE);925 926 Appliance *pAppliance = task->pAppliance;927 928 LogFlowFuncEnter();929 LogFlowFunc(("Appliance %p\n", pAppliance));930 931 HRESULT rc = S_OK;932 933 switch (task->taskType)934 {935 case TaskImportOVF::Read:936 {937 if (task->locInfo.storageType == VFSType_File)938 rc = pAppliance->readFS(task.get());939 else if (task->locInfo.storageType == VFSType_S3)940 rc = pAppliance->readS3(task.get());941 break;942 }943 case TaskImportOVF::Import:944 {945 if (task->locInfo.storageType == VFSType_File)946 rc = pAppliance->importFS(task.get());947 else if (task->locInfo.storageType == VFSType_S3)948 rc = pAppliance->importS3(task.get());949 break;950 }951 }952 953 LogFlowFunc(("rc=%Rhrc\n", rc));954 LogFlowFuncLeave();955 956 return VINF_SUCCESS;957 }958 959 int Appliance::TaskImportOVF::startThread()960 {961 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportOVF, this,962 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,963 "Appliance::Task");964 965 ComAssertMsgRCRet(vrc,966 ("Could not create taskThreadImportOVF (%Rrc)\n", vrc), E_FAIL);967 968 return S_OK;969 }970 971 int Appliance::readFS(TaskImportOVF *pTask)972 {973 LogFlowFuncEnter();974 LogFlowFunc(("Appliance %p\n", this));975 976 AutoCaller autoCaller(this);977 if (FAILED(autoCaller.rc())) return autoCaller.rc();978 979 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);980 981 HRESULT rc = S_OK;982 983 try984 {985 /* Read & parse the XML structure of the OVF file */986 m->pReader = new OVFReader(pTask->locInfo.strPath);987 /* Create the SHA1 sum of the OVF file for later validation */988 char *pszDigest;989 int vrc = RTSha1Digest(pTask->locInfo.strPath.c_str(), &pszDigest);990 if (RT_FAILURE(vrc))991 throw setError(VBOX_E_FILE_ERROR,992 tr("Couldn't calculate SHA1 digest for file '%s' (%Rrc)"),993 RTPathFilename(pTask->locInfo.strPath.c_str()), vrc);994 m->strOVFSHA1Digest = pszDigest;995 RTStrFree(pszDigest);996 }997 catch(xml::Error &x)998 {999 rc = setError(VBOX_E_FILE_ERROR,1000 x.what());1001 }1002 catch(HRESULT aRC)1003 {1004 rc = aRC;1005 }1006 1007 pTask->rc = rc;1008 1009 if (!pTask->progress.isNull())1010 pTask->progress->notifyComplete(rc);1011 1012 LogFlowFunc(("rc=%Rhrc\n", rc));1013 LogFlowFuncLeave();1014 1015 return VINF_SUCCESS;1016 }1017 1018 int Appliance::readS3(TaskImportOVF *pTask)1019 {1020 LogFlowFuncEnter();1021 LogFlowFunc(("Appliance %p\n", this));1022 1023 AutoCaller autoCaller(this);1024 if (FAILED(autoCaller.rc())) return autoCaller.rc();1025 1026 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);1027 1028 HRESULT rc = S_OK;1029 int vrc = VINF_SUCCESS;1030 RTS3 hS3 = NIL_RTS3;1031 char szOSTmpDir[RTPATH_MAX];1032 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));1033 /* The template for the temporary directory created below */1034 char *pszTmpDir;1035 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);1036 list< pair<Utf8Str, ULONG> > filesList;1037 Utf8Str strTmpOvf;1038 1039 try1040 {1041 /* Extract the bucket */1042 Utf8Str tmpPath = pTask->locInfo.strPath;1043 Utf8Str bucket;1044 parseBucket(tmpPath, bucket);1045 1046 /* We need a temporary directory which we can put the OVF file & all1047 * disk images in */1048 vrc = RTDirCreateTemp(pszTmpDir);1049 if (RT_FAILURE(vrc))1050 throw setError(VBOX_E_FILE_ERROR,1051 tr("Cannot create temporary directory '%s'"), pszTmpDir);1052 1053 /* The temporary name of the target OVF file */1054 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));1055 1056 /* Next we have to download the OVF */1057 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);1058 if(RT_FAILURE(vrc))1059 throw setError(VBOX_E_IPRT_ERROR,1060 tr("Cannot create S3 service handler"));1061 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);1062 1063 /* Get it */1064 char *pszFilename = RTPathFilename(strTmpOvf.c_str());1065 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strTmpOvf.c_str());1066 if (RT_FAILURE(vrc))1067 {1068 if(vrc == VERR_S3_CANCELED)1069 throw S_OK; /* todo: !!!!!!!!!!!!! */1070 else if(vrc == VERR_S3_ACCESS_DENIED)1071 throw setError(E_ACCESSDENIED,1072 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);1073 else if(vrc == VERR_S3_NOT_FOUND)1074 throw setError(VBOX_E_FILE_ERROR,1075 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);1076 else1077 throw setError(VBOX_E_IPRT_ERROR,1078 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);1079 }1080 1081 /* Close the connection early */1082 RTS3Destroy(hS3);1083 hS3 = NIL_RTS3;1084 1085 if (!pTask->progress.isNull())1086 pTask->progress->SetNextOperation(Bstr(tr("Reading")), 1);1087 1088 /* Prepare the temporary reading of the OVF */1089 ComObjPtr<Progress> progress;1090 LocationInfo li;1091 li.strPath = strTmpOvf;1092 /* Start the reading from the fs */1093 rc = readImpl(li, progress);1094 if (FAILED(rc)) throw rc;1095 1096 /* Unlock the appliance for the reading thread */1097 appLock.release();1098 /* Wait until the reading is done, but report the progress back to the1099 caller */1100 ComPtr<IProgress> progressInt(progress);1101 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */1102 1103 /* Again lock the appliance for the next steps */1104 appLock.acquire();1105 }1106 catch(HRESULT aRC)1107 {1108 rc = aRC;1109 }1110 /* Cleanup */1111 RTS3Destroy(hS3);1112 /* Delete all files which where temporary created */1113 if (RTPathExists(strTmpOvf.c_str()))1114 {1115 vrc = RTFileDelete(strTmpOvf.c_str());1116 if(RT_FAILURE(vrc))1117 rc = setError(VBOX_E_FILE_ERROR,1118 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);1119 }1120 /* Delete the temporary directory */1121 if (RTPathExists(pszTmpDir))1122 {1123 vrc = RTDirRemove(pszTmpDir);1124 if(RT_FAILURE(vrc))1125 rc = setError(VBOX_E_FILE_ERROR,1126 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);1127 }1128 if (pszTmpDir)1129 RTStrFree(pszTmpDir);1130 1131 pTask->rc = rc;1132 1133 if (!pTask->progress.isNull())1134 pTask->progress->notifyComplete(rc);1135 1136 LogFlowFunc(("rc=%Rhrc\n", rc));1137 LogFlowFuncLeave();1138 1139 return VINF_SUCCESS;1140 }1141 1142 int Appliance::importFS(TaskImportOVF *pTask)1143 {1144 LogFlowFuncEnter();1145 LogFlowFunc(("Appliance %p\n", this));1146 1147 AutoCaller autoCaller(this);1148 if (FAILED(autoCaller.rc())) return autoCaller.rc();1149 1150 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);1151 1152 if (!isApplianceIdle())1153 return VERR_ACCESS_DENIED;1154 1155 // Change the appliance state so we can safely leave the lock while doing time-consuming1156 // disk imports; also the below method calls do all kinds of locking which conflicts with1157 // the appliance object lock1158 m->state = Data::ApplianceImporting;1159 appLock.release();1160 1161 HRESULT rc = S_OK;1162 1163 // rollback for errors:1164 // a list of images that we created/imported1165 list<MyHardDiskAttachment> llHardDiskAttachments;1166 list< ComPtr<IMedium> > llHardDisksCreated;1167 list<Bstr> llMachinesRegistered; // list of string UUIDs1168 1169 ComPtr<ISession> session;1170 bool fSessionOpen = false;1171 rc = session.createInprocObject(CLSID_Session);1172 if (FAILED(rc)) return rc;1173 1174 const OVFReader &reader = *m->pReader;1175 // this is safe to access because this thread only gets started1176 // if pReader != NULL1177 1178 /* If an manifest file exists, verify the content. Therefore we need all1179 * files which are referenced by the OVF & the OVF itself */1180 Utf8Str strMfFile = manifestFileName(pTask->locInfo.strPath);1181 list<Utf8Str> filesList;1182 if (RTPathExists(strMfFile.c_str()))1183 {1184 Utf8Str strSrcDir(pTask->locInfo.strPath);1185 strSrcDir.stripFilename();1186 /* Add every disks of every virtual system to an internal list */1187 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;1188 for (it = m->virtualSystemDescriptions.begin();1189 it != m->virtualSystemDescriptions.end();1190 ++it)1191 {1192 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);1193 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);1194 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;1195 for (itH = avsdeHDs.begin();1196 itH != avsdeHDs.end();1197 ++itH)1198 {1199 VirtualSystemDescriptionEntry *vsdeHD = *itH;1200 /* Find the disk from the OVF's disk list */1201 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);1202 const DiskImage &di = itDiskImage->second;1203 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());1204 filesList.push_back(strSrcFilePath);1205 }1206 }1207 /* Create the test list */1208 PRTMANIFESTTEST pTestList = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST)*(filesList.size()+1));1209 pTestList[0].pszTestFile = (char*)pTask->locInfo.strPath.c_str();1210 pTestList[0].pszTestDigest = (char*)m->strOVFSHA1Digest.c_str();1211 int vrc = VINF_SUCCESS;1212 size_t i = 1;1213 list<Utf8Str>::const_iterator it1;1214 for (it1 = filesList.begin();1215 it1 != filesList.end();1216 ++it1, ++i)1217 {1218 char* pszDigest;1219 vrc = RTSha1Digest((*it1).c_str(), &pszDigest);1220 pTestList[i].pszTestFile = (char*)(*it1).c_str();1221 pTestList[i].pszTestDigest = pszDigest;1222 }1223 size_t cIndexOnError;1224 vrc = RTManifestVerify(strMfFile.c_str(), pTestList, filesList.size() + 1, &cIndexOnError);1225 if (vrc == VERR_MANIFEST_DIGEST_MISMATCH)1226 rc = setError(VBOX_E_FILE_ERROR,1227 tr("The SHA1 digest of '%s' doesn't match to the one in '%s'"),1228 RTPathFilename(pTestList[cIndexOnError].pszTestFile),1229 RTPathFilename(strMfFile.c_str()));1230 else if (RT_FAILURE(vrc))1231 rc = setError(VBOX_E_FILE_ERROR,1232 tr("Couldn't verify the content of '%s' against the available files (%Rrc)"),1233 RTPathFilename(strMfFile.c_str()),1234 vrc);1235 /* Cleanup */1236 for (size_t j = 1;1237 j < filesList.size();1238 ++j)1239 RTStrFree(pTestList[j].pszTestDigest);1240 RTMemFree(pTestList);1241 if (FAILED(rc))1242 {1243 /* Return on error */1244 pTask->rc = rc;1245 1246 if (!pTask->progress.isNull())1247 pTask->progress->notifyComplete(rc);1248 return rc;1249 }1250 }1251 1252 list<VirtualSystem>::const_iterator it;1253 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;1254 /* Iterate through all virtual systems of that appliance */1255 size_t i = 0;1256 for (it = reader.m_llVirtualSystems.begin(),1257 it1 = m->virtualSystemDescriptions.begin();1258 it != reader.m_llVirtualSystems.end();1259 ++it, ++it1, ++i)1260 {1261 const VirtualSystem &vsysThis = *it;1262 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);1263 1264 ComPtr<IMachine> pNewMachine;1265 1266 /* Catch possible errors */1267 try1268 {1269 /* Guest OS type */1270 std::list<VirtualSystemDescriptionEntry*> vsdeOS;1271 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);1272 if (vsdeOS.size() < 1)1273 throw setError(VBOX_E_FILE_ERROR,1274 tr("Missing guest OS type"));1275 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;1276 1277 /* Now that we know the base system get our internal defaults based on that. */1278 ComPtr<IGuestOSType> osType;1279 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());1280 if (FAILED(rc)) throw rc;1281 1282 /* Create the machine */1283 /* First get the name */1284 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);1285 if (vsdeName.size() < 1)1286 throw setError(VBOX_E_FILE_ERROR,1287 tr("Missing VM name"));1288 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;1289 rc = mVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),1290 Bstr(), Bstr(), FALSE,1291 pNewMachine.asOutParam());1292 if (FAILED(rc)) throw rc;1293 1294 // and the description1295 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);1296 if (vsdeDescription.size())1297 {1298 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;1299 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));1300 if (FAILED(rc)) throw rc;1301 }1302 1303 /* CPU count */1304 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU);1305 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL);1306 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox;1307 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str());1308 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount);1309 if (FAILED(rc)) throw rc;1310 bool fEnableIOApic = false;1311 /* We need HWVirt & IO-APIC if more than one CPU is requested */1312 if (tmpCount > 1)1313 {1314 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE);1315 if (FAILED(rc)) throw rc;1316 1317 fEnableIOApic = true;1318 }1319 1320 /* RAM */1321 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);1322 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);1323 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;1324 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());1325 rc = pNewMachine->COMSETTER(MemorySize)(tt);1326 if (FAILED(rc)) throw rc;1327 1328 /* VRAM */1329 /* Get the recommended VRAM for this guest OS type */1330 ULONG vramVBox;1331 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);1332 if (FAILED(rc)) throw rc;1333 1334 /* Set the VRAM */1335 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);1336 if (FAILED(rc)) throw rc;1337 1338 /* I/O APIC: so far we have no setting for this. Enable it if we1339 import a Windows VM because if if Windows was installed without IOAPIC,1340 it will not mind finding an one later on, but if Windows was installed1341 _with_ an IOAPIC, it will bluescreen if it's not found */1342 Bstr bstrFamilyId;1343 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());1344 if (FAILED(rc)) throw rc;1345 1346 Utf8Str strFamilyId(bstrFamilyId);1347 if (strFamilyId == "Windows")1348 fEnableIOApic = true;1349 1350 /* If IP-APIC should be enabled could be have different reasons.1351 See CPU count & the Win test above. Here we enable it if it was1352 previously requested. */1353 if (fEnableIOApic)1354 {1355 ComPtr<IBIOSSettings> pBIOSSettings;1356 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());1357 if (FAILED(rc)) throw rc;1358 1359 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);1360 if (FAILED(rc)) throw rc;1361 }1362 1363 /* Audio Adapter */1364 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);1365 /* @todo: we support one audio adapter only */1366 if (vsdeAudioAdapter.size() > 0)1367 {1368 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;1369 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)1370 {1371 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());1372 ComPtr<IAudioAdapter> audioAdapter;1373 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());1374 if (FAILED(rc)) throw rc;1375 rc = audioAdapter->COMSETTER(Enabled)(true);1376 if (FAILED(rc)) throw rc;1377 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));1378 if (FAILED(rc)) throw rc;1379 }1380 }1381 1382 #ifdef VBOX_WITH_USB1383 /* USB Controller */1384 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);1385 // USB support is enabled if there's at least one such entry; to disable USB support,1386 // the type of the USB item would have been changed to "ignore"1387 bool fUSBEnabled = vsdeUSBController.size() > 0;1388 1389 ComPtr<IUSBController> usbController;1390 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());1391 if (FAILED(rc)) throw rc;1392 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);1393 if (FAILED(rc)) throw rc;1394 #endif /* VBOX_WITH_USB */1395 1396 /* Change the network adapters */1397 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);1398 if (vsdeNW.size() == 0)1399 {1400 /* No network adapters, so we have to disable our default one */1401 ComPtr<INetworkAdapter> nwVBox;1402 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());1403 if (FAILED(rc)) throw rc;1404 rc = nwVBox->COMSETTER(Enabled)(false);1405 if (FAILED(rc)) throw rc;1406 }1407 else1408 {1409 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;1410 /* Iterate through all network cards. We support 8 network adapters1411 * at the maximum. (@todo: warn if there are more!) */1412 size_t a = 0;1413 for (nwIt = vsdeNW.begin();1414 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);1415 ++nwIt, ++a)1416 {1417 const VirtualSystemDescriptionEntry* pvsys = *nwIt;1418 1419 const Utf8Str &nwTypeVBox = pvsys->strVbox;1420 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());1421 ComPtr<INetworkAdapter> pNetworkAdapter;1422 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());1423 if (FAILED(rc)) throw rc;1424 /* Enable the network card & set the adapter type */1425 rc = pNetworkAdapter->COMSETTER(Enabled)(true);1426 if (FAILED(rc)) throw rc;1427 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));1428 if (FAILED(rc)) throw rc;1429 1430 // default is NAT; change to "bridged" if extra conf says so1431 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))1432 {1433 /* Attach to the right interface */1434 rc = pNetworkAdapter->AttachToBridgedInterface();1435 if (FAILED(rc)) throw rc;1436 ComPtr<IHost> host;1437 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());1438 if (FAILED(rc)) throw rc;1439 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;1440 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));1441 if (FAILED(rc)) throw rc;1442 /* We search for the first host network interface which1443 * is usable for bridged networking */1444 for (size_t j = 0;1445 j < nwInterfaces.size();1446 ++j)1447 {1448 HostNetworkInterfaceType_T itype;1449 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);1450 if (FAILED(rc)) throw rc;1451 if (itype == HostNetworkInterfaceType_Bridged)1452 {1453 Bstr name;1454 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());1455 if (FAILED(rc)) throw rc;1456 /* Set the interface name to attach to */1457 pNetworkAdapter->COMSETTER(HostInterface)(name);1458 if (FAILED(rc)) throw rc;1459 break;1460 }1461 }1462 }1463 /* Next test for host only interfaces */1464 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))1465 {1466 /* Attach to the right interface */1467 rc = pNetworkAdapter->AttachToHostOnlyInterface();1468 if (FAILED(rc)) throw rc;1469 ComPtr<IHost> host;1470 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());1471 if (FAILED(rc)) throw rc;1472 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;1473 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));1474 if (FAILED(rc)) throw rc;1475 /* We search for the first host network interface which1476 * is usable for host only networking */1477 for (size_t j = 0;1478 j < nwInterfaces.size();1479 ++j)1480 {1481 HostNetworkInterfaceType_T itype;1482 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);1483 if (FAILED(rc)) throw rc;1484 if (itype == HostNetworkInterfaceType_HostOnly)1485 {1486 Bstr name;1487 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());1488 if (FAILED(rc)) throw rc;1489 /* Set the interface name to attach to */1490 pNetworkAdapter->COMSETTER(HostInterface)(name);1491 if (FAILED(rc)) throw rc;1492 break;1493 }1494 }1495 }1496 }1497 }1498 1499 /* Hard disk controller IDE */1500 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);1501 if (vsdeHDCIDE.size() > 1)1502 throw setError(VBOX_E_FILE_ERROR,1503 tr("Too many IDE controllers in OVF; import facility only supports one"));1504 if (vsdeHDCIDE.size() == 1)1505 {1506 ComPtr<IStorageController> pController;1507 rc = pNewMachine->AddStorageController(Bstr("IDE Controller"), StorageBus_IDE, pController.asOutParam());1508 if (FAILED(rc)) throw rc;1509 1510 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();1511 if (!strcmp(pcszIDEType, "PIIX3"))1512 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);1513 else if (!strcmp(pcszIDEType, "PIIX4"))1514 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);1515 else if (!strcmp(pcszIDEType, "ICH6"))1516 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);1517 else1518 throw setError(VBOX_E_FILE_ERROR,1519 tr("Invalid IDE controller type \"%s\""),1520 pcszIDEType);1521 if (FAILED(rc)) throw rc;1522 }1523 #ifdef VBOX_WITH_AHCI1524 /* Hard disk controller SATA */1525 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);1526 if (vsdeHDCSATA.size() > 1)1527 throw setError(VBOX_E_FILE_ERROR,1528 tr("Too many SATA controllers in OVF; import facility only supports one"));1529 if (vsdeHDCSATA.size() > 0)1530 {1531 ComPtr<IStorageController> pController;1532 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;1533 if (hdcVBox == "AHCI")1534 {1535 rc = pNewMachine->AddStorageController(Bstr("SATA Controller"), StorageBus_SATA, pController.asOutParam());1536 if (FAILED(rc)) throw rc;1537 }1538 else1539 throw setError(VBOX_E_FILE_ERROR,1540 tr("Invalid SATA controller type \"%s\""),1541 hdcVBox.c_str());1542 }1543 #endif /* VBOX_WITH_AHCI */1544 1545 #ifdef VBOX_WITH_LSILOGIC1546 /* Hard disk controller SCSI */1547 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);1548 if (vsdeHDCSCSI.size() > 1)1549 throw setError(VBOX_E_FILE_ERROR,1550 tr("Too many SCSI controllers in OVF; import facility only supports one"));1551 if (vsdeHDCSCSI.size() > 0)1552 {1553 ComPtr<IStorageController> pController;1554 StorageControllerType_T controllerType;1555 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;1556 if (hdcVBox == "LsiLogic")1557 controllerType = StorageControllerType_LsiLogic;1558 else if (hdcVBox == "BusLogic")1559 controllerType = StorageControllerType_BusLogic;1560 else1561 throw setError(VBOX_E_FILE_ERROR,1562 tr("Invalid SCSI controller type \"%s\""),1563 hdcVBox.c_str());1564 1565 rc = pNewMachine->AddStorageController(Bstr("SCSI Controller"), StorageBus_SCSI, pController.asOutParam());1566 if (FAILED(rc)) throw rc;1567 rc = pController->COMSETTER(ControllerType)(controllerType);1568 if (FAILED(rc)) throw rc;1569 }1570 #endif /* VBOX_WITH_LSILOGIC */1571 1572 /* Now its time to register the machine before we add any hard disks */1573 rc = mVirtualBox->RegisterMachine(pNewMachine);1574 if (FAILED(rc)) throw rc;1575 1576 Bstr bstrNewMachineId;1577 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());1578 if (FAILED(rc)) throw rc;1579 1580 // store new machine for roll-back in case of errors1581 llMachinesRegistered.push_back(bstrNewMachineId);1582 1583 // Add floppies and CD-ROMs to the appropriate controllers.1584 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);1585 if (vsdeFloppy.size() > 1)1586 throw setError(VBOX_E_FILE_ERROR,1587 tr("Too many floppy controllers in OVF; import facility only supports one"));1588 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM);1589 if ( (vsdeFloppy.size() > 0)1590 || (vsdeCDROM.size() > 0)1591 )1592 {1593 // If there's an error here we need to close the session, so1594 // we need another try/catch block.1595 1596 try1597 {1598 /* In order to attach things we need to open a session1599 * for the new machine */1600 rc = mVirtualBox->OpenSession(session, bstrNewMachineId);1601 if (FAILED(rc)) throw rc;1602 fSessionOpen = true;1603 1604 ComPtr<IMachine> sMachine;1605 rc = session->COMGETTER(Machine)(sMachine.asOutParam());1606 if (FAILED(rc)) throw rc;1607 1608 // floppy first1609 if (vsdeFloppy.size() == 1)1610 {1611 ComPtr<IStorageController> pController;1612 rc = sMachine->AddStorageController(Bstr("Floppy Controller"), StorageBus_Floppy, pController.asOutParam());1613 if (FAILED(rc)) throw rc;1614 1615 Bstr bstrName;1616 rc = pController->COMGETTER(Name)(bstrName.asOutParam());1617 if (FAILED(rc)) throw rc;1618 1619 // this is for rollback later1620 MyHardDiskAttachment mhda;1621 mhda.bstrUuid = bstrNewMachineId;1622 mhda.pMachine = pNewMachine;1623 mhda.controllerType = bstrName;1624 mhda.lChannel = 0;1625 mhda.lDevice = 0;1626 1627 Log(("Attaching floppy\n"));1628 1629 rc = sMachine->AttachDevice(mhda.controllerType,1630 mhda.lChannel,1631 mhda.lDevice,1632 DeviceType_Floppy,1633 NULL);1634 if (FAILED(rc)) throw rc;1635 1636 llHardDiskAttachments.push_back(mhda);1637 }1638 1639 1640 // CD-ROMs next1641 for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin();1642 jt != vsdeCDROM.end();1643 ++jt)1644 {1645 // for now always attach to secondary master on IDE controller;1646 // there seems to be no useful information in OVF where else to1647 // attach jt (@todo test with latest versions of OVF software)1648 1649 // find the IDE controller1650 const HardDiskController *pController = NULL;1651 for (ControllersMap::const_iterator kt = vsysThis.mapControllers.begin();1652 kt != vsysThis.mapControllers.end();1653 ++kt)1654 {1655 if (kt->second.system == HardDiskController::IDE)1656 {1657 pController = &kt->second;1658 }1659 }1660 1661 if (!pController)1662 throw setError(VBOX_E_FILE_ERROR,1663 tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox"));1664 1665 // this is for rollback later1666 MyHardDiskAttachment mhda;1667 mhda.bstrUuid = bstrNewMachineId;1668 mhda.pMachine = pNewMachine;1669 1670 ConvertDiskAttachmentValues(*pController,1671 2, // interpreted as secondary master1672 mhda.controllerType, // Bstr1673 mhda.lChannel,1674 mhda.lDevice);1675 1676 Log(("Attaching CD-ROM to channel %d on device %d\n", mhda.lChannel, mhda.lDevice));1677 1678 rc = sMachine->AttachDevice(mhda.controllerType,1679 mhda.lChannel,1680 mhda.lDevice,1681 DeviceType_DVD,1682 NULL);1683 if (FAILED(rc)) throw rc;1684 1685 llHardDiskAttachments.push_back(mhda);1686 } // end for (itHD = avsdeHDs.begin();1687 1688 rc = sMachine->SaveSettings();1689 if (FAILED(rc)) throw rc;1690 1691 // only now that we're done with all disks, close the session1692 rc = session->Close();1693 if (FAILED(rc)) throw rc;1694 fSessionOpen = false;1695 }1696 catch(HRESULT /* aRC */)1697 {1698 if (fSessionOpen)1699 session->Close();1700 1701 throw;1702 }1703 }1704 1705 /* Create the hard disks & connect them to the appropriate controllers. */1706 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);1707 if (avsdeHDs.size() > 0)1708 {1709 // If there's an error here we need to close the session, so1710 // we need another try/catch block.1711 ComPtr<IMedium> srcHdVBox;1712 bool fSourceHdNeedsClosing = false;1713 1714 try1715 {1716 /* In order to attach hard disks we need to open a session1717 * for the new machine */1718 rc = mVirtualBox->OpenSession(session, bstrNewMachineId);1719 if (FAILED(rc)) throw rc;1720 fSessionOpen = true;1721 1722 /* The disk image has to be on the same place as the OVF file. So1723 * strip the filename out of the full file path. */1724 Utf8Str strSrcDir(pTask->locInfo.strPath);1725 strSrcDir.stripFilename();1726 1727 /* Iterate over all given disk images */1728 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;1729 for (itHD = avsdeHDs.begin();1730 itHD != avsdeHDs.end();1731 ++itHD)1732 {1733 VirtualSystemDescriptionEntry *vsdeHD = *itHD;1734 1735 /* Check if the destination file exists already or the1736 * destination path is empty. */1737 if ( vsdeHD->strVbox.isEmpty()1738 || RTPathExists(vsdeHD->strVbox.c_str())1739 )1740 /* This isn't allowed */1741 throw setError(VBOX_E_FILE_ERROR,1742 tr("Destination file '%s' exists",1743 vsdeHD->strVbox.c_str()));1744 1745 /* Find the disk from the OVF's disk list */1746 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);1747 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist1748 in the virtual system's disks map under that ID and also in the global images map. */1749 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);1750 1751 if ( itDiskImage == reader.m_mapDisks.end()1752 || itVirtualDisk == vsysThis.mapVirtualDisks.end()1753 )1754 throw setError(E_FAIL,1755 tr("Internal inconsistency looking up disk images."));1756 1757 const DiskImage &di = itDiskImage->second;1758 const VirtualDisk &vd = itVirtualDisk->second;1759 1760 /* Make sure all target directories exists */1761 rc = VirtualBox::ensureFilePathExists(vsdeHD->strVbox.c_str());1762 if (FAILED(rc))1763 throw rc;1764 1765 // subprogress object for hard disk1766 ComPtr<IProgress> pProgress2;1767 1768 ComPtr<IMedium> dstHdVBox;1769 /* If strHref is empty we have to create a new file */1770 if (di.strHref.isEmpty())1771 {1772 /* Which format to use? */1773 Bstr srcFormat = L"VDI";1774 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)1775 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))1776 srcFormat = L"VMDK";1777 /* Create an empty hard disk */1778 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam());1779 if (FAILED(rc)) throw rc;1780 1781 /* Create a dynamic growing disk image with the given capacity */1782 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam());1783 if (FAILED(rc)) throw rc;1784 1785 /* Advance to the next operation */1786 if (!pTask->progress.isNull())1787 pTask->progress->SetNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), vsdeHD->strVbox.c_str()),1788 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally1789 }1790 else1791 {1792 /* Construct the source file path */1793 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());1794 /* Check if the source file exists */1795 if (!RTPathExists(strSrcFilePath.c_str()))1796 /* This isn't allowed */1797 throw setError(VBOX_E_FILE_ERROR,1798 tr("Source virtual disk image file '%s' doesn't exist"),1799 strSrcFilePath.c_str());1800 1801 /* Clone the disk image (this is necessary cause the id has1802 * to be recreated for the case the same hard disk is1803 * attached already from a previous import) */1804 1805 /* First open the existing disk image */1806 rc = mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),1807 AccessMode_ReadOnly,1808 false,1809 NULL,1810 false,1811 NULL,1812 srcHdVBox.asOutParam());1813 if (FAILED(rc)) throw rc;1814 fSourceHdNeedsClosing = true;1815 1816 /* We need the format description of the source disk image */1817 Bstr srcFormat;1818 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());1819 if (FAILED(rc)) throw rc;1820 /* Create a new hard disk interface for the destination disk image */1821 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam());1822 if (FAILED(rc)) throw rc;1823 /* Clone the source disk image */1824 rc = srcHdVBox->CloneTo(dstHdVBox, MediumVariant_Standard, NULL, pProgress2.asOutParam());1825 if (FAILED(rc)) throw rc;1826 1827 /* Advance to the next operation */1828 if (!pTask->progress.isNull())1829 pTask->progress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),1830 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally);1831 }1832 1833 // now wait for the background disk operation to complete; this throws HRESULTs on error1834 waitForAsyncProgress(pTask->progress, pProgress2);1835 1836 if (fSourceHdNeedsClosing)1837 {1838 rc = srcHdVBox->Close();1839 if (FAILED(rc)) throw rc;1840 fSourceHdNeedsClosing = false;1841 }1842 1843 llHardDisksCreated.push_back(dstHdVBox);1844 /* Now use the new uuid to attach the disk image to our new machine */1845 ComPtr<IMachine> sMachine;1846 rc = session->COMGETTER(Machine)(sMachine.asOutParam());1847 if (FAILED(rc)) throw rc;1848 Bstr hdId;1849 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());1850 if (FAILED(rc)) throw rc;1851 1852 /* For now we assume we have one controller of every type only */1853 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;1854 1855 // this is for rollback later1856 MyHardDiskAttachment mhda;1857 mhda.bstrUuid = bstrNewMachineId;1858 mhda.pMachine = pNewMachine;1859 1860 ConvertDiskAttachmentValues(hdc,1861 vd.ulAddressOnParent,1862 mhda.controllerType, // Bstr1863 mhda.lChannel,1864 mhda.lDevice);1865 1866 Log(("Attaching disk %s to channel %d on device %d\n", vsdeHD->strVbox.c_str(), mhda.lChannel, mhda.lDevice));1867 1868 rc = sMachine->AttachDevice(mhda.controllerType,1869 mhda.lChannel,1870 mhda.lDevice,1871 DeviceType_HardDisk,1872 hdId);1873 if (FAILED(rc)) throw rc;1874 1875 llHardDiskAttachments.push_back(mhda);1876 1877 rc = sMachine->SaveSettings();1878 if (FAILED(rc)) throw rc;1879 } // end for (itHD = avsdeHDs.begin();1880 1881 // only now that we're done with all disks, close the session1882 rc = session->Close();1883 if (FAILED(rc)) throw rc;1884 fSessionOpen = false;1885 }1886 catch(HRESULT /* aRC */)1887 {1888 if (fSourceHdNeedsClosing)1889 srcHdVBox->Close();1890 1891 if (fSessionOpen)1892 session->Close();1893 1894 throw;1895 }1896 }1897 }1898 catch(HRESULT aRC)1899 {1900 rc = aRC;1901 }1902 1903 if (FAILED(rc))1904 break;1905 1906 } // for (it = pAppliance->m->llVirtualSystems.begin(),1907 1908 if (FAILED(rc))1909 {1910 // with _whatever_ error we've had, do a complete roll-back of1911 // machines and disks we've created; unfortunately this is1912 // not so trivially done...1913 1914 HRESULT rc2;1915 // detach all hard disks from all machines we created1916 list<MyHardDiskAttachment>::iterator itM;1917 for (itM = llHardDiskAttachments.begin();1918 itM != llHardDiskAttachments.end();1919 ++itM)1920 {1921 const MyHardDiskAttachment &mhda = *itM;1922 Bstr bstrUuid(mhda.bstrUuid); // make a copy, Windows can't handle const Bstr1923 rc2 = mVirtualBox->OpenSession(session, bstrUuid);1924 if (SUCCEEDED(rc2))1925 {1926 ComPtr<IMachine> sMachine;1927 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());1928 if (SUCCEEDED(rc2))1929 {1930 rc2 = sMachine->DetachDevice(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);1931 rc2 = sMachine->SaveSettings();1932 }1933 session->Close();1934 }1935 }1936 1937 // now clean up all hard disks we created1938 list< ComPtr<IMedium> >::iterator itHD;1939 for (itHD = llHardDisksCreated.begin();1940 itHD != llHardDisksCreated.end();1941 ++itHD)1942 {1943 ComPtr<IMedium> pDisk = *itHD;1944 ComPtr<IProgress> pProgress;1945 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());1946 rc2 = pProgress->WaitForCompletion(-1);1947 }1948 1949 // finally, deregister and remove all machines1950 list<Bstr>::iterator itID;1951 for (itID = llMachinesRegistered.begin();1952 itID != llMachinesRegistered.end();1953 ++itID)1954 {1955 Bstr bstrGuid = *itID; // make a copy, Windows can't handle const Bstr1956 ComPtr<IMachine> failedMachine;1957 rc2 = mVirtualBox->UnregisterMachine(bstrGuid, failedMachine.asOutParam());1958 if (SUCCEEDED(rc2))1959 rc2 = failedMachine->DeleteSettings();1960 }1961 }1962 1963 // restore the appliance state1964 appLock.acquire();1965 m->state = Data::ApplianceIdle;1966 1967 pTask->rc = rc;1968 1969 if (!pTask->progress.isNull())1970 pTask->progress->notifyComplete(rc);1971 1972 LogFlowFunc(("rc=%Rhrc\n", rc));1973 LogFlowFuncLeave();1974 1975 return VINF_SUCCESS;1976 }1977 1978 /**1979 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values.1980 * Throws HRESULT values on errors!1981 *1982 * @param hdc1983 * @param vd1984 * @param mhda1985 */1986 void Appliance::ConvertDiskAttachmentValues(const HardDiskController &hdc,1987 uint32_t ulAddressOnParent,1988 Bstr &controllerType,1989 int32_t &lChannel,1990 int32_t &lDevice)1991 {1992 switch (hdc.system)1993 {1994 case HardDiskController::IDE:1995 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary1996 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,1997 // the device number can be either 0 or 1, to specify the master or the slave device,1998 // respectively. For the secondary IDE controller, the device number is always 1 because1999 // the master device is reserved for the CD-ROM drive.2000 controllerType = Bstr("IDE Controller");2001 switch (ulAddressOnParent)2002 {2003 case 0: // interpret this as primary master2004 lChannel = (long)0;2005 lDevice = (long)0;2006 break;2007 2008 case 1: // interpret this as primary slave2009 lChannel = (long)0;2010 lDevice = (long)1;2011 break;2012 2013 case 2: // interpret this as secondary master2014 lChannel = (long)1;2015 lDevice = (long)0;2016 break;2017 2018 case 3: // interpret this as secondary slave2019 lChannel = (long)1;2020 lDevice = (long)1;2021 break;2022 2023 default:2024 throw setError(VBOX_E_NOT_SUPPORTED,2025 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), ulAddressOnParent);2026 break;2027 }2028 break;2029 2030 case HardDiskController::SATA:2031 controllerType = Bstr("SATA Controller");2032 lChannel = (long)ulAddressOnParent;2033 lDevice = (long)0;2034 break;2035 2036 case HardDiskController::SCSI:2037 controllerType = Bstr("SCSI Controller");2038 lChannel = (long)ulAddressOnParent;2039 lDevice = (long)0;2040 break;2041 2042 default: break;2043 }2044 }2045 2046 int Appliance::importS3(TaskImportOVF *pTask)2047 {2048 LogFlowFuncEnter();2049 LogFlowFunc(("Appliance %p\n", this));2050 2051 AutoCaller autoCaller(this);2052 if (FAILED(autoCaller.rc())) return autoCaller.rc();2053 2054 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);2055 2056 int vrc = VINF_SUCCESS;2057 RTS3 hS3 = NIL_RTS3;2058 char szOSTmpDir[RTPATH_MAX];2059 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));2060 /* The template for the temporary directory created below */2061 char *pszTmpDir;2062 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);2063 list< pair<Utf8Str, ULONG> > filesList;2064 2065 HRESULT rc = S_OK;2066 try2067 {2068 /* Extract the bucket */2069 Utf8Str tmpPath = pTask->locInfo.strPath;2070 Utf8Str bucket;2071 parseBucket(tmpPath, bucket);2072 2073 /* We need a temporary directory which we can put the all disk images2074 * in */2075 vrc = RTDirCreateTemp(pszTmpDir);2076 if (RT_FAILURE(vrc))2077 throw setError(VBOX_E_FILE_ERROR,2078 tr("Cannot create temporary directory '%s'"), pszTmpDir);2079 2080 /* Add every disks of every virtual system to an internal list */2081 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;2082 for (it = m->virtualSystemDescriptions.begin();2083 it != m->virtualSystemDescriptions.end();2084 ++it)2085 {2086 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);2087 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);2088 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;2089 for (itH = avsdeHDs.begin();2090 itH != avsdeHDs.end();2091 ++itH)2092 {2093 const Utf8Str &strTargetFile = (*itH)->strOvf;2094 if (!strTargetFile.isEmpty())2095 {2096 /* The temporary name of the target disk file */2097 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str()));2098 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB));2099 }2100 }2101 }2102 2103 /* Next we have to download the disk images */2104 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);2105 if(RT_FAILURE(vrc))2106 throw setError(VBOX_E_IPRT_ERROR,2107 tr("Cannot create S3 service handler"));2108 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);2109 2110 /* Download all files */2111 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)2112 {2113 const pair<Utf8Str, ULONG> &s = (*it1);2114 const Utf8Str &strSrcFile = s.first;2115 /* Construct the source file name */2116 char *pszFilename = RTPathFilename(strSrcFile.c_str());2117 /* Advance to the next operation */2118 if (!pTask->progress.isNull())2119 pTask->progress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), s.second);2120 2121 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str());2122 if (RT_FAILURE(vrc))2123 {2124 if(vrc == VERR_S3_CANCELED)2125 throw S_OK; /* todo: !!!!!!!!!!!!! */2126 else if(vrc == VERR_S3_ACCESS_DENIED)2127 throw setError(E_ACCESSDENIED,2128 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);2129 else if(vrc == VERR_S3_NOT_FOUND)2130 throw setError(VBOX_E_FILE_ERROR,2131 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);2132 else2133 throw setError(VBOX_E_IPRT_ERROR,2134 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);2135 }2136 }2137 2138 /* Provide a OVF file (haven't to exist) so the import routine can2139 * figure out where the disk images/manifest file are located. */2140 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));2141 /* Now check if there is an manifest file. This is optional. */2142 Utf8Str strManifestFile = manifestFileName(strTmpOvf);2143 char *pszFilename = RTPathFilename(strManifestFile.c_str());2144 if (!pTask->progress.isNull())2145 pTask->progress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), 1);2146 2147 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */2148 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str());2149 if (RT_SUCCESS(vrc))2150 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0));2151 else if (RT_FAILURE(vrc))2152 {2153 if(vrc == VERR_S3_CANCELED)2154 throw S_OK; /* todo: !!!!!!!!!!!!! */2155 else if(vrc == VERR_S3_NOT_FOUND)2156 vrc = VINF_SUCCESS; /* Not found is ok */2157 else if(vrc == VERR_S3_ACCESS_DENIED)2158 throw setError(E_ACCESSDENIED,2159 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);2160 else2161 throw setError(VBOX_E_IPRT_ERROR,2162 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);2163 }2164 2165 /* Close the connection early */2166 RTS3Destroy(hS3);2167 hS3 = NIL_RTS3;2168 2169 if (!pTask->progress.isNull())2170 pTask->progress->SetNextOperation(BstrFmt(tr("Importing appliance")), m->ulWeightPerOperation);2171 2172 ComObjPtr<Progress> progress;2173 /* Import the whole temporary OVF & the disk images */2174 LocationInfo li;2175 li.strPath = strTmpOvf;2176 rc = importImpl(li, progress);2177 if (FAILED(rc)) throw rc;2178 2179 /* Unlock the appliance for the fs import thread */2180 appLock.release();2181 /* Wait until the import is done, but report the progress back to the2182 caller */2183 ComPtr<IProgress> progressInt(progress);2184 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */2185 2186 /* Again lock the appliance for the next steps */2187 appLock.acquire();2188 }2189 catch(HRESULT aRC)2190 {2191 rc = aRC;2192 }2193 /* Cleanup */2194 RTS3Destroy(hS3);2195 /* Delete all files which where temporary created */2196 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)2197 {2198 const char *pszFilePath = (*it1).first.c_str();2199 if (RTPathExists(pszFilePath))2200 {2201 vrc = RTFileDelete(pszFilePath);2202 if(RT_FAILURE(vrc))2203 rc = setError(VBOX_E_FILE_ERROR,2204 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);2205 }2206 }2207 /* Delete the temporary directory */2208 if (RTPathExists(pszTmpDir))2209 {2210 vrc = RTDirRemove(pszTmpDir);2211 if(RT_FAILURE(vrc))2212 rc = setError(VBOX_E_FILE_ERROR,2213 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);2214 }2215 if (pszTmpDir)2216 RTStrFree(pszTmpDir);2217 2218 pTask->rc = rc;2219 2220 if (!pTask->progress.isNull())2221 pTask->progress->notifyComplete(rc);2222 2223 LogFlowFunc(("rc=%Rhrc\n", rc));2224 LogFlowFuncLeave();2225 2226 return VINF_SUCCESS;2227 }2228 2229 611 HRESULT Appliance::writeImpl(int aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress) 2230 612 { … … 2266 648 } 2267 649 650 /** 651 * Thread function for the thread started in Appliance::writeImpl(). This will in turn 652 * call Appliance::writeFS() or Appliance::writeS3(). 653 * @param 654 * @param pvUser 655 * @return 656 */ 2268 657 DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser) 2269 658 { … … 2296 685 } 2297 686 2298 int Appliance::TaskExportOVF::startThread() 2299 { 2300 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this, 2301 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, 2302 "Appliance::Task"); 2303 2304 ComAssertMsgRCRet(vrc, 2305 ("Could not create taskThreadWriteOVF (%Rrc)\n", vrc), E_FAIL); 2306 2307 return S_OK; 2308 } 2309 687 /** 688 * Actual worker code for writing out OVF to disk. This is called from Appliance::taskThreadWriteOVF() 689 * and therefore runs on the OVF write worker thread. This runs in two contexts: 690 * 691 * 1) in a first worker thread; in that case, Appliance::Write() called Appliance::writeImpl(); 692 * 693 * 2) in a second worker thread; in that case, Appliance::Write() called Appliance::writeImpl(), which 694 * called Appliance::writeS3(), which called Appliance::writeImpl(), which then called this. In other 695 * words, to write to the cloud, the first worker thread first starts a second worker thread to create 696 * temporary files and then uploads them to the S3 cloud server. 697 * 698 * @param pTask 699 * @return 700 */ 2310 701 int Appliance::writeFS(TaskExportOVF *pTask) 2311 702 { … … 3162 1553 } 3163 1554 1555 /** 1556 * Worker code for writing out OVF to the cloud. This is called from Appliance::taskThreadWriteOVF() 1557 * in S3 mode and therefore runs on the OVF write worker thread. This then starts a second worker 1558 * thread to create temporary files (see Appliance::writeFS()). 1559 * 1560 * @param pTask 1561 * @return 1562 */ 3164 1563 int Appliance::writeS3(TaskExportOVF *pTask) 3165 1564 { … … 3328 1727 } 3329 1728 3330 ////////////////////////////////////////////////////////////////////////////////3331 //3332 // IAppliance public methods3333 //3334 ////////////////////////////////////////////////////////////////////////////////3335 3336 /**3337 * Public method implementation.3338 * @param3339 * @return3340 */3341 STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)3342 {3343 if (!aPath)3344 return E_POINTER;3345 3346 AutoCaller autoCaller(this);3347 if (FAILED(autoCaller.rc())) return autoCaller.rc();3348 3349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);3350 3351 if (!isApplianceIdle())3352 return E_ACCESSDENIED;3353 3354 Bstr bstrPath(m->locInfo.strPath);3355 bstrPath.cloneTo(aPath);3356 3357 return S_OK;3358 }3359 3360 /**3361 * Public method implementation.3362 * @param3363 * @return3364 */3365 STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))3366 {3367 CheckComArgOutSafeArrayPointerValid(aDisks);3368 3369 AutoCaller autoCaller(this);3370 if (FAILED(autoCaller.rc())) return autoCaller.rc();3371 3372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);3373 3374 if (!isApplianceIdle())3375 return E_ACCESSDENIED;3376 3377 if (m->pReader) // OVFReader instantiated?3378 {3379 size_t c = m->pReader->m_mapDisks.size();3380 com::SafeArray<BSTR> sfaDisks(c);3381 3382 DiskImagesMap::const_iterator it;3383 size_t i = 0;3384 for (it = m->pReader->m_mapDisks.begin();3385 it != m->pReader->m_mapDisks.end();3386 ++it, ++i)3387 {3388 // create a string representing this disk3389 const DiskImage &d = it->second;3390 char *psz = NULL;3391 RTStrAPrintf(&psz,3392 "%s\t"3393 "%RI64\t"3394 "%RI64\t"3395 "%s\t"3396 "%s\t"3397 "%RI64\t"3398 "%RI64\t"3399 "%s",3400 d.strDiskId.c_str(),3401 d.iCapacity,3402 d.iPopulatedSize,3403 d.strFormat.c_str(),3404 d.strHref.c_str(),3405 d.iSize,3406 d.iChunkSize,3407 d.strCompression.c_str());3408 Utf8Str utf(psz);3409 Bstr bstr(utf);3410 // push to safearray3411 bstr.cloneTo(&sfaDisks[i]);3412 RTStrFree(psz);3413 }3414 3415 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));3416 }3417 3418 return S_OK;3419 }3420 3421 /**3422 * Public method implementation.3423 * @param3424 * @return3425 */3426 STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))3427 {3428 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);3429 3430 AutoCaller autoCaller(this);3431 if (FAILED(autoCaller.rc())) return autoCaller.rc();3432 3433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);3434 3435 if (!isApplianceIdle())3436 return E_ACCESSDENIED;3437 3438 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);3439 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));3440 3441 return S_OK;3442 }3443 3444 /**3445 * Public method implementation.3446 * @param path3447 * @return3448 */3449 STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress)3450 {3451 if (!path) return E_POINTER;3452 CheckComArgOutPointerValid(aProgress);3453 3454 AutoCaller autoCaller(this);3455 if (FAILED(autoCaller.rc())) return autoCaller.rc();3456 3457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);3458 3459 if (!isApplianceIdle())3460 return E_ACCESSDENIED;3461 3462 if (m->pReader)3463 {3464 delete m->pReader;3465 m->pReader = NULL;3466 }3467 3468 // see if we can handle this file; for now we insist it has an ".ovf" extension3469 Utf8Str strPath (path);3470 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))3471 return setError(VBOX_E_FILE_ERROR,3472 tr("Appliance file must have .ovf extension"));3473 3474 ComObjPtr<Progress> progress;3475 HRESULT rc = S_OK;3476 try3477 {3478 /* Parse all necessary info out of the URI */3479 parseURI(strPath, m->locInfo);3480 rc = readImpl(m->locInfo, progress);3481 }3482 catch (HRESULT aRC)3483 {3484 rc = aRC;3485 }3486 3487 if (SUCCEEDED(rc))3488 /* Return progress to the caller */3489 progress.queryInterfaceTo(aProgress);3490 3491 return S_OK;3492 }3493 3494 /**3495 * Public method implementation.3496 * @return3497 */3498 STDMETHODIMP Appliance::Interpret()3499 {3500 // @todo:3501 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))3502 // - Appropriate handle errors like not supported file formats3503 AutoCaller autoCaller(this);3504 if (FAILED(autoCaller.rc())) return autoCaller.rc();3505 3506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);3507 3508 if (!isApplianceIdle())3509 return E_ACCESSDENIED;3510 3511 HRESULT rc = S_OK;3512 3513 /* Clear any previous virtual system descriptions */3514 m->virtualSystemDescriptions.clear();3515 3516 /* We need the default path for storing disk images */3517 ComPtr<ISystemProperties> systemProps;3518 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());3519 if (FAILED(rc)) return rc;3520 Bstr bstrDefaultHardDiskLocation;3521 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());3522 if (FAILED(rc)) return rc;3523 3524 if (!m->pReader)3525 return setError(E_FAIL,3526 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));3527 3528 // Change the appliance state so we can safely leave the lock while doing time-consuming3529 // disk imports; also the below method calls do all kinds of locking which conflicts with3530 // the appliance object lock3531 m->state = Data::ApplianceImporting;3532 alock.release();3533 3534 /* Try/catch so we can clean up on error */3535 try3536 {3537 list<VirtualSystem>::const_iterator it;3538 /* Iterate through all virtual systems */3539 for (it = m->pReader->m_llVirtualSystems.begin();3540 it != m->pReader->m_llVirtualSystems.end();3541 ++it)3542 {3543 const VirtualSystem &vsysThis = *it;3544 3545 ComObjPtr<VirtualSystemDescription> pNewDesc;3546 rc = pNewDesc.createObject();3547 if (FAILED(rc)) throw rc;3548 rc = pNewDesc->init();3549 if (FAILED(rc)) throw rc;3550 3551 /* Guest OS type */3552 Utf8Str strOsTypeVBox,3553 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);3554 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);3555 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,3556 "",3557 strCIMOSType,3558 strOsTypeVBox);3559 3560 /* VM name */3561 /* If the there isn't any name specified create a default one out of3562 * the OS type */3563 Utf8Str nameVBox = vsysThis.strName;3564 if (nameVBox.isEmpty())3565 nameVBox = strOsTypeVBox;3566 searchUniqueVMName(nameVBox);3567 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,3568 "",3569 vsysThis.strName,3570 nameVBox);3571 3572 /* VM Product */3573 if (!vsysThis.strProduct.isEmpty())3574 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,3575 "",3576 vsysThis.strProduct,3577 vsysThis.strProduct);3578 3579 /* VM Vendor */3580 if (!vsysThis.strVendor.isEmpty())3581 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,3582 "",3583 vsysThis.strVendor,3584 vsysThis.strVendor);3585 3586 /* VM Version */3587 if (!vsysThis.strVersion.isEmpty())3588 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,3589 "",3590 vsysThis.strVersion,3591 vsysThis.strVersion);3592 3593 /* VM ProductUrl */3594 if (!vsysThis.strProductUrl.isEmpty())3595 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,3596 "",3597 vsysThis.strProductUrl,3598 vsysThis.strProductUrl);3599 3600 /* VM VendorUrl */3601 if (!vsysThis.strVendorUrl.isEmpty())3602 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,3603 "",3604 vsysThis.strVendorUrl,3605 vsysThis.strVendorUrl);3606 3607 /* VM description */3608 if (!vsysThis.strDescription.isEmpty())3609 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,3610 "",3611 vsysThis.strDescription,3612 vsysThis.strDescription);3613 3614 /* VM license */3615 if (!vsysThis.strLicenseText.isEmpty())3616 pNewDesc->addEntry(VirtualSystemDescriptionType_License,3617 "",3618 vsysThis.strLicenseText,3619 vsysThis.strLicenseText);3620 3621 /* Now that we know the OS type, get our internal defaults based on that. */3622 ComPtr<IGuestOSType> pGuestOSType;3623 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());3624 if (FAILED(rc)) throw rc;3625 3626 /* CPU count */3627 ULONG cpuCountVBox = vsysThis.cCPUs;3628 /* Check for the constrains */3629 if (cpuCountVBox > SchemaDefs::MaxCPUCount)3630 {3631 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),3632 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);3633 cpuCountVBox = SchemaDefs::MaxCPUCount;3634 }3635 if (vsysThis.cCPUs == 0)3636 cpuCountVBox = 1;3637 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,3638 "",3639 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),3640 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));3641 3642 /* RAM */3643 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;3644 /* Check for the constrains */3645 if (ullMemSizeVBox != 0 &&3646 (ullMemSizeVBox < MM_RAM_MIN_IN_MB ||3647 ullMemSizeVBox > MM_RAM_MAX_IN_MB))3648 {3649 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."),3650 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);3651 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);3652 }3653 if (vsysThis.ullMemorySize == 0)3654 {3655 /* If the RAM of the OVF is zero, use our predefined values */3656 ULONG memSizeVBox2;3657 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);3658 if (FAILED(rc)) throw rc;3659 /* VBox stores that in MByte */3660 ullMemSizeVBox = (uint64_t)memSizeVBox2;3661 }3662 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,3663 "",3664 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),3665 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));3666 3667 /* Audio */3668 if (!vsysThis.strSoundCardType.isEmpty())3669 /* Currently we set the AC97 always.3670 @todo: figure out the hardware which could be possible */3671 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,3672 "",3673 vsysThis.strSoundCardType,3674 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));3675 3676 #ifdef VBOX_WITH_USB3677 /* USB Controller */3678 if (vsysThis.fHasUsbController)3679 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");3680 #endif /* VBOX_WITH_USB */3681 3682 /* Network Controller */3683 size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();3684 if (cEthernetAdapters > 0)3685 {3686 /* Check for the constrains */3687 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)3688 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),3689 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);3690 3691 /* Get the default network adapter type for the selected guest OS */3692 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;3693 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);3694 if (FAILED(rc)) throw rc;3695 3696 EthernetAdaptersList::const_iterator itEA;3697 /* Iterate through all abstract networks. We support 8 network3698 * adapters at the maximum, so the first 8 will be added only. */3699 size_t a = 0;3700 for (itEA = vsysThis.llEthernetAdapters.begin();3701 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;3702 ++itEA, ++a)3703 {3704 const EthernetAdapter &ea = *itEA; // logical network to connect to3705 Utf8Str strNetwork = ea.strNetworkName;3706 // make sure it's one of these two3707 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))3708 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))3709 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))3710 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))3711 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))3712 )3713 strNetwork = "Bridged"; // VMware assumes this is the default apparently3714 3715 /* Figure out the hardware type */3716 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;3717 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))3718 {3719 /* If the default adapter is already one of the two3720 * PCNet adapters use the default one. If not use the3721 * Am79C970A as fallback. */3722 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||3723 defaultAdapterVBox == NetworkAdapterType_Am79C973))3724 nwAdapterVBox = NetworkAdapterType_Am79C970A;3725 }3726 #ifdef VBOX_WITH_E10003727 /* VMWare accidentally write this with VirtualCenter 3.5,3728 so make sure in this case always to use the VMWare one */3729 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))3730 nwAdapterVBox = NetworkAdapterType_I82545EM;3731 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))3732 {3733 /* Check if this OVF was written by VirtualBox */3734 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))3735 {3736 /* If the default adapter is already one of the three3737 * E1000 adapters use the default one. If not use the3738 * I82545EM as fallback. */3739 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||3740 defaultAdapterVBox == NetworkAdapterType_I82543GC ||3741 defaultAdapterVBox == NetworkAdapterType_I82545EM))3742 nwAdapterVBox = NetworkAdapterType_I82540EM;3743 }3744 else3745 /* Always use this one since it's what VMware uses */3746 nwAdapterVBox = NetworkAdapterType_I82545EM;3747 }3748 #endif /* VBOX_WITH_E1000 */3749 3750 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,3751 "", // ref3752 ea.strNetworkName, // orig3753 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf3754 0,3755 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf3756 }3757 }3758 3759 /* Floppy Drive */3760 if (vsysThis.fHasFloppyDrive)3761 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");3762 3763 /* CD Drive */3764 if (vsysThis.fHasCdromDrive)3765 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");3766 3767 /* Hard disk Controller */3768 uint16_t cIDEused = 0;3769 uint16_t cSATAused = 0; NOREF(cSATAused);3770 uint16_t cSCSIused = 0; NOREF(cSCSIused);3771 ControllersMap::const_iterator hdcIt;3772 /* Iterate through all hard disk controllers */3773 for (hdcIt = vsysThis.mapControllers.begin();3774 hdcIt != vsysThis.mapControllers.end();3775 ++hdcIt)3776 {3777 const HardDiskController &hdc = hdcIt->second;3778 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);3779 3780 switch (hdc.system)3781 {3782 case HardDiskController::IDE:3783 {3784 /* Check for the constrains */3785 /* @todo: I'm very confused! Are these bits *one* controller or3786 is every port/bus declared as an extra controller. */3787 if (cIDEused < 4)3788 {3789 // @todo: figure out the IDE types3790 /* Use PIIX4 as default */3791 Utf8Str strType = "PIIX4";3792 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))3793 strType = "PIIX3";3794 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))3795 strType = "ICH6";3796 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,3797 strControllerID,3798 hdc.strControllerType,3799 strType);3800 }3801 else3802 {3803 /* Warn only once */3804 if (cIDEused == 1)3805 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),3806 vsysThis.strName.c_str());3807 3808 }3809 ++cIDEused;3810 break;3811 }3812 3813 case HardDiskController::SATA:3814 {3815 #ifdef VBOX_WITH_AHCI3816 /* Check for the constrains */3817 if (cSATAused < 1)3818 {3819 // @todo: figure out the SATA types3820 /* We only support a plain AHCI controller, so use them always */3821 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,3822 strControllerID,3823 hdc.strControllerType,3824 "AHCI");3825 }3826 else3827 {3828 /* Warn only once */3829 if (cSATAused == 1)3830 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),3831 vsysThis.strName.c_str());3832 3833 }3834 ++cSATAused;3835 break;3836 #else /* !VBOX_WITH_AHCI */3837 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SATA controller emulation"),3838 vsysThis.strName.c_str());3839 #endif /* !VBOX_WITH_AHCI */3840 }3841 3842 case HardDiskController::SCSI:3843 {3844 #ifdef VBOX_WITH_LSILOGIC3845 /* Check for the constrains */3846 if (cSCSIused < 1)3847 {3848 Utf8Str hdcController = "LsiLogic";3849 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))3850 hdcController = "BusLogic";3851 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,3852 strControllerID,3853 hdc.strControllerType,3854 hdcController);3855 }3856 else3857 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."),3858 vsysThis.strName.c_str(),3859 hdc.strControllerType.c_str(),3860 strControllerID.c_str());3861 ++cSCSIused;3862 break;3863 #else /* !VBOX_WITH_LSILOGIC */3864 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SCSI controller emulation"),3865 vsysThis.strName.c_str());3866 #endif /* !VBOX_WITH_LSILOGIC */3867 }3868 }3869 }3870 3871 /* Hard disks */3872 if (vsysThis.mapVirtualDisks.size() > 0)3873 {3874 VirtualDisksMap::const_iterator itVD;3875 /* Iterate through all hard disks ()*/3876 for (itVD = vsysThis.mapVirtualDisks.begin();3877 itVD != vsysThis.mapVirtualDisks.end();3878 ++itVD)3879 {3880 const VirtualDisk &hd = itVD->second;3881 /* Get the associated disk image */3882 const DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId];3883 3884 // @todo:3885 // - figure out all possible vmdk formats we also support3886 // - figure out if there is a url specifier for vhd already3887 // - we need a url specifier for the vdi format3888 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)3889 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))3890 {3891 /* If the href is empty use the VM name as filename */3892 Utf8Str strFilename = di.strHref;3893 if (!strFilename.length())3894 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());3895 /* Construct a unique target path */3896 Utf8StrFmt strPath("%ls%c%s",3897 bstrDefaultHardDiskLocation.raw(),3898 RTPATH_DELIMITER,3899 strFilename.c_str());3900 searchUniqueDiskImageFilePath(strPath);3901 3902 /* find the description for the hard disk controller3903 * that has the same ID as hd.idController */3904 const VirtualSystemDescriptionEntry *pController;3905 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))3906 throw setError(E_FAIL,3907 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),3908 hd.idController,3909 di.strHref.c_str());3910 3911 /* controller to attach to, and the bus within that controller */3912 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",3913 pController->ulIndex,3914 hd.ulAddressOnParent);3915 ULONG ulSize = 0;3916 if (di.iCapacity != -1)3917 ulSize = (ULONG)(di.iCapacity / _1M);3918 else if (di.iPopulatedSize != -1)3919 ulSize = (ULONG)(di.iPopulatedSize / _1M);3920 else if (di.iSize != -1)3921 ulSize = (ULONG)(di.iSize / _1M);3922 if (ulSize == 0)3923 ulSize = 10000; // assume 10 GB, this is for the progress bar only anyway3924 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,3925 hd.strDiskId,3926 di.strHref,3927 strPath,3928 ulSize,3929 strExtraConfig);3930 }3931 else3932 throw setError(VBOX_E_FILE_ERROR,3933 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));3934 }3935 }3936 3937 m->virtualSystemDescriptions.push_back(pNewDesc);3938 }3939 }3940 catch (HRESULT aRC)3941 {3942 /* On error we clear the list & return */3943 m->virtualSystemDescriptions.clear();3944 rc = aRC;3945 }3946 3947 // reset the appliance state3948 alock.acquire();3949 m->state = Data::ApplianceIdle;3950 3951 return rc;3952 }3953 3954 /**3955 * Public method implementation.3956 * @param aProgress3957 * @return3958 */3959 STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)3960 {3961 CheckComArgOutPointerValid(aProgress);3962 3963 AutoCaller autoCaller(this);3964 if (FAILED(autoCaller.rc())) return autoCaller.rc();3965 3966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);3967 3968 // do not allow entering this method if the appliance is busy reading or writing3969 if (!isApplianceIdle())3970 return E_ACCESSDENIED;3971 3972 if (!m->pReader)3973 return setError(E_FAIL,3974 tr("Cannot import machines without reading it first (call read() before importMachines())"));3975 3976 ComObjPtr<Progress> progress;3977 HRESULT rc = S_OK;3978 try3979 {3980 rc = importImpl(m->locInfo, progress);3981 }3982 catch (HRESULT aRC)3983 {3984 rc = aRC;3985 }3986 3987 if (SUCCEEDED(rc))3988 /* Return progress to the caller */3989 progress.queryInterfaceTo(aProgress);3990 3991 return rc;3992 }3993 3994 STDMETHODIMP Appliance::CreateVFSExplorer(IN_BSTR aURI, IVFSExplorer **aExplorer)3995 {3996 CheckComArgOutPointerValid(aExplorer);3997 3998 AutoCaller autoCaller(this);3999 if (FAILED(autoCaller.rc())) return autoCaller.rc();4000 4001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4002 4003 ComObjPtr<VFSExplorer> explorer;4004 HRESULT rc = S_OK;4005 try4006 {4007 Utf8Str uri(aURI);4008 /* Check which kind of export the user has requested */4009 LocationInfo li;4010 parseURI(uri, li);4011 /* Create the explorer object */4012 explorer.createObject();4013 rc = explorer->init(li.storageType, li.strPath, li.strHostname, li.strUsername, li.strPassword, mVirtualBox);4014 }4015 catch (HRESULT aRC)4016 {4017 rc = aRC;4018 }4019 4020 if (SUCCEEDED(rc))4021 /* Return explorer to the caller */4022 explorer.queryInterfaceTo(aExplorer);4023 4024 return rc;4025 }4026 4027 STDMETHODIMP Appliance::Write(IN_BSTR format, IN_BSTR path, IProgress **aProgress)4028 {4029 if (!path) return E_POINTER;4030 CheckComArgOutPointerValid(aProgress);4031 4032 AutoCaller autoCaller(this);4033 if (FAILED(autoCaller.rc())) return autoCaller.rc();4034 4035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);4036 4037 // do not allow entering this method if the appliance is busy reading or writing4038 if (!isApplianceIdle())4039 return E_ACCESSDENIED;4040 4041 // see if we can handle this file; for now we insist it has an ".ovf" extension4042 Utf8Str strPath = path;4043 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))4044 return setError(VBOX_E_FILE_ERROR,4045 tr("Appliance file must have .ovf extension"));4046 4047 Utf8Str strFormat(format);4048 TaskExportOVF::OVFFormat ovfF;4049 if (strFormat == "ovf-0.9")4050 ovfF = TaskExportOVF::OVF_0_9;4051 else if (strFormat == "ovf-1.0")4052 ovfF = TaskExportOVF::OVF_1_0;4053 else4054 return setError(VBOX_E_FILE_ERROR,4055 tr("Invalid format \"%s\" specified"), strFormat.c_str());4056 4057 ComObjPtr<Progress> progress;4058 HRESULT rc = S_OK;4059 try4060 {4061 /* Parse all necessary info out of the URI */4062 parseURI(strPath, m->locInfo);4063 rc = writeImpl(ovfF, m->locInfo, progress);4064 }4065 catch (HRESULT aRC)4066 {4067 rc = aRC;4068 }4069 4070 if (SUCCEEDED(rc))4071 /* Return progress to the caller */4072 progress.queryInterfaceTo(aProgress);4073 4074 return rc;4075 }4076 4077 /**4078 * Public method implementation.4079 * @return4080 */4081 STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))4082 {4083 if (ComSafeArrayOutIsNull(aWarnings))4084 return E_POINTER;4085 4086 AutoCaller autoCaller(this);4087 if (FAILED(autoCaller.rc())) return autoCaller.rc();4088 4089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4090 4091 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());4092 4093 list<Utf8Str>::const_iterator it;4094 size_t i = 0;4095 for (it = m->llWarnings.begin();4096 it != m->llWarnings.end();4097 ++it, ++i)4098 {4099 Bstr bstr = *it;4100 bstr.cloneTo(&sfaWarnings[i]);4101 }4102 4103 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));4104 4105 return S_OK;4106 }4107 4108 ////////////////////////////////////////////////////////////////////////////////4109 //4110 // IVirtualSystemDescription constructor / destructor4111 //4112 ////////////////////////////////////////////////////////////////////////////////4113 4114 DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)4115 4116 /**4117 * COM initializer.4118 * @return4119 */4120 HRESULT VirtualSystemDescription::init()4121 {4122 /* Enclose the state transition NotReady->InInit->Ready */4123 AutoInitSpan autoInitSpan(this);4124 AssertReturn(autoInitSpan.isOk(), E_FAIL);4125 4126 /* Initialize data */4127 m = new Data();4128 4129 /* Confirm a successful initialization */4130 autoInitSpan.setSucceeded();4131 return S_OK;4132 }4133 4134 /**4135 * COM uninitializer.4136 */4137 4138 void VirtualSystemDescription::uninit()4139 {4140 delete m;4141 m = NULL;4142 }4143 4144 ////////////////////////////////////////////////////////////////////////////////4145 //4146 // IVirtualSystemDescription public methods4147 //4148 ////////////////////////////////////////////////////////////////////////////////4149 4150 /**4151 * Public method implementation.4152 * @param4153 * @return4154 */4155 STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)4156 {4157 if (!aCount)4158 return E_POINTER;4159 4160 AutoCaller autoCaller(this);4161 if (FAILED(autoCaller.rc())) return autoCaller.rc();4162 4163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4164 4165 *aCount = (ULONG)m->llDescriptions.size();4166 4167 return S_OK;4168 }4169 4170 /**4171 * Public method implementation.4172 * @return4173 */4174 STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),4175 ComSafeArrayOut(BSTR, aRefs),4176 ComSafeArrayOut(BSTR, aOrigValues),4177 ComSafeArrayOut(BSTR, aVboxValues),4178 ComSafeArrayOut(BSTR, aExtraConfigValues))4179 {4180 if (ComSafeArrayOutIsNull(aTypes) ||4181 ComSafeArrayOutIsNull(aRefs) ||4182 ComSafeArrayOutIsNull(aOrigValues) ||4183 ComSafeArrayOutIsNull(aVboxValues) ||4184 ComSafeArrayOutIsNull(aExtraConfigValues))4185 return E_POINTER;4186 4187 AutoCaller autoCaller(this);4188 if (FAILED(autoCaller.rc())) return autoCaller.rc();4189 4190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4191 4192 ULONG c = (ULONG)m->llDescriptions.size();4193 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);4194 com::SafeArray<BSTR> sfaRefs(c);4195 com::SafeArray<BSTR> sfaOrigValues(c);4196 com::SafeArray<BSTR> sfaVboxValues(c);4197 com::SafeArray<BSTR> sfaExtraConfigValues(c);4198 4199 list<VirtualSystemDescriptionEntry>::const_iterator it;4200 size_t i = 0;4201 for (it = m->llDescriptions.begin();4202 it != m->llDescriptions.end();4203 ++it, ++i)4204 {4205 const VirtualSystemDescriptionEntry &vsde = (*it);4206 4207 sfaTypes[i] = vsde.type;4208 4209 Bstr bstr = vsde.strRef;4210 bstr.cloneTo(&sfaRefs[i]);4211 4212 bstr = vsde.strOvf;4213 bstr.cloneTo(&sfaOrigValues[i]);4214 4215 bstr = vsde.strVbox;4216 bstr.cloneTo(&sfaVboxValues[i]);4217 4218 bstr = vsde.strExtraConfig;4219 bstr.cloneTo(&sfaExtraConfigValues[i]);4220 }4221 4222 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));4223 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));4224 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));4225 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));4226 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));4227 4228 return S_OK;4229 }4230 4231 /**4232 * Public method implementation.4233 * @return4234 */4235 STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescriptionType_T aType,4236 ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),4237 ComSafeArrayOut(BSTR, aRefs),4238 ComSafeArrayOut(BSTR, aOrigValues),4239 ComSafeArrayOut(BSTR, aVboxValues),4240 ComSafeArrayOut(BSTR, aExtraConfigValues))4241 {4242 if (ComSafeArrayOutIsNull(aTypes) ||4243 ComSafeArrayOutIsNull(aRefs) ||4244 ComSafeArrayOutIsNull(aOrigValues) ||4245 ComSafeArrayOutIsNull(aVboxValues) ||4246 ComSafeArrayOutIsNull(aExtraConfigValues))4247 return E_POINTER;4248 4249 AutoCaller autoCaller(this);4250 if (FAILED(autoCaller.rc())) return autoCaller.rc();4251 4252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4253 4254 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);4255 ULONG c = (ULONG)vsd.size();4256 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);4257 com::SafeArray<BSTR> sfaRefs(c);4258 com::SafeArray<BSTR> sfaOrigValues(c);4259 com::SafeArray<BSTR> sfaVboxValues(c);4260 com::SafeArray<BSTR> sfaExtraConfigValues(c);4261 4262 list<VirtualSystemDescriptionEntry*>::const_iterator it;4263 size_t i = 0;4264 for (it = vsd.begin();4265 it != vsd.end();4266 ++it, ++i)4267 {4268 const VirtualSystemDescriptionEntry *vsde = (*it);4269 4270 sfaTypes[i] = vsde->type;4271 4272 Bstr bstr = vsde->strRef;4273 bstr.cloneTo(&sfaRefs[i]);4274 4275 bstr = vsde->strOvf;4276 bstr.cloneTo(&sfaOrigValues[i]);4277 4278 bstr = vsde->strVbox;4279 bstr.cloneTo(&sfaVboxValues[i]);4280 4281 bstr = vsde->strExtraConfig;4282 bstr.cloneTo(&sfaExtraConfigValues[i]);4283 }4284 4285 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));4286 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));4287 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));4288 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));4289 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));4290 4291 return S_OK;4292 }4293 4294 /**4295 * Public method implementation.4296 * @return4297 */4298 STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionType_T aType,4299 VirtualSystemDescriptionValueType_T aWhich,4300 ComSafeArrayOut(BSTR, aValues))4301 {4302 if (ComSafeArrayOutIsNull(aValues))4303 return E_POINTER;4304 4305 AutoCaller autoCaller(this);4306 if (FAILED(autoCaller.rc())) return autoCaller.rc();4307 4308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4309 4310 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);4311 com::SafeArray<BSTR> sfaValues((ULONG)vsd.size());4312 4313 list<VirtualSystemDescriptionEntry*>::const_iterator it;4314 size_t i = 0;4315 for (it = vsd.begin();4316 it != vsd.end();4317 ++it, ++i)4318 {4319 const VirtualSystemDescriptionEntry *vsde = (*it);4320 4321 Bstr bstr;4322 switch (aWhich)4323 {4324 case VirtualSystemDescriptionValueType_Reference: bstr = vsde->strRef; break;4325 case VirtualSystemDescriptionValueType_Original: bstr = vsde->strOvf; break;4326 case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVbox; break;4327 case VirtualSystemDescriptionValueType_ExtraConfig: bstr = vsde->strExtraConfig; break;4328 }4329 4330 bstr.cloneTo(&sfaValues[i]);4331 }4332 4333 sfaValues.detachTo(ComSafeArrayOutArg(aValues));4334 4335 return S_OK;4336 }4337 4338 /**4339 * Public method implementation.4340 * @return4341 */4342 STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),4343 ComSafeArrayIn(IN_BSTR, argVboxValues),4344 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))4345 {4346 #ifndef RT_OS_WINDOWS4347 NOREF(aEnabledSize);4348 #endif /* RT_OS_WINDOWS */4349 4350 CheckComArgSafeArrayNotNull(aEnabled);4351 CheckComArgSafeArrayNotNull(argVboxValues);4352 CheckComArgSafeArrayNotNull(argExtraConfigValues);4353 4354 AutoCaller autoCaller(this);4355 if (FAILED(autoCaller.rc())) return autoCaller.rc();4356 4357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);4358 4359 com::SafeArray<BOOL> sfaEnabled(ComSafeArrayInArg(aEnabled));4360 com::SafeArray<IN_BSTR> sfaVboxValues(ComSafeArrayInArg(argVboxValues));4361 com::SafeArray<IN_BSTR> sfaExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));4362 4363 if ( (sfaEnabled.size() != m->llDescriptions.size())4364 || (sfaVboxValues.size() != m->llDescriptions.size())4365 || (sfaExtraConfigValues.size() != m->llDescriptions.size())4366 )4367 return E_INVALIDARG;4368 4369 list<VirtualSystemDescriptionEntry>::iterator it;4370 size_t i = 0;4371 for (it = m->llDescriptions.begin();4372 it != m->llDescriptions.end();4373 ++it, ++i)4374 {4375 VirtualSystemDescriptionEntry& vsde = *it;4376 4377 if (sfaEnabled[i])4378 {4379 vsde.strVbox = sfaVboxValues[i];4380 vsde.strExtraConfig = sfaExtraConfigValues[i];4381 }4382 else4383 vsde.type = VirtualSystemDescriptionType_Ignore;4384 }4385 4386 return S_OK;4387 }4388 4389 /**4390 * Public method implementation.4391 * @return4392 */4393 STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionType_T aType,4394 IN_BSTR aVboxValue,4395 IN_BSTR aExtraConfigValue)4396 {4397 AutoCaller autoCaller(this);4398 if (FAILED(autoCaller.rc())) return autoCaller.rc();4399 4400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);4401 4402 addEntry(aType, "", aVboxValue, aVboxValue, 0, aExtraConfigValue);4403 4404 return S_OK;4405 }4406 4407 /**4408 * Internal method; adds a new description item to the member list.4409 * @param aType Type of description for the new item.4410 * @param strRef Reference item; only used with hard disk controllers.4411 * @param aOrigValue Corresponding original value from OVF.4412 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).4413 * @param ulSizeMB Weight for IProgress4414 * @param strExtraConfig Extra configuration; meaning dependent on type.4415 */4416 void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,4417 const Utf8Str &strRef,4418 const Utf8Str &aOrigValue,4419 const Utf8Str &aAutoValue,4420 uint32_t ulSizeMB,4421 const Utf8Str &strExtraConfig /*= ""*/)4422 {4423 VirtualSystemDescriptionEntry vsde;4424 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them4425 vsde.type = aType;4426 vsde.strRef = strRef;4427 vsde.strOvf = aOrigValue;4428 vsde.strVbox = aAutoValue;4429 vsde.strExtraConfig = strExtraConfig;4430 vsde.ulSizeMB = ulSizeMB;4431 4432 m->llDescriptions.push_back(vsde);4433 }4434 4435 /**4436 * Private method; returns a list of description items containing all the items from the member4437 * description items of this virtual system that match the given type.4438 * @param aType4439 * @return4440 */4441 std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)4442 {4443 std::list<VirtualSystemDescriptionEntry*> vsd;4444 4445 list<VirtualSystemDescriptionEntry>::iterator it;4446 for (it = m->llDescriptions.begin();4447 it != m->llDescriptions.end();4448 ++it)4449 {4450 if (it->type == aType)4451 vsd.push_back(&(*it));4452 }4453 4454 return vsd;4455 }4456 4457 /**4458 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with4459 * the given reference ID. Useful when needing the controller for a particular4460 * virtual disk.4461 * @param id4462 * @return4463 */4464 const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)4465 {4466 Utf8Str strRef = Utf8StrFmt("%RI32", id);4467 list<VirtualSystemDescriptionEntry>::const_iterator it;4468 for (it = m->llDescriptions.begin();4469 it != m->llDescriptions.end();4470 ++it)4471 {4472 const VirtualSystemDescriptionEntry &d = *it;4473 switch (d.type)4474 {4475 case VirtualSystemDescriptionType_HardDiskControllerIDE:4476 case VirtualSystemDescriptionType_HardDiskControllerSATA:4477 case VirtualSystemDescriptionType_HardDiskControllerSCSI:4478 if (d.strRef == strRef)4479 return &d;4480 break;4481 }4482 }4483 4484 return NULL;4485 }4486 4487 ////////////////////////////////////////////////////////////////////////////////4488 //4489 // IMachine public methods4490 //4491 ////////////////////////////////////////////////////////////////////////////////4492 4493 // This code is here so we won't have to include the appliance headers in the4494 // IMachine implementation, and we also need to access private appliance data.4495 4496 /**4497 * Public method implementation.4498 * @param appliance4499 * @return4500 */4501 4502 STDMETHODIMP Machine::Export(IAppliance *aAppliance, IVirtualSystemDescription **aDescription)4503 {4504 HRESULT rc = S_OK;4505 4506 if (!aAppliance)4507 return E_POINTER;4508 4509 AutoCaller autoCaller(this);4510 if (FAILED(autoCaller.rc())) return autoCaller.rc();4511 4512 ComObjPtr<VirtualSystemDescription> pNewDesc;4513 4514 try4515 {4516 Bstr bstrName1;4517 Bstr bstrDescription;4518 Bstr bstrGuestOSType;4519 uint32_t cCPUs;4520 uint32_t ulMemSizeMB;4521 BOOL fUSBEnabled;4522 BOOL fAudioEnabled;4523 AudioControllerType_T audioController;4524 4525 ComPtr<IUSBController> pUsbController;4526 ComPtr<IAudioAdapter> pAudioAdapter;4527 4528 // first, call the COM methods, as they request locks4529 rc = COMGETTER(USBController)(pUsbController.asOutParam());4530 if (FAILED(rc))4531 fUSBEnabled = false;4532 else4533 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);4534 4535 // request the machine lock while acessing internal members4536 AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS);4537 4538 pAudioAdapter = mAudioAdapter;4539 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);4540 if (FAILED(rc)) throw rc;4541 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);4542 if (FAILED(rc)) throw rc;4543 4544 // get name4545 bstrName1 = mUserData->mName;4546 // get description4547 bstrDescription = mUserData->mDescription;4548 // get guest OS4549 bstrGuestOSType = mUserData->mOSTypeId;4550 // CPU count4551 cCPUs = mHWData->mCPUCount;4552 // memory size in MB4553 ulMemSizeMB = mHWData->mMemorySize;4554 // VRAM size?4555 // BIOS settings?4556 // 3D acceleration enabled?4557 // hardware virtualization enabled?4558 // nested paging enabled?4559 // HWVirtExVPIDEnabled?4560 // PAEEnabled?4561 // snapshotFolder?4562 // VRDPServer?4563 4564 // create a new virtual system4565 rc = pNewDesc.createObject();4566 if (FAILED(rc)) throw rc;4567 rc = pNewDesc->init();4568 if (FAILED(rc)) throw rc;4569 4570 /* Guest OS type */4571 Utf8Str strOsTypeVBox(bstrGuestOSType);4572 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());4573 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,4574 "",4575 Utf8StrFmt("%RI32", cim),4576 strOsTypeVBox);4577 4578 /* VM name */4579 Utf8Str strVMName(bstrName1);4580 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,4581 "",4582 strVMName,4583 strVMName);4584 4585 // description4586 Utf8Str strDescription(bstrDescription);4587 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,4588 "",4589 strDescription,4590 strDescription);4591 4592 /* CPU count*/4593 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);4594 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,4595 "",4596 strCpuCount,4597 strCpuCount);4598 4599 /* Memory */4600 Utf8Str strMemory = Utf8StrFmt("%RI64", (uint64_t)ulMemSizeMB * _1M);4601 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,4602 "",4603 strMemory,4604 strMemory);4605 4606 int32_t lIDEControllerIndex = 0;4607 int32_t lSATAControllerIndex = 0;4608 int32_t lSCSIControllerIndex = 0;4609 4610 /* Fetch all available storage controllers */4611 com::SafeIfaceArray<IStorageController> nwControllers;4612 rc = COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(nwControllers));4613 if (FAILED(rc)) throw rc;4614 4615 ComPtr<IStorageController> pIDEController;4616 #ifdef VBOX_WITH_AHCI4617 ComPtr<IStorageController> pSATAController;4618 #endif /* VBOX_WITH_AHCI */4619 #ifdef VBOX_WITH_LSILOGIC4620 ComPtr<IStorageController> pSCSIController;4621 #endif /* VBOX_WITH_LSILOGIC */4622 for (size_t j = 0; j < nwControllers.size(); ++j)4623 {4624 StorageBus_T eType;4625 rc = nwControllers[j]->COMGETTER(Bus)(&eType);4626 if (FAILED(rc)) throw rc;4627 if ( eType == StorageBus_IDE4628 && pIDEController.isNull())4629 pIDEController = nwControllers[j];4630 #ifdef VBOX_WITH_AHCI4631 else if ( eType == StorageBus_SATA4632 && pSATAController.isNull())4633 pSATAController = nwControllers[j];4634 #endif /* VBOX_WITH_AHCI */4635 #ifdef VBOX_WITH_LSILOGIC4636 else if ( eType == StorageBus_SCSI4637 && pSATAController.isNull())4638 pSCSIController = nwControllers[j];4639 #endif /* VBOX_WITH_LSILOGIC */4640 }4641 4642 // <const name="HardDiskControllerIDE" value="6" />4643 if (!pIDEController.isNull())4644 {4645 Utf8Str strVbox;4646 StorageControllerType_T ctlr;4647 rc = pIDEController->COMGETTER(ControllerType)(&ctlr);4648 if (FAILED(rc)) throw rc;4649 switch(ctlr)4650 {4651 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;4652 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;4653 case StorageControllerType_ICH6: strVbox = "ICH6"; break;4654 }4655 4656 if (strVbox.length())4657 {4658 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();4659 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,4660 Utf8StrFmt("%d", lIDEControllerIndex),4661 strVbox,4662 strVbox);4663 }4664 }4665 4666 #ifdef VBOX_WITH_AHCI4667 // <const name="HardDiskControllerSATA" value="7" />4668 if (!pSATAController.isNull())4669 {4670 Utf8Str strVbox = "AHCI";4671 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();4672 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,4673 Utf8StrFmt("%d", lSATAControllerIndex),4674 strVbox,4675 strVbox);4676 }4677 #endif // VBOX_WITH_AHCI4678 4679 #ifdef VBOX_WITH_LSILOGIC4680 // <const name="HardDiskControllerSCSI" value="8" />4681 if (!pSCSIController.isNull())4682 {4683 StorageControllerType_T ctlr;4684 rc = pSCSIController->COMGETTER(ControllerType)(&ctlr);4685 if (SUCCEEDED(rc))4686 {4687 Utf8Str strVbox = "LsiLogic"; // the default in VBox4688 switch(ctlr)4689 {4690 case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break;4691 case StorageControllerType_BusLogic: strVbox = "BusLogic"; break;4692 }4693 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();4694 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,4695 Utf8StrFmt("%d", lSCSIControllerIndex),4696 strVbox,4697 strVbox);4698 }4699 else4700 throw rc;4701 }4702 #endif // VBOX_WITH_LSILOGIC4703 4704 // <const name="HardDiskImage" value="9" />4705 // <const name="Floppy" value="18" />4706 // <const name="CDROM" value="19" />4707 4708 MediaData::AttachmentList::iterator itA;4709 for (itA = mMediaData->mAttachments.begin();4710 itA != mMediaData->mAttachments.end();4711 ++itA)4712 {4713 ComObjPtr<MediumAttachment> pHDA = *itA;4714 4715 // the attachment's data4716 ComPtr<IMedium> pMedium;4717 ComPtr<IStorageController> ctl;4718 Bstr controllerName;4719 4720 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());4721 if (FAILED(rc)) throw rc;4722 4723 rc = GetStorageControllerByName(controllerName, ctl.asOutParam());4724 if (FAILED(rc)) throw rc;4725 4726 StorageBus_T storageBus;4727 DeviceType_T deviceType;4728 LONG lChannel;4729 LONG lDevice;4730 4731 rc = ctl->COMGETTER(Bus)(&storageBus);4732 if (FAILED(rc)) throw rc;4733 4734 rc = pHDA->COMGETTER(Type)(&deviceType);4735 if (FAILED(rc)) throw rc;4736 4737 rc = pHDA->COMGETTER(Medium)(pMedium.asOutParam());4738 if (FAILED(rc)) throw rc;4739 4740 rc = pHDA->COMGETTER(Port)(&lChannel);4741 if (FAILED(rc)) throw rc;4742 4743 rc = pHDA->COMGETTER(Device)(&lDevice);4744 if (FAILED(rc)) throw rc;4745 4746 Utf8Str strTargetVmdkName;4747 Utf8Str strLocation;4748 ULONG64 ullSize = 0;4749 4750 if ( deviceType == DeviceType_HardDisk4751 && pMedium4752 )4753 {4754 Bstr bstrLocation;4755 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());4756 if (FAILED(rc)) throw rc;4757 strLocation = bstrLocation;4758 4759 Bstr bstrName;4760 rc = pMedium->COMGETTER(Name)(bstrName.asOutParam());4761 if (FAILED(rc)) throw rc;4762 4763 strTargetVmdkName = bstrName;4764 strTargetVmdkName.stripExt();4765 strTargetVmdkName.append(".vmdk");4766 4767 // we need the size of the image so we can give it to addEntry();4768 // later, on export, the progress weight will be based on this.4769 // pMedium can be a differencing image though; in that case, we4770 // need to use the size of the base instead.4771 ComPtr<IMedium> pBaseMedium;4772 rc = pMedium->COMGETTER(Base)(pBaseMedium.asOutParam());4773 // returns pMedium if there are no diff images4774 if (FAILED(rc)) throw rc;4775 4776 // force reading state, or else size will be returned as 04777 MediumState_T ms;4778 rc = pBaseMedium->RefreshState(&ms);4779 if (FAILED(rc)) throw rc;4780 4781 rc = pBaseMedium->COMGETTER(Size)(&ullSize);4782 if (FAILED(rc)) throw rc;4783 }4784 4785 // and how this translates to the virtual system4786 int32_t lControllerVsys = 0;4787 LONG lChannelVsys;4788 4789 switch (storageBus)4790 {4791 case StorageBus_IDE:4792 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,4793 // and it must be updated when that is changed!4794 4795 if (lChannel == 0 && lDevice == 0) // primary master4796 lChannelVsys = 0;4797 else if (lChannel == 0 && lDevice == 1) // primary slave4798 lChannelVsys = 1;4799 else if (lChannel == 1 && lDevice == 0) // secondary master; by default this is the CD-ROM but as of VirtualBox 3.1 that can change4800 lChannelVsys = 2;4801 else if (lChannel == 1 && lDevice == 1) // secondary slave4802 lChannelVsys = 3;4803 else4804 throw setError(VBOX_E_NOT_SUPPORTED,4805 tr("Cannot handle medium attachment: channel is %d, device is %d"), lChannel, lDevice);4806 4807 lControllerVsys = lIDEControllerIndex;4808 break;4809 4810 case StorageBus_SATA:4811 lChannelVsys = lChannel; // should be between 0 and 294812 lControllerVsys = lSATAControllerIndex;4813 break;4814 4815 case StorageBus_SCSI:4816 lChannelVsys = lChannel; // should be between 0 and 154817 lControllerVsys = lSCSIControllerIndex;4818 break;4819 4820 case StorageBus_Floppy:4821 lChannelVsys = 0;4822 lControllerVsys = 0;4823 break;4824 4825 default:4826 throw setError(VBOX_E_NOT_SUPPORTED,4827 tr("Cannot handle medium attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);4828 break;4829 }4830 4831 Utf8StrFmt strExtra("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys);4832 Utf8Str strEmpty;4833 4834 switch (deviceType)4835 {4836 case DeviceType_HardDisk:4837 Log(("Adding VirtualSystemDescriptionType_HardDiskImage, disk size: %RI64\n", ullSize));4838 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,4839 strTargetVmdkName, // disk ID: let's use the name4840 strTargetVmdkName, // OVF value:4841 strLocation, // vbox value: media path4842 (uint32_t)(ullSize / _1M),4843 strExtra);4844 break;4845 4846 case DeviceType_DVD:4847 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM,4848 strEmpty, // disk ID4849 strEmpty, // OVF value4850 strEmpty, // vbox value4851 1, // ulSize4852 strExtra);4853 break;4854 4855 case DeviceType_Floppy:4856 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy,4857 strEmpty, // disk ID4858 strEmpty, // OVF value4859 strEmpty, // vbox value4860 1, // ulSize4861 strExtra);4862 break;4863 }4864 }4865 4866 // <const name="NetworkAdapter" />4867 size_t a;4868 for (a = 0;4869 a < SchemaDefs::NetworkAdapterCount;4870 ++a)4871 {4872 ComPtr<INetworkAdapter> pNetworkAdapter;4873 BOOL fEnabled;4874 NetworkAdapterType_T adapterType;4875 NetworkAttachmentType_T attachmentType;4876 4877 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());4878 if (FAILED(rc)) throw rc;4879 /* Enable the network card & set the adapter type */4880 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);4881 if (FAILED(rc)) throw rc;4882 4883 if (fEnabled)4884 {4885 Utf8Str strAttachmentType;4886 4887 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);4888 if (FAILED(rc)) throw rc;4889 4890 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);4891 if (FAILED(rc)) throw rc;4892 4893 switch (attachmentType)4894 {4895 case NetworkAttachmentType_Null:4896 strAttachmentType = "Null";4897 break;4898 4899 case NetworkAttachmentType_NAT:4900 strAttachmentType = "NAT";4901 break;4902 4903 case NetworkAttachmentType_Bridged:4904 strAttachmentType = "Bridged";4905 break;4906 4907 case NetworkAttachmentType_Internal:4908 strAttachmentType = "Internal";4909 break;4910 4911 case NetworkAttachmentType_HostOnly:4912 strAttachmentType = "HostOnly";4913 break;4914 }4915 4916 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,4917 "", // ref4918 strAttachmentType, // orig4919 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf4920 0,4921 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf4922 }4923 }4924 4925 // <const name="USBController" />4926 #ifdef VBOX_WITH_USB4927 if (fUSBEnabled)4928 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");4929 #endif /* VBOX_WITH_USB */4930 4931 // <const name="SoundCard" />4932 if (fAudioEnabled)4933 {4934 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,4935 "",4936 "ensoniq1371", // this is what OVFTool writes and VMware supports4937 Utf8StrFmt("%RI32", audioController));4938 }4939 4940 // finally, add the virtual system to the appliance4941 Appliance *pAppliance = static_cast<Appliance*>(aAppliance);4942 AutoCaller autoCaller1(pAppliance);4943 if (FAILED(autoCaller1.rc())) return autoCaller1.rc();4944 4945 /* We return the new description to the caller */4946 ComPtr<IVirtualSystemDescription> copy(pNewDesc);4947 copy.queryInterfaceTo(aDescription);4948 4949 AutoWriteLock alock(pAppliance COMMA_LOCKVAL_SRC_POS);4950 4951 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);4952 }4953 catch(HRESULT arc)4954 {4955 rc = arc;4956 }4957 4958 return rc;4959 }4960 4961 /* vi: set tabstop=4 shiftwidth=4 expandtab: */ -
trunk/src/VBox/Main/Makefile.kmk
r27741 r27829 291 291 VirtualBoxImpl.cpp \ 292 292 ApplianceImpl.cpp \ 293 ApplianceImplExport.cpp \ 293 294 xml/ovfreader.cpp \ 294 295 VFSExplorerImpl.cpp \
Note:
See TracChangeset
for help on using the changeset viewer.