Changeset 21612 in vbox for trunk/src/VBox/Main/ApplianceImpl.cpp
- Timestamp:
- Jul 15, 2009 3:00:34 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/ApplianceImpl.cpp
r21588 r21612 50 50 //////////////////////////////////////////////////////////////////////////////// 51 51 52 /* Describe a location for the import/export. The location could be a file on a 53 * local hard disk or a remote target based on the supported inet protocols. */ 54 struct Appliance::LocationInfo 55 { 56 LocationInfo() 57 : storageType(VFSType_File) {} 58 VFSType_T storageType; /* Which type of storage should be handled */ 59 Utf8Str strPath; /* File path for the import/export */ 60 Utf8Str strHostname; /* Hostname on remote storage locations (could be empty) */ 61 Utf8Str strUsername; /* Username on remote storage locations (could be empty) */ 62 Utf8Str strPassword; /* Password on remote storage locations (could be empty) */ 63 }; 64 52 65 // opaque private instance data of Appliance class 53 66 struct Appliance::Data 54 67 { 68 Data() 69 : pReader(NULL) {} 70 71 ~Data() 72 { 73 if (pReader) 74 { 75 delete pReader; 76 pReader = NULL; 77 } 78 } 79 80 LocationInfo locInfo; /* The location info for the currently processed OVF */ 81 55 82 OVFReader *pReader; 56 83 57 list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions; //84 list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions; 58 85 59 86 list<Utf8Str> llWarnings; 60 87 61 ULONG ulWeightPerOperation; // for progress calculations 62 63 Data() 64 : pReader(NULL) 65 { 66 } 67 68 ~Data() 69 { 70 if (pReader) 71 { 72 delete pReader; 73 pReader = NULL; 74 } 75 } 88 ULONG ulWeightPerOperation; 76 89 }; 77 90 … … 92 105 const char *pcszVbox; 93 106 } 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 107 g_osTypes[] = 108 { 109 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other }, 110 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 }, 111 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS }, 112 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 }, 113 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 }, 114 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 }, 115 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 }, 116 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware }, 117 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware }, 118 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris }, 119 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_OpenSolaris }, 120 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD }, 121 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD }, 122 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX }, 123 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 }, 124 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe }, 125 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD }, 126 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP }, 127 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP }, 128 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP }, 129 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 }, 130 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 }, 131 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 }, 132 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista }, 133 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 }, 134 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 }, 135 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 }, 136 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 }, 137 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat }, 138 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 }, 139 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 }, 140 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE }, 141 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE }, 142 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE }, 143 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 }, 144 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 }, 145 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux }, 146 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux }, 147 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux}, 148 149 // { CIMOSType_CIMOS_TurboLinux_64, }, 150 151 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva }, 152 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 }, 153 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu }, 154 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 }, 155 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian }, 156 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 }, 157 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 }, 158 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 }, 159 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 }, 160 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 }, 161 { CIMOSType_CIMOS_Linux_64, SchemaDefs_OSTypeId_Linux26_64 } 149 162 }; 150 163 151 /* Pattern structure for matching the ostype description field */164 /* Pattern structure for matching the OS type description field */ 152 165 struct osTypePattern 153 166 { … … 302 315 303 316 DEFINE_EMPTY_CTOR_DTOR(Appliance) 304 struct shutup {};305 317 306 318 /** … … 339 351 //////////////////////////////////////////////////////////////////////////////// 340 352 // 353 // Appliance private methods 354 // 355 //////////////////////////////////////////////////////////////////////////////// 356 357 HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const 358 { 359 IMachine *machine = NULL; 360 char *tmpName = RTStrDup(aName.c_str()); 361 int i = 1; 362 /* @todo: Maybe too cost-intensive; try to find a lighter way */ 363 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND) 364 { 365 RTStrFree(tmpName); 366 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i); 367 ++i; 368 } 369 aName = tmpName; 370 RTStrFree(tmpName); 371 372 return S_OK; 373 } 374 375 HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const 376 { 377 IHardDisk *harddisk = NULL; 378 char *tmpName = RTStrDup(aName.c_str()); 379 int i = 1; 380 /* Check if the file exists or if a file with this path is registered 381 * already */ 382 /* @todo: Maybe too cost-intensive; try to find a lighter way */ 383 while (RTPathExists(tmpName) || 384 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND) 385 { 386 RTStrFree(tmpName); 387 char *tmpDir = RTStrDup(aName.c_str()); 388 RTPathStripFilename(tmpDir);; 389 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str())); 390 RTPathStripExt(tmpFile); 391 const char *tmpExt = RTPathExt(aName.c_str()); 392 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt); 393 RTStrFree(tmpFile); 394 RTStrFree(tmpDir); 395 ++i; 396 } 397 aName = tmpName; 398 RTStrFree(tmpName); 399 400 return S_OK; 401 } 402 403 /** 404 * Called from the import and export background threads to synchronize the second 405 * background disk thread's progress object with the current progress object so 406 * that the user interface sees progress correctly and that cancel signals are 407 * passed on to the second thread. 408 * @param pProgressThis Progress object of the current thread. 409 * @param pProgressAsync Progress object of asynchronous task running in background. 410 */ 411 void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis, 412 ComPtr<IProgress> &pProgressAsync) 413 { 414 HRESULT rc; 415 416 // now loop until the asynchronous operation completes and then report its result 417 BOOL fCompleted; 418 BOOL fCanceled; 419 ULONG currentPercent; 420 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted)))) 421 { 422 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled); 423 if (FAILED(rc)) throw rc; 424 if (fCanceled) 425 { 426 pProgressAsync->Cancel(); 427 break; 428 } 429 430 rc = pProgressAsync->COMGETTER(Percent(¤tPercent)); 431 if (FAILED(rc)) throw rc; 432 if (!pProgressThis.isNull()) 433 pProgressThis->setCurrentOperationProgress(currentPercent); 434 if (fCompleted) 435 break; 436 437 /* Make sure the loop is not too tight */ 438 rc = pProgressAsync->WaitForCompletion(100); 439 if (FAILED(rc)) throw rc; 440 } 441 // report result of asynchronous operation 442 LONG iRc; 443 rc = pProgressAsync->COMGETTER(ResultCode)(&iRc); 444 if (FAILED(rc)) throw rc; 445 446 447 // if the thread of the progress object has an error, then 448 // retrieve the error info from there, or it'll be lost 449 if (FAILED(iRc)) 450 { 451 ProgressErrorInfo info(pProgressAsync); 452 Utf8Str str(info.getText()); 453 const char *pcsz = str.c_str(); 454 HRESULT rc2 = setError(iRc, pcsz); 455 throw rc2; 456 } 457 } 458 459 void Appliance::addWarning(const char* aWarning, ...) 460 { 461 va_list args; 462 va_start(args, aWarning); 463 Utf8StrFmtVA str(aWarning, args); 464 va_end(args); 465 m->llWarnings.push_back(str); 466 } 467 468 void Appliance::disksWeight(uint32_t &ulTotalMB, uint32_t &cDisks) const 469 { 470 ulTotalMB = 0; 471 cDisks = 0; 472 /* Weigh the disk images according to their sizes */ 473 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; 474 for (it = m->virtualSystemDescriptions.begin(); 475 it != m->virtualSystemDescriptions.end(); 476 ++it) 477 { 478 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it); 479 /* One for every hard disk of the Virtual System */ 480 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 481 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH; 482 for (itH = avsdeHDs.begin(); 483 itH != avsdeHDs.end(); 484 ++itH) 485 { 486 const VirtualSystemDescriptionEntry *pHD = *itH; 487 ulTotalMB += pHD->ulSizeMB; 488 ++cDisks; 489 } 490 } 491 492 } 493 494 HRESULT Appliance::setUpProgressFS(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription) 495 { 496 HRESULT rc; 497 498 /* Create the progress object */ 499 pProgress.createObject(); 500 501 /* Weigh the disk images according to their sizes */ 502 uint32_t ulTotalMB; 503 uint32_t cDisks; 504 disksWeight(ulTotalMB, cDisks); 505 506 ULONG cOperations = 1 + cDisks; // one op per disk plus 1 for the XML 507 508 ULONG ulTotalOperationsWeight; 509 if (ulTotalMB) 510 { 511 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for the XML 512 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation; 513 } 514 else 515 { 516 // no disks to export: 517 ulTotalOperationsWeight = 1; 518 m->ulWeightPerOperation = 1; 519 } 520 521 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n", 522 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation)); 523 524 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 525 bstrDescription, 526 TRUE /* aCancelable */, 527 cOperations, // ULONG cOperations, 528 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight, 529 bstrDescription, // CBSTR bstrFirstOperationDescription, 530 m->ulWeightPerOperation); // ULONG ulFirstOperationWeight, 531 return rc; 532 } 533 534 HRESULT Appliance::setUpProgressImportS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription) 535 { 536 HRESULT rc; 537 538 /* Create the progress object */ 539 pProgress.createObject(); 540 541 /* Weigh the disk images according to their sizes */ 542 uint32_t ulTotalMB; 543 uint32_t cDisks; 544 disksWeight(ulTotalMB, cDisks); 545 546 ULONG cOperations = 1 + 1 + cDisks; // one op per disk plus 1 for init & 1 plus for the import */ 547 548 ULONG ulTotalOperationsWeight = ulTotalMB; 549 if (!ulTotalOperationsWeight) 550 // no disks to export: 551 ulTotalOperationsWeight = 1; 552 553 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import 554 ulTotalOperationsWeight += ulImportWeight; 555 556 m->ulWeightPerOperation = ulImportWeight; /* save for using later */ 557 558 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init 559 ulTotalOperationsWeight += ulInitWeight; 560 561 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n", 562 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation)); 563 564 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 565 bstrDescription, 566 TRUE /* aCancelable */, 567 cOperations, // ULONG cOperations, 568 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight, 569 Bstr(tr("Init")), // CBSTR bstrFirstOperationDescription, 570 ulInitWeight); // ULONG ulFirstOperationWeight, 571 return rc; 572 } 573 574 HRESULT Appliance::setUpProgressWriteS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription) 575 { 576 HRESULT rc; 577 578 /* Create the progress object */ 579 pProgress.createObject(); 580 581 /* Weigh the disk images according to their sizes */ 582 uint32_t ulTotalMB; 583 uint32_t cDisks; 584 disksWeight(ulTotalMB, cDisks); 585 586 ULONG cOperations = 1 + 1 + cDisks; // one op per disk plus 1 for the OVF & 1 plus to the temporary creation */ 587 588 ULONG ulTotalOperationsWeight; 589 if (ulTotalMB) 590 { 591 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) 592 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation; 593 } 594 else 595 { 596 // no disks to export: 597 ulTotalOperationsWeight = 1; 598 m->ulWeightPerOperation = 1; 599 } 600 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */ 601 ulTotalOperationsWeight += ulOVFCreationWeight; 602 603 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n", 604 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation)); 605 606 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 607 bstrDescription, 608 TRUE /* aCancelable */, 609 cOperations, // ULONG cOperations, 610 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight, 611 bstrDescription, // CBSTR bstrFirstOperationDescription, 612 ulOVFCreationWeight); // ULONG ulFirstOperationWeight, 613 return rc; 614 } 615 616 void Appliance::parseURI(Utf8Str strUri, LocationInfo &locInfo) const 617 { 618 /* Check the URI for the protocol */ 619 if (strUri.startsWith("file://", Utf8Str::CaseInsensitive)) /* File based */ 620 { 621 locInfo.storageType = VFSType_File; 622 strUri = strUri.substr(sizeof("file://") - 1); 623 } 624 else if (strUri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */ 625 { 626 locInfo.storageType = VFSType_S3; 627 strUri = strUri.substr(sizeof("SunCloud://") - 1); 628 } 629 else if (strUri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */ 630 { 631 locInfo.storageType = VFSType_S3; 632 strUri = strUri.substr(sizeof("S3://") - 1); 633 } 634 else if (strUri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */ 635 throw E_NOTIMPL; 636 637 /* Not necessary on a file based URI */ 638 if (locInfo.storageType != VFSType_File) 639 { 640 size_t uppos = strUri.find("@"); /* username:password combo */ 641 if (uppos != Utf8Str::npos) 642 { 643 locInfo.strUsername = strUri.substr(0, uppos); 644 strUri = strUri.substr(uppos + 1); 645 size_t upos = locInfo.strUsername.find(":"); 646 if (upos != Utf8Str::npos) 647 { 648 locInfo.strPassword = locInfo.strUsername.substr(upos + 1); 649 locInfo.strUsername = locInfo.strUsername.substr(0, upos); 650 } 651 } 652 size_t hpos = strUri.find("/"); /* hostname part */ 653 if (hpos != Utf8Str::npos) 654 { 655 locInfo.strHostname = strUri.substr(0, hpos); 656 strUri = strUri.substr(hpos); 657 } 658 } 659 660 locInfo.strPath = strUri; 661 } 662 663 void Appliance::parseBucket(Utf8Str &aPath, Utf8Str &aBucket) const 664 { 665 /* Buckets are S3 specific. So parse the bucket out of the file path */ 666 if (!aPath.startsWith("/")) 667 throw setError(E_INVALIDARG, 668 tr("The path '%s' must start with /"), aPath.c_str()); 669 size_t bpos = aPath.find("/", 1); 670 if (bpos != Utf8Str::npos) 671 { 672 aBucket = aPath.substr(1, bpos - 1); /* The bucket without any slashes */ 673 aPath = aPath.substr(bpos); /* The rest of the file path */ 674 } 675 /* If there is no bucket name provided reject it */ 676 if (aBucket.isEmpty()) 677 throw setError(E_INVALIDARG, 678 tr("You doesn't provide a bucket name in the URI '%s'"), aPath.c_str()); 679 } 680 681 struct Appliance::TaskOVF 682 { 683 TaskOVF(Appliance *aThat) 684 : pAppliance(aThat) 685 , rc(S_OK) {} 686 687 static int updateProgress(unsigned uPercent, void *pvUser); 688 689 LocationInfo locInfo; 690 Appliance *pAppliance; 691 ComObjPtr<Progress> progress; 692 HRESULT rc; 693 }; 694 695 struct Appliance::TaskImportOVF: Appliance::TaskOVF 696 { 697 enum TaskType 698 { 699 Read, 700 Import 701 }; 702 703 TaskImportOVF(Appliance *aThat) 704 : TaskOVF(aThat) 705 , taskType(Read) {} 706 707 int startThread(); 708 709 TaskType taskType; 710 }; 711 712 struct Appliance::TaskExportOVF: Appliance::TaskOVF 713 { 714 enum OVFFormat 715 { 716 unspecified, 717 OVF_0_9, 718 OVF_1_0 719 }; 720 enum TaskType 721 { 722 Write 723 }; 724 725 TaskExportOVF(Appliance *aThat) 726 : TaskOVF(aThat) 727 , taskType(Write) {} 728 729 int startThread(); 730 731 TaskType taskType; 732 OVFFormat enFormat; 733 }; 734 735 struct MyHardDiskAttachment 736 { 737 Guid uuid; 738 ComPtr<IMachine> pMachine; 739 Bstr controllerType; 740 int32_t lChannel; 741 int32_t lDevice; 742 }; 743 744 /* static */ 745 int Appliance::TaskOVF::updateProgress(unsigned uPercent, void *pvUser) 746 { 747 Appliance::TaskOVF* pTask = *(Appliance::TaskOVF**)pvUser; 748 749 if (pTask && 750 !pTask->progress.isNull()) 751 { 752 BOOL fCanceled; 753 pTask->progress->COMGETTER(Canceled)(&fCanceled); 754 if (fCanceled) 755 return -1; 756 pTask->progress->setCurrentOperationProgress(uPercent); 757 } 758 return VINF_SUCCESS; 759 } 760 761 HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress) 762 { 763 /* Initialize our worker task */ 764 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this)); 765 /* What should the task do */ 766 task->taskType = TaskImportOVF::Read; 767 /* Copy the current location info to the task */ 768 task->locInfo = aLocInfo; 769 770 BstrFmt bstrDesc = BstrFmt(tr("Read appliance '%s'"), 771 aLocInfo.strPath.c_str()); 772 HRESULT rc; 773 /* Create the progress object */ 774 aProgress.createObject(); 775 if (task->locInfo.storageType == VFSType_File) 776 { 777 /* 1 operation only */ 778 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 779 bstrDesc, 780 TRUE /* aCancelable */); 781 } 782 else 783 { 784 /* 4/5 is downloading, 1/5 is reading */ 785 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 786 bstrDesc, 787 TRUE /* aCancelable */, 788 2, // ULONG cOperations, 789 5, // ULONG ulTotalOperationsWeight, 790 BstrFmt(tr("Download appliance '%s'"), 791 aLocInfo.strPath.c_str()), // CBSTR bstrFirstOperationDescription, 792 4); // ULONG ulFirstOperationWeight, 793 } 794 if (FAILED(rc)) throw rc; 795 796 task->progress = aProgress; 797 798 rc = task->startThread(); 799 CheckComRCThrowRC(rc); 800 801 /* Don't destruct on success */ 802 task.release(); 803 804 return rc; 805 } 806 807 HRESULT Appliance::importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress) 808 { 809 /* Initialize our worker task */ 810 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this)); 811 /* What should the task do */ 812 task->taskType = TaskImportOVF::Import; 813 /* Copy the current location info to the task */ 814 task->locInfo = aLocInfo; 815 816 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"), 817 aLocInfo.strPath.c_str()); 818 819 HRESULT rc = S_OK; 820 821 /* todo: This progress init stuff should be done a little bit more generic */ 822 if (task->locInfo.storageType == VFSType_File) 823 rc = setUpProgressFS(aProgress, progressDesc); 824 else 825 rc = setUpProgressImportS3(aProgress, progressDesc); 826 if (FAILED(rc)) throw rc; 827 828 task->progress = aProgress; 829 830 rc = task->startThread(); 831 CheckComRCThrowRC(rc); 832 833 /* Don't destruct on success */ 834 task.release(); 835 836 return rc; 837 } 838 839 /** 840 * Worker thread implementation for Read() (ovf reader). 841 * @param aThread 842 * @param pvUser 843 */ 844 /* static */ 845 DECLCALLBACK(int) Appliance::taskThreadImportOVF(RTTHREAD /* aThread */, void *pvUser) 846 { 847 std::auto_ptr<TaskImportOVF> task(static_cast<TaskImportOVF*>(pvUser)); 848 AssertReturn(task.get(), VERR_GENERAL_FAILURE); 849 850 Appliance *pAppliance = task->pAppliance; 851 852 LogFlowFuncEnter(); 853 LogFlowFunc(("Appliance %p\n", pAppliance)); 854 855 HRESULT rc = S_OK; 856 857 switch(task->taskType) 858 { 859 case TaskImportOVF::Read: 860 { 861 if (task->locInfo.storageType == VFSType_File) 862 rc = pAppliance->readFS(task.get()); 863 else if (task->locInfo.storageType == VFSType_S3) 864 rc = pAppliance->readS3(task.get()); 865 break; 866 } 867 case TaskImportOVF::Import: 868 { 869 if (task->locInfo.storageType == VFSType_File) 870 rc = pAppliance->importFS(task.get()); 871 else if (task->locInfo.storageType == VFSType_S3) 872 rc = pAppliance->importS3(task.get()); 873 break; 874 } 875 } 876 877 LogFlowFunc(("rc=%Rhrc\n", rc)); 878 LogFlowFuncLeave(); 879 880 return VINF_SUCCESS; 881 } 882 883 int Appliance::TaskImportOVF::startThread() 884 { 885 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportOVF, this, 886 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, 887 "Appliance::Task"); 888 889 ComAssertMsgRCRet(vrc, 890 ("Could not create taskThreadImportOVF (%Rrc)\n", vrc), E_FAIL); 891 892 return S_OK; 893 } 894 895 int Appliance::readFS(TaskImportOVF *pTask) 896 { 897 LogFlowFuncEnter(); 898 LogFlowFunc(("Appliance %p\n", this)); 899 900 AutoCaller autoCaller(this); 901 CheckComRCReturnRC(autoCaller.rc()); 902 903 AutoWriteLock appLock(this); 904 905 HRESULT rc = S_OK; 906 907 try 908 { 909 m->pReader = new OVFReader(pTask->locInfo.strPath); 910 } 911 catch(xml::Error &x) 912 { 913 rc = setError(VBOX_E_FILE_ERROR, 914 x.what()); 915 } 916 917 pTask->rc = rc; 918 919 if (!pTask->progress.isNull()) 920 pTask->progress->notifyComplete(rc); 921 922 LogFlowFunc(("rc=%Rhrc\n", rc)); 923 LogFlowFuncLeave(); 924 925 return VINF_SUCCESS; 926 } 927 928 int Appliance::readS3(TaskImportOVF *pTask) 929 { 930 LogFlowFuncEnter(); 931 LogFlowFunc(("Appliance %p\n", this)); 932 933 AutoCaller autoCaller(this); 934 CheckComRCReturnRC(autoCaller.rc()); 935 936 AutoWriteLock appLock(this); 937 938 HRESULT rc = S_OK; 939 int vrc = VINF_SUCCESS; 940 RTS3 hS3 = NIL_RTS3; 941 char szOSTmpDir[RTPATH_MAX]; 942 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir)); 943 /* The template for the temporary directory created below */ 944 char *pszTmpDir; 945 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir); 946 list< pair<Utf8Str, ULONG> > filesList; 947 Utf8Str strTmpOvf; 948 949 try 950 { 951 /* Extract the bucket */ 952 Utf8Str tmpPath = pTask->locInfo.strPath; 953 Utf8Str bucket; 954 parseBucket(tmpPath, bucket); 955 956 /* We need a temporary directory which we can put the OVF file & all 957 * disk images in */ 958 vrc = RTDirCreateTemp(pszTmpDir); 959 if (RT_FAILURE(rc)) 960 throw setError(VBOX_E_FILE_ERROR, 961 tr("Cannot create temporary directory '%s'"), pszTmpDir); 962 963 /* The temporary name of the target OVF file */ 964 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath)); 965 966 /* Next we have to download the OVF */ 967 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING); 968 if(RT_FAILURE(vrc)) 969 throw setError(VBOX_E_IPRT_ERROR, 970 tr("Cannot create S3 service handler")); 971 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask); 972 973 /* Get it */ 974 char *pszFilename = RTPathFilename(strTmpOvf.c_str()); 975 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strTmpOvf.c_str()); 976 if (RT_FAILURE(vrc)) 977 { 978 if(vrc == VERR_S3_CANCELED) 979 throw S_OK; /* todo: !!!!!!!!!!!!! */ 980 else if(vrc == VERR_S3_ACCESS_DENIED) 981 throw setError(E_ACCESSDENIED, 982 tr("Cannot download file '%s' from S3 storage server (Access denied)"), pszFilename); 983 else if(vrc == VERR_S3_NOT_FOUND) 984 throw setError(VBOX_E_FILE_ERROR, 985 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename); 986 else 987 throw setError(VBOX_E_IPRT_ERROR, 988 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc); 989 } 990 991 /* Close the connection early */ 992 RTS3Destroy(hS3); 993 hS3 = NIL_RTS3; 994 995 if (!pTask->progress.isNull()) 996 pTask->progress->setNextOperation(Bstr(tr("Reading")), 1); 997 998 /* Prepare the temporary reading of the OVF */ 999 ComObjPtr<Progress> progress; 1000 LocationInfo li; 1001 li.strPath = strTmpOvf; 1002 /* Start the reading from the fs */ 1003 rc = readImpl(li, progress); 1004 if (FAILED(rc)) throw rc; 1005 1006 /* Unlock the appliance for the reading thread */ 1007 appLock.unlock(); 1008 /* Wait until the reading is done, but report the progress back to the 1009 caller */ 1010 ComPtr<IProgress> progressInt(progress); 1011 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */ 1012 1013 /* Again lock the appliance for the next steps */ 1014 appLock.lock(); 1015 } 1016 catch(HRESULT aRC) 1017 { 1018 rc = aRC; 1019 } 1020 /* Cleanup */ 1021 RTS3Destroy(hS3); 1022 /* Delete all files which where temporary created */ 1023 if (RTPathExists(strTmpOvf.c_str())) 1024 { 1025 vrc = RTFileDelete(strTmpOvf.c_str()); 1026 if(RT_FAILURE(vrc)) 1027 rc = setError(VBOX_E_FILE_ERROR, 1028 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc); 1029 } 1030 /* Delete the temporary directory */ 1031 if (RTPathExists(pszTmpDir)) 1032 { 1033 vrc = RTDirRemove(pszTmpDir); 1034 if(RT_FAILURE(vrc)) 1035 rc = setError(VBOX_E_FILE_ERROR, 1036 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 1037 } 1038 if (pszTmpDir) 1039 RTStrFree(pszTmpDir); 1040 1041 pTask->rc = rc; 1042 1043 if (!pTask->progress.isNull()) 1044 pTask->progress->notifyComplete(rc); 1045 1046 LogFlowFunc(("rc=%Rhrc\n", rc)); 1047 LogFlowFuncLeave(); 1048 1049 return VINF_SUCCESS; 1050 } 1051 1052 int Appliance::importFS(TaskImportOVF *pTask) 1053 { 1054 LogFlowFuncEnter(); 1055 LogFlowFunc(("Appliance %p\n", this)); 1056 1057 AutoCaller autoCaller(this); 1058 CheckComRCReturnRC(autoCaller.rc()); 1059 1060 AutoWriteLock appLock(this); 1061 1062 HRESULT rc = S_OK; 1063 1064 // rollback for errors: 1065 // a list of images that we created/imported 1066 list<MyHardDiskAttachment> llHardDiskAttachments; 1067 list< ComPtr<IHardDisk> > llHardDisksCreated; 1068 list<Guid> llMachinesRegistered; 1069 1070 ComPtr<ISession> session; 1071 bool fSessionOpen = false; 1072 rc = session.createInprocObject(CLSID_Session); 1073 CheckComRCReturnRC(rc); 1074 1075 const OVFReader reader = *m->pReader; 1076 // this is safe to access because this thread only gets started 1077 // if pReader != NULL 1078 1079 list<VirtualSystem>::const_iterator it; 1080 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1; 1081 /* Iterate through all virtual systems of that appliance */ 1082 size_t i = 0; 1083 for (it = reader.m_llVirtualSystems.begin(), 1084 it1 = m->virtualSystemDescriptions.begin(); 1085 it != reader.m_llVirtualSystems.end(); 1086 ++it, ++it1, ++i) 1087 { 1088 const VirtualSystem &vsysThis = *it; 1089 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1); 1090 1091 ComPtr<IMachine> pNewMachine; 1092 1093 /* Catch possible errors */ 1094 try 1095 { 1096 /* Guest OS type */ 1097 std::list<VirtualSystemDescriptionEntry*> vsdeOS; 1098 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS); 1099 if (vsdeOS.size() < 1) 1100 throw setError(VBOX_E_FILE_ERROR, 1101 tr("Missing guest OS type")); 1102 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox; 1103 1104 /* Now that we know the base system get our internal defaults based on that. */ 1105 ComPtr<IGuestOSType> osType; 1106 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam()); 1107 if (FAILED(rc)) throw rc; 1108 1109 /* Create the machine */ 1110 /* First get the name */ 1111 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name); 1112 if (vsdeName.size() < 1) 1113 throw setError(VBOX_E_FILE_ERROR, 1114 tr("Missing VM name")); 1115 const Utf8Str &strNameVBox = vsdeName.front()->strVbox; 1116 rc = mVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox), 1117 Bstr(), Bstr(), 1118 pNewMachine.asOutParam()); 1119 if (FAILED(rc)) throw rc; 1120 1121 // and the description 1122 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description); 1123 if (vsdeDescription.size()) 1124 { 1125 const Utf8Str &strDescription = vsdeDescription.front()->strVbox; 1126 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription)); 1127 if (FAILED(rc)) throw rc; 1128 } 1129 1130 /* CPU count */ 1131 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType (VirtualSystemDescriptionType_CPU); 1132 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL); 1133 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox; 1134 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str()); 1135 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount); 1136 if (FAILED(rc)) throw rc; 1137 bool fEnableIOApic = false; 1138 /* We need HWVirt & IO-APIC if more than one CPU is requested */ 1139 if (tmpCount > 1) 1140 { 1141 rc = pNewMachine->COMSETTER(HWVirtExEnabled)(TRUE); 1142 if (FAILED(rc)) throw rc; 1143 1144 fEnableIOApic = true; 1145 } 1146 1147 /* RAM */ 1148 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory); 1149 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL); 1150 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox; 1151 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str()); 1152 rc = pNewMachine->COMSETTER(MemorySize)(tt); 1153 if (FAILED(rc)) throw rc; 1154 1155 /* VRAM */ 1156 /* Get the recommended VRAM for this guest OS type */ 1157 ULONG vramVBox; 1158 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox); 1159 if (FAILED(rc)) throw rc; 1160 1161 /* Set the VRAM */ 1162 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox); 1163 if (FAILED(rc)) throw rc; 1164 1165 /* I/O APIC: so far we have no setting for this. Enable it if we 1166 import a Windows VM because if if Windows was installed without IOAPIC, 1167 it will not mind finding an one later on, but if Windows was installed 1168 _with_ an IOAPIC, it will bluescreen if it's not found */ 1169 Bstr bstrFamilyId; 1170 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam()); 1171 if (FAILED(rc)) throw rc; 1172 1173 Utf8Str strFamilyId(bstrFamilyId); 1174 if (strFamilyId == "Windows") 1175 fEnableIOApic = true; 1176 1177 /* If IP-APIC should be enabled could be have different reasons. 1178 See CPU count & the Win test above. Here we enable it if it was 1179 previously requested. */ 1180 if (fEnableIOApic) 1181 { 1182 ComPtr<IBIOSSettings> pBIOSSettings; 1183 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam()); 1184 if (FAILED(rc)) throw rc; 1185 1186 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE); 1187 if (FAILED(rc)) throw rc; 1188 } 1189 1190 /* Audio Adapter */ 1191 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard); 1192 /* @todo: we support one audio adapter only */ 1193 if (vsdeAudioAdapter.size() > 0) 1194 { 1195 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox; 1196 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0) 1197 { 1198 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str()); 1199 ComPtr<IAudioAdapter> audioAdapter; 1200 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam()); 1201 if (FAILED(rc)) throw rc; 1202 rc = audioAdapter->COMSETTER(Enabled)(true); 1203 if (FAILED(rc)) throw rc; 1204 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio)); 1205 if (FAILED(rc)) throw rc; 1206 } 1207 } 1208 1209 #ifdef VBOX_WITH_USB 1210 /* USB Controller */ 1211 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController); 1212 // USB support is enabled if there's at least one such entry; to disable USB support, 1213 // the type of the USB item would have been changed to "ignore" 1214 bool fUSBEnabled = vsdeUSBController.size() > 0; 1215 1216 ComPtr<IUSBController> usbController; 1217 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam()); 1218 if (FAILED(rc)) throw rc; 1219 rc = usbController->COMSETTER(Enabled)(fUSBEnabled); 1220 if (FAILED(rc)) throw rc; 1221 #endif /* VBOX_WITH_USB */ 1222 1223 /* Change the network adapters */ 1224 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter); 1225 if (vsdeNW.size() == 0) 1226 { 1227 /* No network adapters, so we have to disable our default one */ 1228 ComPtr<INetworkAdapter> nwVBox; 1229 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam()); 1230 if (FAILED(rc)) throw rc; 1231 rc = nwVBox->COMSETTER(Enabled)(false); 1232 if (FAILED(rc)) throw rc; 1233 } 1234 else 1235 { 1236 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt; 1237 /* Iterate through all network cards. We support 8 network adapters 1238 * at the maximum. (@todo: warn if there are more!) */ 1239 size_t a = 0; 1240 for (nwIt = vsdeNW.begin(); 1241 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount); 1242 ++nwIt, ++a) 1243 { 1244 const VirtualSystemDescriptionEntry* pvsys = *nwIt; 1245 1246 const Utf8Str &nwTypeVBox = pvsys->strVbox; 1247 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str()); 1248 ComPtr<INetworkAdapter> pNetworkAdapter; 1249 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam()); 1250 if (FAILED(rc)) throw rc; 1251 /* Enable the network card & set the adapter type */ 1252 rc = pNetworkAdapter->COMSETTER(Enabled)(true); 1253 if (FAILED(rc)) throw rc; 1254 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1)); 1255 if (FAILED(rc)) throw rc; 1256 1257 // default is NAT; change to "bridged" if extra conf says so 1258 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive)) 1259 { 1260 /* Attach to the right interface */ 1261 rc = pNetworkAdapter->AttachToBridgedInterface(); 1262 if (FAILED(rc)) throw rc; 1263 ComPtr<IHost> host; 1264 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam()); 1265 if (FAILED(rc)) throw rc; 1266 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces; 1267 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces)); 1268 if (FAILED(rc)) throw rc; 1269 /* We search for the first host network interface which 1270 * is usable for bridged networking */ 1271 for (size_t i=0; i < nwInterfaces.size(); ++i) 1272 { 1273 HostNetworkInterfaceType_T itype; 1274 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype); 1275 if (FAILED(rc)) throw rc; 1276 if (itype == HostNetworkInterfaceType_Bridged) 1277 { 1278 Bstr name; 1279 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam()); 1280 if (FAILED(rc)) throw rc; 1281 /* Set the interface name to attach to */ 1282 pNetworkAdapter->COMSETTER(HostInterface)(name); 1283 if (FAILED(rc)) throw rc; 1284 break; 1285 } 1286 } 1287 } 1288 /* Next test for host only interfaces */ 1289 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive)) 1290 { 1291 /* Attach to the right interface */ 1292 rc = pNetworkAdapter->AttachToHostOnlyInterface(); 1293 if (FAILED(rc)) throw rc; 1294 ComPtr<IHost> host; 1295 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam()); 1296 if (FAILED(rc)) throw rc; 1297 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces; 1298 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces)); 1299 if (FAILED(rc)) throw rc; 1300 /* We search for the first host network interface which 1301 * is usable for host only networking */ 1302 for (size_t i=0; i < nwInterfaces.size(); ++i) 1303 { 1304 HostNetworkInterfaceType_T itype; 1305 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype); 1306 if (FAILED(rc)) throw rc; 1307 if (itype == HostNetworkInterfaceType_HostOnly) 1308 { 1309 Bstr name; 1310 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam()); 1311 if (FAILED(rc)) throw rc; 1312 /* Set the interface name to attach to */ 1313 pNetworkAdapter->COMSETTER(HostInterface)(name); 1314 if (FAILED(rc)) throw rc; 1315 break; 1316 } 1317 } 1318 } 1319 } 1320 } 1321 1322 /* Floppy drive */ 1323 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy); 1324 // Floppy support is enabled if there's at least one such entry; to disable floppy support, 1325 // the type of the floppy item would have been changed to "ignore" 1326 bool fFloppyEnabled = vsdeFloppy.size() > 0; 1327 ComPtr<IFloppyDrive> floppyDrive; 1328 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam()); 1329 if (FAILED(rc)) throw rc; 1330 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled); 1331 if (FAILED(rc)) throw rc; 1332 1333 /* CDROM drive */ 1334 /* @todo: I can't disable the CDROM. So nothing to do for now */ 1335 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM); 1336 1337 /* Hard disk controller IDE */ 1338 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE); 1339 if (vsdeHDCIDE.size() > 1) 1340 throw setError(VBOX_E_FILE_ERROR, 1341 tr("Too many IDE controllers in OVF; VirtualBox only supports one")); 1342 if (vsdeHDCIDE.size() == 1) 1343 { 1344 ComPtr<IStorageController> pController; 1345 rc = pNewMachine->GetStorageControllerByName(Bstr("IDE"), pController.asOutParam()); 1346 if (FAILED(rc)) throw rc; 1347 1348 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str(); 1349 if (!strcmp(pcszIDEType, "PIIX3")) 1350 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3); 1351 else if (!strcmp(pcszIDEType, "PIIX4")) 1352 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4); 1353 else if (!strcmp(pcszIDEType, "ICH6")) 1354 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6); 1355 else 1356 throw setError(VBOX_E_FILE_ERROR, 1357 tr("Invalid IDE controller type \"%s\""), 1358 pcszIDEType); 1359 if (FAILED(rc)) throw rc; 1360 } 1361 #ifdef VBOX_WITH_AHCI 1362 /* Hard disk controller SATA */ 1363 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA); 1364 if (vsdeHDCSATA.size() > 1) 1365 throw setError(VBOX_E_FILE_ERROR, 1366 tr("Too many SATA controllers in OVF; VirtualBox only supports one")); 1367 if (vsdeHDCSATA.size() > 0) 1368 { 1369 ComPtr<IStorageController> pController; 1370 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox; 1371 if (hdcVBox == "AHCI") 1372 { 1373 rc = pNewMachine->AddStorageController(Bstr("SATA"), StorageBus_SATA, pController.asOutParam()); 1374 if (FAILED(rc)) throw rc; 1375 } 1376 else 1377 throw setError(VBOX_E_FILE_ERROR, 1378 tr("Invalid SATA controller type \"%s\""), 1379 hdcVBox.c_str()); 1380 } 1381 #endif /* VBOX_WITH_AHCI */ 1382 1383 #ifdef VBOX_WITH_LSILOGIC 1384 /* Hard disk controller SCSI */ 1385 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI); 1386 if (vsdeHDCSCSI.size() > 1) 1387 throw setError(VBOX_E_FILE_ERROR, 1388 tr("Too many SCSI controllers in OVF; VirtualBox only supports one")); 1389 if (vsdeHDCSCSI.size() > 0) 1390 { 1391 ComPtr<IStorageController> pController; 1392 StorageControllerType_T controllerType; 1393 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox; 1394 if (hdcVBox == "LsiLogic") 1395 controllerType = StorageControllerType_LsiLogic; 1396 else if (hdcVBox == "BusLogic") 1397 controllerType = StorageControllerType_BusLogic; 1398 else 1399 throw setError(VBOX_E_FILE_ERROR, 1400 tr("Invalid SCSI controller type \"%s\""), 1401 hdcVBox.c_str()); 1402 1403 rc = pNewMachine->AddStorageController(Bstr("SCSI"), StorageBus_SCSI, pController.asOutParam()); 1404 if (FAILED(rc)) throw rc; 1405 rc = pController->COMSETTER(ControllerType)(controllerType); 1406 if (FAILED(rc)) throw rc; 1407 } 1408 #endif /* VBOX_WITH_LSILOGIC */ 1409 1410 /* Now its time to register the machine before we add any hard disks */ 1411 rc = mVirtualBox->RegisterMachine(pNewMachine); 1412 if (FAILED(rc)) throw rc; 1413 1414 Bstr newMachineId_; 1415 rc = pNewMachine->COMGETTER(Id)(newMachineId_.asOutParam()); 1416 if (FAILED(rc)) throw rc; 1417 Guid newMachineId(newMachineId_); 1418 1419 // store new machine for roll-back in case of errors 1420 llMachinesRegistered.push_back(newMachineId); 1421 1422 /* Create the hard disks & connect them to the appropriate controllers. */ 1423 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 1424 if (avsdeHDs.size() > 0) 1425 { 1426 /* If in the next block an error occur we have to deregister 1427 the machine, so make an extra try/catch block. */ 1428 ComPtr<IHardDisk> srcHdVBox; 1429 bool fSourceHdNeedsClosing = false; 1430 1431 try 1432 { 1433 /* In order to attach hard disks we need to open a session 1434 * for the new machine */ 1435 rc = mVirtualBox->OpenSession(session, newMachineId_); 1436 if (FAILED(rc)) throw rc; 1437 fSessionOpen = true; 1438 1439 /* The disk image has to be on the same place as the OVF file. So 1440 * strip the filename out of the full file path. */ 1441 Utf8Str strSrcDir(pTask->locInfo.strPath); 1442 strSrcDir.stripFilename(); 1443 1444 /* Iterate over all given disk images */ 1445 list<VirtualSystemDescriptionEntry*>::const_iterator itHD; 1446 for (itHD = avsdeHDs.begin(); 1447 itHD != avsdeHDs.end(); 1448 ++itHD) 1449 { 1450 VirtualSystemDescriptionEntry *vsdeHD = *itHD; 1451 1452 const char *pcszDstFilePath = vsdeHD->strVbox.c_str(); 1453 /* Check if the destination file exists already or the 1454 * destination path is empty. */ 1455 if ( !(*pcszDstFilePath) 1456 || RTPathExists(pcszDstFilePath) 1457 ) 1458 /* This isn't allowed */ 1459 throw setError(VBOX_E_FILE_ERROR, 1460 tr("Destination file '%s' exists", 1461 pcszDstFilePath)); 1462 1463 /* Find the disk from the OVF's disk list */ 1464 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef); 1465 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist 1466 in the virtual system's disks map under that ID and also in the global images map. */ 1467 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef); 1468 1469 if ( itDiskImage == reader.m_mapDisks.end() 1470 || itVirtualDisk == vsysThis.mapVirtualDisks.end() 1471 ) 1472 throw setError(E_FAIL, 1473 tr("Internal inconsistency looking up disk images.")); 1474 1475 const DiskImage &di = itDiskImage->second; 1476 const VirtualDisk &vd = itVirtualDisk->second; 1477 1478 /* Make sure all target directories exists */ 1479 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath); 1480 if (FAILED(rc)) 1481 throw rc; 1482 1483 // subprogress object for hard disk 1484 ComPtr<IProgress> pProgress2; 1485 1486 ComPtr<IHardDisk> dstHdVBox; 1487 /* If strHref is empty we have to create a new file */ 1488 if (di.strHref.isEmpty()) 1489 { 1490 /* Which format to use? */ 1491 Bstr srcFormat = L"VDI"; 1492 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive) 1493 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)) 1494 srcFormat = L"VMDK"; 1495 /* Create an empty hard disk */ 1496 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam()); 1497 if (FAILED(rc)) throw rc; 1498 1499 /* Create a dynamic growing disk image with the given capacity */ 1500 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, HardDiskVariant_Standard, pProgress2.asOutParam()); 1501 if (FAILED(rc)) throw rc; 1502 1503 /* Advance to the next operation */ 1504 if (!pTask->progress.isNull()) 1505 pTask->progress->setNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath), 1506 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally 1507 } 1508 else 1509 { 1510 /* Construct the source file path */ 1511 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str()); 1512 /* Check if the source file exists */ 1513 if (!RTPathExists(strSrcFilePath.c_str())) 1514 /* This isn't allowed */ 1515 throw setError(VBOX_E_FILE_ERROR, 1516 tr("Source virtual disk image file '%s' doesn't exist"), 1517 strSrcFilePath.c_str()); 1518 1519 /* Clone the disk image (this is necessary cause the id has 1520 * to be recreated for the case the same hard disk is 1521 * attached already from a previous import) */ 1522 1523 /* First open the existing disk image */ 1524 rc = mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath), 1525 AccessMode_ReadOnly, 1526 false, Bstr(""), false, Bstr(""), 1527 srcHdVBox.asOutParam()); 1528 if (FAILED(rc)) throw rc; 1529 fSourceHdNeedsClosing = true; 1530 1531 /* We need the format description of the source disk image */ 1532 Bstr srcFormat; 1533 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam()); 1534 if (FAILED(rc)) throw rc; 1535 /* Create a new hard disk interface for the destination disk image */ 1536 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam()); 1537 if (FAILED(rc)) throw rc; 1538 /* Clone the source disk image */ 1539 rc = srcHdVBox->CloneTo(dstHdVBox, HardDiskVariant_Standard, NULL, pProgress2.asOutParam()); 1540 if (FAILED(rc)) throw rc; 1541 1542 /* Advance to the next operation */ 1543 if (!pTask->progress.isNull()) 1544 pTask->progress->setNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()), 1545 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally); 1546 } 1547 1548 // now wait for the background disk operation to complete; this throws HRESULTs on error 1549 waitForAsyncProgress(pTask->progress, pProgress2); 1550 1551 if (fSourceHdNeedsClosing) 1552 { 1553 rc = srcHdVBox->Close(); 1554 if (FAILED(rc)) throw rc; 1555 fSourceHdNeedsClosing = false; 1556 } 1557 1558 llHardDisksCreated.push_back(dstHdVBox); 1559 /* Now use the new uuid to attach the disk image to our new machine */ 1560 ComPtr<IMachine> sMachine; 1561 rc = session->COMGETTER(Machine)(sMachine.asOutParam()); 1562 if (FAILED(rc)) throw rc; 1563 Bstr hdId; 1564 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam()); 1565 if (FAILED(rc)) throw rc; 1566 1567 /* For now we assume we have one controller of every type only */ 1568 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second; 1569 1570 // this is for rollback later 1571 MyHardDiskAttachment mhda; 1572 mhda.uuid = newMachineId; 1573 mhda.pMachine = pNewMachine; 1574 1575 switch (hdc.system) 1576 { 1577 case HardDiskController::IDE: 1578 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary 1579 // or secondary IDE controller, respectively. For the primary controller of the IDE bus, 1580 // the device number can be either 0 or 1, to specify the master or the slave device, 1581 // respectively. For the secondary IDE controller, the device number is always 1 because 1582 // the master device is reserved for the CD-ROM drive. 1583 mhda.controllerType = Bstr("IDE"); 1584 switch (vd.ulAddressOnParent) 1585 { 1586 case 0: // interpret this as primary master 1587 mhda.lChannel = (long)0; 1588 mhda.lDevice = (long)0; 1589 break; 1590 1591 case 1: // interpret this as primary slave 1592 mhda.lChannel = (long)0; 1593 mhda.lDevice = (long)1; 1594 break; 1595 1596 case 2: // interpret this as secondary slave 1597 mhda.lChannel = (long)1; 1598 mhda.lDevice = (long)1; 1599 break; 1600 1601 default: 1602 throw setError(VBOX_E_NOT_SUPPORTED, 1603 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), vd.ulAddressOnParent); 1604 break; 1605 } 1606 break; 1607 1608 case HardDiskController::SATA: 1609 mhda.controllerType = Bstr("SATA"); 1610 mhda.lChannel = (long)vd.ulAddressOnParent; 1611 mhda.lDevice = (long)0; 1612 break; 1613 1614 case HardDiskController::SCSI: 1615 mhda.controllerType = Bstr("SCSI"); 1616 mhda.lChannel = (long)vd.ulAddressOnParent; 1617 mhda.lDevice = (long)0; 1618 break; 1619 1620 default: break; 1621 } 1622 1623 Log(("Attaching disk %s to channel %d on device %d\n", pcszDstFilePath, mhda.lChannel, mhda.lDevice)); 1624 1625 rc = sMachine->AttachHardDisk(hdId, 1626 mhda.controllerType, 1627 mhda.lChannel, 1628 mhda.lDevice); 1629 if (FAILED(rc)) throw rc; 1630 1631 llHardDiskAttachments.push_back(mhda); 1632 1633 rc = sMachine->SaveSettings(); 1634 if (FAILED(rc)) throw rc; 1635 } // end for (itHD = avsdeHDs.begin(); 1636 1637 // only now that we're done with all disks, close the session 1638 rc = session->Close(); 1639 if (FAILED(rc)) throw rc; 1640 fSessionOpen = false; 1641 } 1642 catch(HRESULT /* aRC */) 1643 { 1644 if (fSourceHdNeedsClosing) 1645 srcHdVBox->Close(); 1646 1647 if (fSessionOpen) 1648 session->Close(); 1649 1650 throw; 1651 } 1652 } 1653 } 1654 catch(HRESULT aRC) 1655 { 1656 rc = aRC; 1657 } 1658 1659 if (FAILED(rc)) 1660 break; 1661 1662 } // for (it = pAppliance->m->llVirtualSystems.begin(), 1663 1664 if (FAILED(rc)) 1665 { 1666 // with _whatever_ error we've had, do a complete roll-back of 1667 // machines and disks we've created; unfortunately this is 1668 // not so trivially done... 1669 1670 HRESULT rc2; 1671 // detach all hard disks from all machines we created 1672 list<MyHardDiskAttachment>::iterator itM; 1673 for (itM = llHardDiskAttachments.begin(); 1674 itM != llHardDiskAttachments.end(); 1675 ++itM) 1676 { 1677 const MyHardDiskAttachment &mhda = *itM; 1678 rc2 = mVirtualBox->OpenSession(session, Bstr(mhda.uuid)); 1679 if (SUCCEEDED(rc2)) 1680 { 1681 ComPtr<IMachine> sMachine; 1682 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam()); 1683 if (SUCCEEDED(rc2)) 1684 { 1685 rc2 = sMachine->DetachHardDisk(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice); 1686 rc2 = sMachine->SaveSettings(); 1687 } 1688 session->Close(); 1689 } 1690 } 1691 1692 // now clean up all hard disks we created 1693 list< ComPtr<IHardDisk> >::iterator itHD; 1694 for (itHD = llHardDisksCreated.begin(); 1695 itHD != llHardDisksCreated.end(); 1696 ++itHD) 1697 { 1698 ComPtr<IHardDisk> pDisk = *itHD; 1699 ComPtr<IProgress> pProgress; 1700 rc2 = pDisk->DeleteStorage(pProgress.asOutParam()); 1701 rc2 = pProgress->WaitForCompletion(-1); 1702 } 1703 1704 // finally, deregister and remove all machines 1705 list<Guid>::iterator itID; 1706 for (itID = llMachinesRegistered.begin(); 1707 itID != llMachinesRegistered.end(); 1708 ++itID) 1709 { 1710 const Guid &guid = *itID; 1711 ComPtr<IMachine> failedMachine; 1712 rc2 = mVirtualBox->UnregisterMachine(guid.toUtf16(), failedMachine.asOutParam()); 1713 if (SUCCEEDED(rc2)) 1714 rc2 = failedMachine->DeleteSettings(); 1715 } 1716 } 1717 1718 pTask->rc = rc; 1719 1720 if (!pTask->progress.isNull()) 1721 pTask->progress->notifyComplete(rc); 1722 1723 LogFlowFunc(("rc=%Rhrc\n", rc)); 1724 LogFlowFuncLeave(); 1725 1726 return VINF_SUCCESS; 1727 } 1728 1729 int Appliance::importS3(TaskImportOVF *pTask) 1730 { 1731 LogFlowFuncEnter(); 1732 LogFlowFunc(("Appliance %p\n", this)); 1733 1734 AutoCaller autoCaller(this); 1735 CheckComRCReturnRC(autoCaller.rc()); 1736 1737 AutoWriteLock appLock(this); 1738 1739 int vrc = VINF_SUCCESS; 1740 RTS3 hS3 = NIL_RTS3; 1741 char szOSTmpDir[RTPATH_MAX]; 1742 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir)); 1743 /* The template for the temporary directory created below */ 1744 char *pszTmpDir; 1745 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir); 1746 list< pair<Utf8Str, ULONG> > filesList; 1747 1748 HRESULT rc = S_OK; 1749 try 1750 { 1751 /* Extract the bucket */ 1752 Utf8Str tmpPath = pTask->locInfo.strPath; 1753 Utf8Str bucket; 1754 parseBucket(tmpPath, bucket); 1755 1756 /* We need a temporary directory which we can put the all disk images 1757 * in */ 1758 vrc = RTDirCreateTemp(pszTmpDir); 1759 if (RT_FAILURE(rc)) 1760 throw setError(VBOX_E_FILE_ERROR, 1761 tr("Cannot create temporary directory '%s'"), pszTmpDir); 1762 1763 /* Add every disks of every virtual system to an internal list */ 1764 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; 1765 for (it = m->virtualSystemDescriptions.begin(); 1766 it != m->virtualSystemDescriptions.end(); 1767 ++it) 1768 { 1769 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it); 1770 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 1771 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH; 1772 for (itH = avsdeHDs.begin(); 1773 itH != avsdeHDs.end(); 1774 ++itH) 1775 { 1776 const Utf8Str &strTargetFile = (*itH)->strOvf; 1777 if (!strTargetFile.isEmpty()) 1778 { 1779 /* The temporary name of the target disk file */ 1780 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile)); 1781 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB)); 1782 } 1783 } 1784 } 1785 1786 /* Next we have to download the disk images */ 1787 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING); 1788 if(RT_FAILURE(vrc)) 1789 throw setError(VBOX_E_IPRT_ERROR, 1790 tr("Cannot create S3 service handler")); 1791 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask); 1792 1793 /* Download all files */ 1794 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1) 1795 { 1796 const pair<Utf8Str, ULONG> &s = (*it1); 1797 const Utf8Str &strSrcFile = s.first; 1798 /* Construct the source file name */ 1799 char *pszFilename = RTPathFilename(strSrcFile.c_str()); 1800 /* Advance to the next operation */ 1801 if (!pTask->progress.isNull()) 1802 pTask->progress->setNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), s.second); 1803 1804 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str()); 1805 if (RT_FAILURE(vrc)) 1806 { 1807 if(vrc == VERR_S3_CANCELED) 1808 throw S_OK; /* todo: !!!!!!!!!!!!! */ 1809 else if(vrc == VERR_S3_ACCESS_DENIED) 1810 throw setError(E_ACCESSDENIED, 1811 tr("Cannot download file '%s' from S3 storage server (Access denied)"), pszFilename); 1812 else if(vrc == VERR_S3_NOT_FOUND) 1813 throw setError(VBOX_E_FILE_ERROR, 1814 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename); 1815 else 1816 throw setError(VBOX_E_IPRT_ERROR, 1817 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc); 1818 } 1819 } 1820 1821 /* Close the connection early */ 1822 RTS3Destroy(hS3); 1823 hS3 = NIL_RTS3; 1824 1825 if (!pTask->progress.isNull()) 1826 pTask->progress->setNextOperation(BstrFmt(tr("Importing appliance")), m->ulWeightPerOperation); 1827 1828 ComObjPtr<Progress> progress; 1829 /* Import the whole temporary OVF & the disk images */ 1830 LocationInfo li; 1831 /* Provide a OVF file (haven't to exist) so the import routine can 1832 * figure out where the disk images are located. */ 1833 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath)); 1834 li.strPath = strTmpOvf; 1835 rc = importImpl(li, progress); 1836 if (FAILED(rc)) throw rc; 1837 1838 /* Unlock the appliance for the fs import thread */ 1839 appLock.unlock(); 1840 /* Wait until the import is done, but report the progress back to the 1841 caller */ 1842 ComPtr<IProgress> progressInt(progress); 1843 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */ 1844 1845 /* Again lock the appliance for the next steps */ 1846 appLock.lock(); 1847 } 1848 catch(HRESULT aRC) 1849 { 1850 rc = aRC; 1851 } 1852 /* Cleanup */ 1853 RTS3Destroy(hS3); 1854 /* Delete all files which where temporary created */ 1855 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1) 1856 { 1857 const char *pszFilePath = (*it1).first.c_str(); 1858 if (RTPathExists(pszFilePath)) 1859 { 1860 vrc = RTFileDelete(pszFilePath); 1861 if(RT_FAILURE(vrc)) 1862 rc = setError(VBOX_E_FILE_ERROR, 1863 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc); 1864 } 1865 } 1866 /* Delete the temporary directory */ 1867 if (RTPathExists(pszTmpDir)) 1868 { 1869 vrc = RTDirRemove(pszTmpDir); 1870 if(RT_FAILURE(vrc)) 1871 rc = setError(VBOX_E_FILE_ERROR, 1872 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 1873 } 1874 if (pszTmpDir) 1875 RTStrFree(pszTmpDir); 1876 1877 pTask->rc = rc; 1878 1879 if (!pTask->progress.isNull()) 1880 pTask->progress->notifyComplete(rc); 1881 1882 LogFlowFunc(("rc=%Rhrc\n", rc)); 1883 LogFlowFuncLeave(); 1884 1885 return VINF_SUCCESS; 1886 } 1887 1888 HRESULT Appliance::writeImpl(int aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress) 1889 { 1890 HRESULT rc = S_OK; 1891 try 1892 { 1893 /* Initialize our worker task */ 1894 std::auto_ptr<TaskExportOVF> task(new TaskExportOVF(this)); 1895 /* What should the task do */ 1896 task->taskType = TaskExportOVF::Write; 1897 /* The OVF version to write */ 1898 task->enFormat = (TaskExportOVF::OVFFormat)aFormat; 1899 /* Copy the current location info to the task */ 1900 task->locInfo = aLocInfo; 1901 1902 Bstr progressDesc = BstrFmt(tr("Export appliance '%s'"), 1903 task->locInfo.strPath.c_str()); 1904 1905 /* todo: This progress init stuff should be done a little bit more generic */ 1906 if (task->locInfo.storageType == VFSType_File) 1907 rc = setUpProgressFS(aProgress, progressDesc); 1908 else 1909 rc = setUpProgressWriteS3(aProgress, progressDesc); 1910 1911 task->progress = aProgress; 1912 1913 rc = task->startThread(); 1914 CheckComRCThrowRC(rc); 1915 1916 /* Don't destruct on success */ 1917 task.release(); 1918 } 1919 catch (HRESULT aRC) 1920 { 1921 rc = aRC; 1922 } 1923 1924 return rc; 1925 } 1926 1927 DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser) 1928 { 1929 std::auto_ptr<TaskExportOVF> task(static_cast<TaskExportOVF*>(pvUser)); 1930 AssertReturn(task.get(), VERR_GENERAL_FAILURE); 1931 1932 Appliance *pAppliance = task->pAppliance; 1933 1934 LogFlowFuncEnter(); 1935 LogFlowFunc(("Appliance %p\n", pAppliance)); 1936 1937 HRESULT rc = S_OK; 1938 1939 switch(task->taskType) 1940 { 1941 case TaskExportOVF::Write: 1942 { 1943 if (task->locInfo.storageType == VFSType_File) 1944 rc = pAppliance->writeFS(task.get()); 1945 else if (task->locInfo.storageType == VFSType_S3) 1946 rc = pAppliance->writeS3(task.get()); 1947 break; 1948 } 1949 } 1950 1951 LogFlowFunc(("rc=%Rhrc\n", rc)); 1952 LogFlowFuncLeave(); 1953 1954 return VINF_SUCCESS; 1955 } 1956 1957 int Appliance::TaskExportOVF::startThread() 1958 { 1959 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this, 1960 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, 1961 "Appliance::Task"); 1962 1963 ComAssertMsgRCRet(vrc, 1964 ("Could not create taskThreadWriteOVF (%Rrc)\n", vrc), E_FAIL); 1965 1966 return S_OK; 1967 } 1968 1969 int Appliance::writeFS(TaskExportOVF *pTask) 1970 { 1971 LogFlowFuncEnter(); 1972 LogFlowFunc(("Appliance %p\n", this)); 1973 1974 AutoCaller autoCaller(this); 1975 CheckComRCReturnRC(autoCaller.rc()); 1976 1977 AutoWriteLock appLock(this); 1978 1979 HRESULT rc = S_OK; 1980 1981 try 1982 { 1983 xml::Document doc; 1984 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope"); 1985 1986 pelmRoot->setAttribute("ovf:version", (pTask->enFormat == TaskExportOVF::OVF_1_0) ? "1.0" : "0.9"); 1987 pelmRoot->setAttribute("xml:lang", "en-US"); 1988 1989 Utf8Str strNamespace = (pTask->enFormat == TaskExportOVF::OVF_0_9) 1990 ? "http://www.vmware.com/schema/ovf/1/envelope" // 0.9 1991 : "http://schemas.dmtf.org/ovf/envelope/1"; // 1.0 1992 pelmRoot->setAttribute("xmlns", strNamespace); 1993 pelmRoot->setAttribute("xmlns:ovf", strNamespace); 1994 1995 // pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1"); 1996 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"); 1997 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"); 1998 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); 1999 // pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd"); 2000 2001 // <Envelope>/<References> 2002 xml::ElementNode *pelmReferences = pelmRoot->createChild("References"); // 0.9 and 1.0 2003 2004 /* <Envelope>/<DiskSection>: 2005 <DiskSection> 2006 <Info>List of the virtual disks used in the package</Info> 2007 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/> 2008 </DiskSection> */ 2009 xml::ElementNode *pelmDiskSection; 2010 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2011 { 2012 // <Section xsi:type="ovf:DiskSection_Type"> 2013 pelmDiskSection = pelmRoot->createChild("Section"); 2014 pelmDiskSection->setAttribute("xsi:type", "ovf:DiskSection_Type"); 2015 } 2016 else 2017 pelmDiskSection = pelmRoot->createChild("DiskSection"); 2018 2019 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info"); 2020 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package"); 2021 // for now, set up a map so we have a list of unique disk names (to make 2022 // sure the same disk name is only added once) 2023 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks; 2024 2025 /* <Envelope>/<NetworkSection>: 2026 <NetworkSection> 2027 <Info>Logical networks used in the package</Info> 2028 <Network ovf:name="VM Network"> 2029 <Description>The network that the LAMP Service will be available on</Description> 2030 </Network> 2031 </NetworkSection> */ 2032 xml::ElementNode *pelmNetworkSection; 2033 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2034 { 2035 // <Section xsi:type="ovf:NetworkSection_Type"> 2036 pelmNetworkSection = pelmRoot->createChild("Section"); 2037 pelmNetworkSection->setAttribute("xsi:type", "ovf:NetworkSection_Type"); 2038 } 2039 else 2040 pelmNetworkSection = pelmRoot->createChild("NetworkSection"); 2041 2042 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info"); 2043 pelmNetworkSectionInfo->addContent("Logical networks used in the package"); 2044 // for now, set up a map so we have a list of unique network names (to make 2045 // sure the same network name is only added once) 2046 map<Utf8Str, bool> mapNetworks; 2047 // we fill this later below when we iterate over the networks 2048 2049 // and here come the virtual systems: 2050 2051 // write a collection if we have more than one virtual system _and_ we're 2052 // writing OVF 1.0; otherwise fail since ovftool can't import more than 2053 // one machine, it seems 2054 xml::ElementNode *pelmToAddVirtualSystemsTo; 2055 if (m->virtualSystemDescriptions.size() > 1) 2056 { 2057 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2058 throw setError(VBOX_E_FILE_ERROR, 2059 tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0")); 2060 2061 pelmToAddVirtualSystemsTo = pelmRoot->createChild("VirtualSystemCollection"); 2062 /* xml::AttributeNode *pattrVirtualSystemCollectionId = */ pelmToAddVirtualSystemsTo->setAttribute("ovf:name", "ExportedVirtualBoxMachines"); // whatever 2063 } 2064 else 2065 pelmToAddVirtualSystemsTo = pelmRoot; // add virtual system directly under root element 2066 2067 uint32_t cDisks = 0; 2068 2069 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; 2070 /* Iterate through all virtual systems of that appliance */ 2071 for (it = m->virtualSystemDescriptions.begin(); 2072 it != m->virtualSystemDescriptions.end(); 2073 ++it) 2074 { 2075 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it); 2076 2077 xml::ElementNode *pelmVirtualSystem; 2078 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2079 { 2080 // <Section xsi:type="ovf:NetworkSection_Type"> 2081 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("Content"); 2082 pelmVirtualSystem->setAttribute("xsi:type", "ovf:VirtualSystem_Type"); 2083 } 2084 else 2085 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("VirtualSystem"); 2086 2087 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine"); 2088 2089 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name); 2090 if (llName.size() != 1) 2091 throw setError(VBOX_E_NOT_SUPPORTED, 2092 tr("Missing VM name")); 2093 Utf8Str &strVMName = llName.front()->strVbox; 2094 pelmVirtualSystem->setAttribute("ovf:id", strVMName); 2095 2096 // product info 2097 std::list<VirtualSystemDescriptionEntry*> llProduct = vsdescThis->findByType(VirtualSystemDescriptionType_Product); 2098 std::list<VirtualSystemDescriptionEntry*> llProductUrl = vsdescThis->findByType(VirtualSystemDescriptionType_ProductUrl); 2099 std::list<VirtualSystemDescriptionEntry*> llVendor = vsdescThis->findByType(VirtualSystemDescriptionType_Vendor); 2100 std::list<VirtualSystemDescriptionEntry*> llVendorUrl = vsdescThis->findByType(VirtualSystemDescriptionType_VendorUrl); 2101 std::list<VirtualSystemDescriptionEntry*> llVersion = vsdescThis->findByType(VirtualSystemDescriptionType_Version); 2102 bool fProduct = llProduct.size() && !llProduct.front()->strVbox.isEmpty(); 2103 bool fProductUrl = llProductUrl.size() && !llProductUrl.front()->strVbox.isEmpty(); 2104 bool fVendor = llVendor.size() && !llVendor.front()->strVbox.isEmpty(); 2105 bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.front()->strVbox.isEmpty(); 2106 bool fVersion = llVersion.size() && !llVersion.front()->strVbox.isEmpty(); 2107 if (fProduct || 2108 fProductUrl || 2109 fVersion || 2110 fVendorUrl || 2111 fVersion) 2112 { 2113 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type"> 2114 <Info>Meta-information about the installed software</Info> 2115 <Product>VAtest</Product> 2116 <Vendor>SUN Microsystems</Vendor> 2117 <Version>10.0</Version> 2118 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl> 2119 <VendorUrl>http://www.sun.com</VendorUrl> 2120 </Section> */ 2121 xml::ElementNode *pelmAnnotationSection; 2122 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2123 { 2124 // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type"> 2125 pelmAnnotationSection = pelmVirtualSystem->createChild("Section"); 2126 pelmAnnotationSection->setAttribute("xsi:type", "ovf:ProductSection_Type"); 2127 } 2128 else 2129 pelmAnnotationSection = pelmVirtualSystem->createChild("ProductSection"); 2130 2131 pelmAnnotationSection->createChild("Info")->addContent("Meta-information about the installed software"); 2132 if (fProduct) 2133 pelmAnnotationSection->createChild("Product")->addContent(llProduct.front()->strVbox); 2134 if (fVendor) 2135 pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.front()->strVbox); 2136 if (fVersion) 2137 pelmAnnotationSection->createChild("Version")->addContent(llVersion.front()->strVbox); 2138 if (fProductUrl) 2139 pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.front()->strVbox); 2140 if (fVendorUrl) 2141 pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.front()->strVbox); 2142 } 2143 2144 // description 2145 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description); 2146 if (llDescription.size() && 2147 !llDescription.front()->strVbox.isEmpty()) 2148 { 2149 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type"> 2150 <Info>A human-readable annotation</Info> 2151 <Annotation>Plan 9</Annotation> 2152 </Section> */ 2153 xml::ElementNode *pelmAnnotationSection; 2154 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2155 { 2156 // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type"> 2157 pelmAnnotationSection = pelmVirtualSystem->createChild("Section"); 2158 pelmAnnotationSection->setAttribute("xsi:type", "ovf:AnnotationSection_Type"); 2159 } 2160 else 2161 pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection"); 2162 2163 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation"); 2164 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVbox); 2165 } 2166 2167 // license 2168 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License); 2169 if (llLicense.size() && 2170 !llLicense.front()->strVbox.isEmpty()) 2171 { 2172 /* <EulaSection> 2173 <Info ovf:msgid="6">License agreement for the Virtual System.</Info> 2174 <License ovf:msgid="1">License terms can go in here.</License> 2175 </EulaSection> */ 2176 xml::ElementNode *pelmEulaSection; 2177 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2178 { 2179 pelmEulaSection = pelmVirtualSystem->createChild("Section"); 2180 pelmEulaSection->setAttribute("xsi:type", "ovf:EulaSection_Type"); 2181 } 2182 else 2183 pelmEulaSection = pelmVirtualSystem->createChild("EulaSection"); 2184 2185 pelmEulaSection->createChild("Info")->addContent("License agreement for the virtual system"); 2186 pelmEulaSection->createChild("License")->addContent(llLicense.front()->strVbox); 2187 } 2188 2189 // operating system 2190 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS); 2191 if (llOS.size() != 1) 2192 throw setError(VBOX_E_NOT_SUPPORTED, 2193 tr("Missing OS type")); 2194 /* <OperatingSystemSection ovf:id="82"> 2195 <Info>Guest Operating System</Info> 2196 <Description>Linux 2.6.x</Description> 2197 </OperatingSystemSection> */ 2198 xml::ElementNode *pelmOperatingSystemSection; 2199 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2200 { 2201 pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section"); 2202 pelmOperatingSystemSection->setAttribute("xsi:type", "ovf:OperatingSystemSection_Type"); 2203 } 2204 else 2205 pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection"); 2206 2207 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf); 2208 pelmOperatingSystemSection->createChild("Info")->addContent("The kind of installed guest operating system"); 2209 Utf8Str strOSDesc; 2210 convertCIMOSType2VBoxOSType(strOSDesc, (CIMOSType_T)llOS.front()->strOvf.toInt32(), ""); 2211 pelmOperatingSystemSection->createChild("Description")->addContent(strOSDesc); 2212 2213 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso"> 2214 xml::ElementNode *pelmVirtualHardwareSection; 2215 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2216 { 2217 // <Section xsi:type="ovf:VirtualHardwareSection_Type"> 2218 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("Section"); 2219 pelmVirtualHardwareSection->setAttribute("xsi:type", "ovf:VirtualHardwareSection_Type"); 2220 } 2221 else 2222 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection"); 2223 2224 pelmVirtualHardwareSection->createChild("Info")->addContent("Virtual hardware requirements for a virtual machine"); 2225 2226 /* <System> 2227 <vssd:Description>Description of the virtual hardware section.</vssd:Description> 2228 <vssd:ElementName>vmware</vssd:ElementName> 2229 <vssd:InstanceID>1</vssd:InstanceID> 2230 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier> 2231 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType> 2232 </System> */ 2233 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System"); 2234 2235 pelmSystem->createChild("vssd:ElementName")->addContent("Virtual Hardware Family"); // required OVF 1.0 2236 2237 // <vssd:InstanceId>0</vssd:InstanceId> 2238 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2239 pelmSystem->createChild("vssd:InstanceId")->addContent("0"); 2240 else // capitalization changed... 2241 pelmSystem->createChild("vssd:InstanceID")->addContent("0"); 2242 2243 // <vssd:VirtualSystemIdentifier>VAtest</vssd:VirtualSystemIdentifier> 2244 pelmSystem->createChild("vssd:VirtualSystemIdentifier")->addContent(strVMName); 2245 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType> 2246 const char *pcszHardware = "virtualbox-2.2"; 2247 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2248 // pretend to be vmware compatible then 2249 pcszHardware = "vmx-6"; 2250 pelmSystem->createChild("vssd:VirtualSystemType")->addContent(pcszHardware); 2251 2252 // loop thru all description entries twice; once to write out all 2253 // devices _except_ disk images, and a second time to assign the 2254 // disk images; this is because disk images need to reference 2255 // IDE controllers, and we can't know their instance IDs without 2256 // assigning them first 2257 2258 uint32_t idIDEController = 0; 2259 int32_t lIDEControllerIndex = 0; 2260 uint32_t idSATAController = 0; 2261 int32_t lSATAControllerIndex = 0; 2262 uint32_t idSCSIController = 0; 2263 int32_t lSCSIControllerIndex = 0; 2264 2265 uint32_t ulInstanceID = 1; 2266 2267 for (size_t uLoop = 1; 2268 uLoop <= 2; 2269 ++uLoop) 2270 { 2271 int32_t lIndexThis = 0; 2272 list<VirtualSystemDescriptionEntry>::const_iterator itD; 2273 for (itD = vsdescThis->m->llDescriptions.begin(); 2274 itD != vsdescThis->m->llDescriptions.end(); 2275 ++itD, ++lIndexThis) 2276 { 2277 const VirtualSystemDescriptionEntry &desc = *itD; 2278 2279 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff 2280 Utf8Str strResourceSubType; 2281 2282 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block 2283 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block 2284 2285 uint32_t ulParent = 0; 2286 2287 int32_t lVirtualQuantity = -1; 2288 Utf8Str strAllocationUnits; 2289 2290 int32_t lAddress = -1; 2291 int32_t lBusNumber = -1; 2292 int32_t lAddressOnParent = -1; 2293 2294 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true" 2295 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block 2296 Utf8Str strHostResource; 2297 2298 uint64_t uTemp; 2299 2300 switch (desc.type) 2301 { 2302 case VirtualSystemDescriptionType_CPU: 2303 /* <Item> 2304 <rasd:Caption>1 virtual CPU</rasd:Caption> 2305 <rasd:Description>Number of virtual CPUs</rasd:Description> 2306 <rasd:ElementName>virtual CPU</rasd:ElementName> 2307 <rasd:InstanceID>1</rasd:InstanceID> 2308 <rasd:ResourceType>3</rasd:ResourceType> 2309 <rasd:VirtualQuantity>1</rasd:VirtualQuantity> 2310 </Item> */ 2311 if (uLoop == 1) 2312 { 2313 strDescription = "Number of virtual CPUs"; 2314 type = OVFResourceType_Processor; // 3 2315 desc.strVbox.toInt(uTemp); 2316 lVirtualQuantity = uTemp; 2317 strCaption = Utf8StrFmt("%d virtual CPU", lVirtualQuantity); // without this ovftool won't eat the item 2318 } 2319 break; 2320 2321 case VirtualSystemDescriptionType_Memory: 2322 /* <Item> 2323 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits> 2324 <rasd:Caption>256 MB of memory</rasd:Caption> 2325 <rasd:Description>Memory Size</rasd:Description> 2326 <rasd:ElementName>Memory</rasd:ElementName> 2327 <rasd:InstanceID>2</rasd:InstanceID> 2328 <rasd:ResourceType>4</rasd:ResourceType> 2329 <rasd:VirtualQuantity>256</rasd:VirtualQuantity> 2330 </Item> */ 2331 if (uLoop == 1) 2332 { 2333 strDescription = "Memory Size"; 2334 type = OVFResourceType_Memory; // 4 2335 desc.strVbox.toInt(uTemp); 2336 lVirtualQuantity = (int32_t)(uTemp / _1M); 2337 strAllocationUnits = "MegaBytes"; 2338 strCaption = Utf8StrFmt("%d MB of memory", lVirtualQuantity); // without this ovftool won't eat the item 2339 } 2340 break; 2341 2342 case VirtualSystemDescriptionType_HardDiskControllerIDE: 2343 /* <Item> 2344 <rasd:Caption>ideController1</rasd:Caption> 2345 <rasd:Description>IDE Controller</rasd:Description> 2346 <rasd:InstanceId>5</rasd:InstanceId> 2347 <rasd:ResourceType>5</rasd:ResourceType> 2348 <rasd:Address>1</rasd:Address> 2349 <rasd:BusNumber>1</rasd:BusNumber> 2350 </Item> */ 2351 if (uLoop == 1) 2352 { 2353 strDescription = "IDE Controller"; 2354 strCaption = "ideController0"; 2355 type = OVFResourceType_IDEController; // 5 2356 strResourceSubType = desc.strVbox; 2357 // it seems that OVFTool always writes these two, and since we can only 2358 // have one IDE controller, we'll use this as well 2359 lAddress = 1; 2360 lBusNumber = 1; 2361 2362 // remember this ID 2363 idIDEController = ulInstanceID; 2364 lIDEControllerIndex = lIndexThis; 2365 } 2366 break; 2367 2368 case VirtualSystemDescriptionType_HardDiskControllerSATA: 2369 /* <Item> 2370 <rasd:Caption>sataController0</rasd:Caption> 2371 <rasd:Description>SATA Controller</rasd:Description> 2372 <rasd:InstanceId>4</rasd:InstanceId> 2373 <rasd:ResourceType>20</rasd:ResourceType> 2374 <rasd:ResourceSubType>ahci</rasd:ResourceSubType> 2375 <rasd:Address>0</rasd:Address> 2376 <rasd:BusNumber>0</rasd:BusNumber> 2377 </Item> 2378 */ 2379 if (uLoop == 1) 2380 { 2381 strDescription = "SATA Controller"; 2382 strCaption = "sataController0"; 2383 type = OVFResourceType_OtherStorageDevice; // 20 2384 // it seems that OVFTool always writes these two, and since we can only 2385 // have one SATA controller, we'll use this as well 2386 lAddress = 0; 2387 lBusNumber = 0; 2388 2389 if ( desc.strVbox.isEmpty() // AHCI is the default in VirtualBox 2390 || (!desc.strVbox.compare("ahci", Utf8Str::CaseInsensitive)) 2391 ) 2392 strResourceSubType = "AHCI"; 2393 else 2394 throw setError(VBOX_E_NOT_SUPPORTED, 2395 tr("Invalid config string \"%s\" in SATA controller"), desc.strVbox.c_str()); 2396 2397 // remember this ID 2398 idSATAController = ulInstanceID; 2399 lSATAControllerIndex = lIndexThis; 2400 } 2401 break; 2402 2403 case VirtualSystemDescriptionType_HardDiskControllerSCSI: 2404 /* <Item> 2405 <rasd:Caption>scsiController0</rasd:Caption> 2406 <rasd:Description>SCSI Controller</rasd:Description> 2407 <rasd:InstanceId>4</rasd:InstanceId> 2408 <rasd:ResourceType>6</rasd:ResourceType> 2409 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType> 2410 <rasd:Address>0</rasd:Address> 2411 <rasd:BusNumber>0</rasd:BusNumber> 2412 </Item> 2413 */ 2414 if (uLoop == 1) 2415 { 2416 strDescription = "SCSI Controller"; 2417 strCaption = "scsiController0"; 2418 type = OVFResourceType_ParallelSCSIHBA; // 6 2419 // it seems that OVFTool always writes these two, and since we can only 2420 // have one SATA controller, we'll use this as well 2421 lAddress = 0; 2422 lBusNumber = 0; 2423 2424 if ( desc.strVbox.isEmpty() // LsiLogic is the default in VirtualBox 2425 || (!desc.strVbox.compare("lsilogic", Utf8Str::CaseInsensitive)) 2426 ) 2427 strResourceSubType = "lsilogic"; 2428 else if (!desc.strVbox.compare("buslogic", Utf8Str::CaseInsensitive)) 2429 strResourceSubType = "buslogic"; 2430 else 2431 throw setError(VBOX_E_NOT_SUPPORTED, 2432 tr("Invalid config string \"%s\" in SCSI controller"), desc.strVbox.c_str()); 2433 2434 // remember this ID 2435 idSCSIController = ulInstanceID; 2436 lSCSIControllerIndex = lIndexThis; 2437 } 2438 break; 2439 2440 case VirtualSystemDescriptionType_HardDiskImage: 2441 /* <Item> 2442 <rasd:Caption>disk1</rasd:Caption> 2443 <rasd:InstanceId>8</rasd:InstanceId> 2444 <rasd:ResourceType>17</rasd:ResourceType> 2445 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource> 2446 <rasd:Parent>4</rasd:Parent> 2447 <rasd:AddressOnParent>0</rasd:AddressOnParent> 2448 </Item> */ 2449 if (uLoop == 2) 2450 { 2451 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks); 2452 2453 strDescription = "Disk Image"; 2454 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else 2455 type = OVFResourceType_HardDisk; // 17 2456 2457 // the following references the "<Disks>" XML block 2458 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str()); 2459 2460 // controller=<index>;channel=<c> 2461 size_t pos1 = desc.strExtraConfig.find("controller="); 2462 size_t pos2 = desc.strExtraConfig.find("channel="); 2463 if (pos1 != Utf8Str::npos) 2464 { 2465 int32_t lControllerIndex = -1; 2466 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos1 + 11, NULL, 0, &lControllerIndex); 2467 if (lControllerIndex == lIDEControllerIndex) 2468 ulParent = idIDEController; 2469 else if (lControllerIndex == lSCSIControllerIndex) 2470 ulParent = idSCSIController; 2471 else if (lControllerIndex == lSATAControllerIndex) 2472 ulParent = idSATAController; 2473 } 2474 if (pos2 != Utf8Str::npos) 2475 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent); 2476 2477 if ( !ulParent 2478 || lAddressOnParent == -1 2479 ) 2480 throw setError(VBOX_E_NOT_SUPPORTED, 2481 tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfig.c_str()); 2482 2483 mapDisks[strDiskID] = &desc; 2484 } 2485 break; 2486 2487 case VirtualSystemDescriptionType_Floppy: 2488 if (uLoop == 1) 2489 { 2490 strDescription = "Floppy Drive"; 2491 strCaption = "floppy0"; // this is what OVFTool writes 2492 type = OVFResourceType_FloppyDrive; // 14 2493 lAutomaticAllocation = 0; 2494 lAddressOnParent = 0; // this is what OVFTool writes 2495 } 2496 break; 2497 2498 case VirtualSystemDescriptionType_CDROM: 2499 if (uLoop == 2) 2500 { 2501 // we can't have a CD without an IDE controller 2502 if (!idIDEController) 2503 throw setError(VBOX_E_NOT_SUPPORTED, 2504 tr("Can't have CD-ROM without IDE controller")); 2505 2506 strDescription = "CD-ROM Drive"; 2507 strCaption = "cdrom1"; // this is what OVFTool writes 2508 type = OVFResourceType_CDDrive; // 15 2509 lAutomaticAllocation = 1; 2510 ulParent = idIDEController; 2511 lAddressOnParent = 0; // this is what OVFTool writes 2512 } 2513 break; 2514 2515 case VirtualSystemDescriptionType_NetworkAdapter: 2516 /* <Item> 2517 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation> 2518 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption> 2519 <rasd:Connection>VM Network</rasd:Connection> 2520 <rasd:ElementName>VM network</rasd:ElementName> 2521 <rasd:InstanceID>3</rasd:InstanceID> 2522 <rasd:ResourceType>10</rasd:ResourceType> 2523 </Item> */ 2524 if (uLoop == 1) 2525 { 2526 lAutomaticAllocation = 1; 2527 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str()); 2528 type = OVFResourceType_EthernetAdapter; // 10 2529 /* Set the hardware type to something useful. 2530 * To be compatible with vmware & others we set 2531 * PCNet32 for our PCNet types & E1000 for the 2532 * E1000 cards. */ 2533 switch (desc.strVbox.toInt32()) 2534 { 2535 case NetworkAdapterType_Am79C970A: 2536 case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break; 2537 #ifdef VBOX_WITH_E1000 2538 case NetworkAdapterType_I82540EM: 2539 case NetworkAdapterType_I82545EM: 2540 case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break; 2541 #endif /* VBOX_WITH_E1000 */ 2542 } 2543 strConnection = desc.strOvf; 2544 2545 mapNetworks[desc.strOvf] = true; 2546 } 2547 break; 2548 2549 case VirtualSystemDescriptionType_USBController: 2550 /* <Item ovf:required="false"> 2551 <rasd:Caption>usb</rasd:Caption> 2552 <rasd:Description>USB Controller</rasd:Description> 2553 <rasd:InstanceId>3</rasd:InstanceId> 2554 <rasd:ResourceType>23</rasd:ResourceType> 2555 <rasd:Address>0</rasd:Address> 2556 <rasd:BusNumber>0</rasd:BusNumber> 2557 </Item> */ 2558 if (uLoop == 1) 2559 { 2560 strDescription = "USB Controller"; 2561 strCaption = "usb"; 2562 type = OVFResourceType_USBController; // 23 2563 lAddress = 0; // this is what OVFTool writes 2564 lBusNumber = 0; // this is what OVFTool writes 2565 } 2566 break; 2567 2568 case VirtualSystemDescriptionType_SoundCard: 2569 /* <Item ovf:required="false"> 2570 <rasd:Caption>sound</rasd:Caption> 2571 <rasd:Description>Sound Card</rasd:Description> 2572 <rasd:InstanceId>10</rasd:InstanceId> 2573 <rasd:ResourceType>35</rasd:ResourceType> 2574 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType> 2575 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation> 2576 <rasd:AddressOnParent>3</rasd:AddressOnParent> 2577 </Item> */ 2578 if (uLoop == 1) 2579 { 2580 strDescription = "Sound Card"; 2581 strCaption = "sound"; 2582 type = OVFResourceType_SoundCard; // 35 2583 strResourceSubType = desc.strOvf; // e.g. ensoniq1371 2584 lAutomaticAllocation = 0; 2585 lAddressOnParent = 3; // what gives? this is what OVFTool writes 2586 } 2587 break; 2588 } 2589 2590 if (type) 2591 { 2592 xml::ElementNode *pItem; 2593 2594 pItem = pelmVirtualHardwareSection->createChild("Item"); 2595 2596 // NOTE: do not change the order of these items without good reason! While we don't care 2597 // about ordering, VMware's ovftool does and fails if the items are not written in 2598 // exactly this order, as stupid as it seems. 2599 2600 if (!strCaption.isEmpty()) 2601 { 2602 pItem->createChild("rasd:Caption")->addContent(strCaption); 2603 if (pTask->enFormat == TaskExportOVF::OVF_1_0) 2604 pItem->createChild("rasd:ElementName")->addContent(strCaption); 2605 } 2606 2607 if (!strDescription.isEmpty()) 2608 pItem->createChild("rasd:Description")->addContent(strDescription); 2609 2610 // <rasd:InstanceID>1</rasd:InstanceID> 2611 xml::ElementNode *pelmInstanceID; 2612 if (pTask->enFormat == TaskExportOVF::OVF_0_9) 2613 pelmInstanceID = pItem->createChild("rasd:InstanceId"); 2614 else 2615 pelmInstanceID = pItem->createChild("rasd:InstanceID"); // capitalization changed... 2616 pelmInstanceID->addContent(Utf8StrFmt("%d", ulInstanceID++)); 2617 2618 // <rasd:ResourceType>3</rasd:ResourceType> 2619 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type)); 2620 if (!strResourceSubType.isEmpty()) 2621 pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType); 2622 2623 if (!strHostResource.isEmpty()) 2624 pItem->createChild("rasd:HostResource")->addContent(strHostResource); 2625 2626 if (!strAllocationUnits.isEmpty()) 2627 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits); 2628 2629 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity> 2630 if (lVirtualQuantity != -1) 2631 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity)); 2632 2633 if (lAutomaticAllocation != -1) 2634 pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" ); 2635 2636 if (!strConnection.isEmpty()) 2637 pItem->createChild("rasd:Connection")->addContent(strConnection); 2638 2639 if (lAddress != -1) 2640 pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress)); 2641 2642 if (lBusNumber != -1) 2643 if (pTask->enFormat == TaskExportOVF::OVF_0_9) // BusNumber is invalid OVF 1.0 so only write it in 0.9 mode for OVFTool compatibility 2644 pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber)); 2645 2646 if (ulParent) 2647 pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent)); 2648 if (lAddressOnParent != -1) 2649 pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent)); 2650 } 2651 } 2652 } // for (size_t uLoop = 0; ... 2653 } 2654 2655 // finally, fill in the network section we set up empty above according 2656 // to the networks we found with the hardware items 2657 map<Utf8Str, bool>::const_iterator itN; 2658 for (itN = mapNetworks.begin(); 2659 itN != mapNetworks.end(); 2660 ++itN) 2661 { 2662 const Utf8Str &strNetwork = itN->first; 2663 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network"); 2664 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str()); 2665 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance."); 2666 } 2667 2668 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS; 2669 uint32_t ulFile = 1; 2670 for (itS = mapDisks.begin(); 2671 itS != mapDisks.end(); 2672 ++itS) 2673 { 2674 const Utf8Str &strDiskID = itS->first; 2675 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second; 2676 2677 // source path: where the VBox image is 2678 const Utf8Str &strSrcFilePath = pDiskEntry->strVbox; 2679 Bstr bstrSrcFilePath(strSrcFilePath); 2680 if (!RTPathExists(strSrcFilePath.c_str())) 2681 /* This isn't allowed */ 2682 throw setError(VBOX_E_FILE_ERROR, 2683 tr("Source virtual disk image file '%s' doesn't exist"), 2684 strSrcFilePath.c_str()); 2685 2686 // output filename 2687 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf; 2688 // target path needs to be composed from where the output OVF is 2689 Utf8Str strTargetFilePath(pTask->locInfo.strPath); 2690 strTargetFilePath.stripFilename(); 2691 strTargetFilePath.append("/"); 2692 strTargetFilePath.append(strTargetFileNameOnly); 2693 2694 // clone the disk: 2695 ComPtr<IHardDisk> pSourceDisk; 2696 ComPtr<IHardDisk> pTargetDisk; 2697 ComPtr<IProgress> pProgress2; 2698 2699 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw())); 2700 rc = mVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam()); 2701 if (FAILED(rc)) throw rc; 2702 2703 /* We are always exporting to vmdfk stream optimized for now */ 2704 Bstr bstrSrcFormat = L"VMDK"; 2705 2706 // create a new hard disk interface for the destination disk image 2707 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw())); 2708 rc = mVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam()); 2709 if (FAILED(rc)) throw rc; 2710 2711 // the target disk is now registered and needs to be removed again, 2712 // both after successful cloning or if anything goes bad! 2713 try 2714 { 2715 // create a flat copy of the source disk image 2716 rc = pSourceDisk->CloneTo(pTargetDisk, HardDiskVariant_VmdkStreamOptimized, NULL, pProgress2.asOutParam()); 2717 if (FAILED(rc)) throw rc; 2718 2719 // advance to the next operation 2720 if (!pTask->progress.isNull()) 2721 pTask->progress->setNextOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()), 2722 pDiskEntry->ulSizeMB); // operation's weight, as set up with the IProgress originally); 2723 2724 // now wait for the background disk operation to complete; this throws HRESULTs on error 2725 waitForAsyncProgress(pTask->progress, pProgress2); 2726 } 2727 catch (HRESULT rc3) 2728 { 2729 // upon error after registering, close the disk or 2730 // it'll stick in the registry forever 2731 pTargetDisk->Close(); 2732 throw; 2733 } 2734 2735 // we need the following for the XML 2736 uint64_t cbFile = 0; // actual file size 2737 rc = pTargetDisk->COMGETTER(Size)(&cbFile); 2738 if (FAILED(rc)) throw rc; 2739 2740 ULONG64 cbCapacity = 0; // size reported to guest 2741 rc = pTargetDisk->COMGETTER(LogicalSize)(&cbCapacity); 2742 if (FAILED(rc)) throw rc; 2743 // capacity is reported in megabytes, so... 2744 cbCapacity *= _1M; 2745 2746 // upon success, close the disk as well 2747 rc = pTargetDisk->Close(); 2748 if (FAILED(rc)) throw rc; 2749 2750 // now handle the XML for the disk: 2751 Utf8StrFmt strFileRef("file%RI32", ulFile++); 2752 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/> 2753 xml::ElementNode *pelmFile = pelmReferences->createChild("File"); 2754 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly); 2755 pelmFile->setAttribute("ovf:id", strFileRef); 2756 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str()); 2757 2758 // add disk to XML Disks section 2759 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/> 2760 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk"); 2761 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str()); 2762 pelmDisk->setAttribute("ovf:diskId", strDiskID); 2763 pelmDisk->setAttribute("ovf:fileRef", strFileRef); 2764 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#sparse"); // must be sparse or ovftool chokes 2765 } 2766 2767 // now go write the XML 2768 xml::XmlFileWriter writer(doc); 2769 writer.write(pTask->locInfo.strPath.c_str()); 2770 } 2771 catch(xml::Error &x) 2772 { 2773 rc = setError(VBOX_E_FILE_ERROR, 2774 x.what()); 2775 } 2776 catch(HRESULT aRC) 2777 { 2778 rc = aRC; 2779 } 2780 2781 pTask->rc = rc; 2782 2783 if (!pTask->progress.isNull()) 2784 pTask->progress->notifyComplete(rc); 2785 2786 LogFlowFunc(("rc=%Rhrc\n", rc)); 2787 LogFlowFuncLeave(); 2788 2789 return VINF_SUCCESS; 2790 } 2791 2792 int Appliance::writeS3(TaskExportOVF *pTask) 2793 { 2794 LogFlowFuncEnter(); 2795 LogFlowFunc(("Appliance %p\n", this)); 2796 2797 AutoCaller autoCaller(this); 2798 CheckComRCReturnRC(autoCaller.rc()); 2799 2800 HRESULT rc = S_OK; 2801 2802 AutoWriteLock appLock(this); 2803 2804 int vrc = VINF_SUCCESS; 2805 RTS3 hS3 = NIL_RTS3; 2806 char szOSTmpDir[RTPATH_MAX]; 2807 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir)); 2808 /* The template for the temporary directory created below */ 2809 char *pszTmpDir; 2810 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir); 2811 list< pair<Utf8Str, ULONG> > filesList; 2812 2813 // todo: 2814 // - usable error codes 2815 // - seems snapshot filenames are problematic {uuid}.vdi 2816 try 2817 { 2818 /* Extract the bucket */ 2819 Utf8Str tmpPath = pTask->locInfo.strPath; 2820 Utf8Str bucket; 2821 parseBucket(tmpPath, bucket); 2822 2823 /* We need a temporary directory which we can put the OVF file & all 2824 * disk images in */ 2825 vrc = RTDirCreateTemp(pszTmpDir); 2826 if (RT_FAILURE(rc)) 2827 throw setError(VBOX_E_FILE_ERROR, 2828 tr("Cannot create temporary directory '%s'"), pszTmpDir); 2829 2830 /* The temporary name of the target OVF file */ 2831 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath)); 2832 2833 /* Prepare the temporary writing of the OVF */ 2834 ComObjPtr<Progress> progress; 2835 /* Create a temporary file based location info for the sub task */ 2836 LocationInfo li; 2837 li.strPath = strTmpOvf; 2838 rc = writeImpl(pTask->enFormat, li, progress); 2839 if (FAILED(rc)) throw rc; 2840 2841 /* Unlock the appliance for the writing thread */ 2842 appLock.unlock(); 2843 /* Wait until the writing is done, but report the progress back to the 2844 caller */ 2845 ComPtr<IProgress> progressInt(progress); 2846 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */ 2847 2848 /* Again lock the appliance for the next steps */ 2849 appLock.lock(); 2850 2851 vrc = RTPathExists(strTmpOvf.c_str()); /* Paranoid check */ 2852 if(RT_FAILURE(vrc)) 2853 throw setError(VBOX_E_FILE_ERROR, 2854 tr("Cannot find source file '%s'"), strTmpOvf.c_str()); 2855 /* Add the OVF file */ 2856 filesList.push_back(pair<Utf8Str, ULONG>(strTmpOvf, m->ulWeightPerOperation)); /* Use 1% of the total for the OVF file upload */ 2857 2858 /* Now add every disks of every virtual system */ 2859 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; 2860 for (it = m->virtualSystemDescriptions.begin(); 2861 it != m->virtualSystemDescriptions.end(); 2862 ++it) 2863 { 2864 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it); 2865 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 2866 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH; 2867 for (itH = avsdeHDs.begin(); 2868 itH != avsdeHDs.end(); 2869 ++itH) 2870 { 2871 const Utf8Str &strTargetFileNameOnly = (*itH)->strOvf; 2872 /* Target path needs to be composed from where the output OVF is */ 2873 Utf8Str strTargetFilePath(strTmpOvf); 2874 strTargetFilePath.stripFilename(); 2875 strTargetFilePath.append("/"); 2876 strTargetFilePath.append(strTargetFileNameOnly); 2877 vrc = RTPathExists(strTargetFilePath.c_str()); /* Paranoid check */ 2878 if(RT_FAILURE(vrc)) 2879 throw setError(VBOX_E_FILE_ERROR, 2880 tr("Cannot find source file '%s'"), strTargetFilePath.c_str()); 2881 filesList.push_back(pair<Utf8Str, ULONG>(strTargetFilePath, (*itH)->ulSizeMB)); 2882 } 2883 } 2884 /* Next we have to upload the OVF & all disk images */ 2885 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING); 2886 if(RT_FAILURE(vrc)) 2887 throw setError(VBOX_E_IPRT_ERROR, 2888 tr("Cannot create S3 service handler")); 2889 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask); 2890 2891 /* Upload all files */ 2892 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1) 2893 { 2894 const pair<Utf8Str, ULONG> &s = (*it1); 2895 char *pszFilename = RTPathFilename(s.first.c_str()); 2896 /* Advance to the next operation */ 2897 if (!pTask->progress.isNull()) 2898 pTask->progress->setNextOperation(BstrFmt(tr("Uploading file '%s'"), pszFilename), s.second); 2899 vrc = RTS3PutKey(hS3, bucket.c_str(), pszFilename, s.first.c_str()); 2900 if (RT_FAILURE(vrc)) 2901 { 2902 if(vrc == VERR_S3_CANCELED) 2903 break; 2904 else if(vrc == VERR_S3_ACCESS_DENIED) 2905 throw setError(E_ACCESSDENIED, 2906 tr("Cannot upload file '%s' to S3 storage server (Access denied)"), pszFilename); 2907 else if(vrc == VERR_S3_NOT_FOUND) 2908 throw setError(VBOX_E_FILE_ERROR, 2909 tr("Cannot upload file '%s' to S3 storage server (File not found)"), pszFilename); 2910 else 2911 throw setError(VBOX_E_IPRT_ERROR, 2912 tr("Cannot upload file '%s' to S3 storage server (%Rrc)"), pszFilename, vrc); 2913 } 2914 } 2915 } 2916 catch(HRESULT aRC) 2917 { 2918 rc = aRC; 2919 } 2920 /* Cleanup */ 2921 RTS3Destroy(hS3); 2922 /* Delete all files which where temporary created */ 2923 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1) 2924 { 2925 const char *pszFilePath = (*it1).first.c_str(); 2926 if (RTPathExists(pszFilePath)) 2927 { 2928 vrc = RTFileDelete(pszFilePath); 2929 if(RT_FAILURE(vrc)) 2930 rc = setError(VBOX_E_FILE_ERROR, 2931 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc); 2932 } 2933 } 2934 /* Delete the temporary directory */ 2935 if (RTPathExists(pszTmpDir)) 2936 { 2937 vrc = RTDirRemove(pszTmpDir); 2938 if(RT_FAILURE(vrc)) 2939 rc = setError(VBOX_E_FILE_ERROR, 2940 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 2941 } 2942 if (pszTmpDir) 2943 RTStrFree(pszTmpDir); 2944 2945 pTask->rc = rc; 2946 2947 if (!pTask->progress.isNull()) 2948 pTask->progress->notifyComplete(rc); 2949 2950 LogFlowFunc(("rc=%Rhrc\n", rc)); 2951 LogFlowFuncLeave(); 2952 2953 return VINF_SUCCESS; 2954 } 2955 2956 //////////////////////////////////////////////////////////////////////////////// 2957 // 341 2958 // IAppliance public methods 342 2959 // … … 358 2975 AutoReadLock alock(this); 359 2976 360 if (m->pReader) // OVFReader instantiated? 361 { 362 Bstr bstrPath(m->pReader->m_strPath); 363 bstrPath.cloneTo(aPath); 364 } 365 else 366 Bstr("").cloneTo(aPath); 2977 Bstr bstrPath(m->locInfo.strPath); 2978 bstrPath.cloneTo(aPath); 367 2979 368 2980 return S_OK; … … 391 3003 size_t i = 0; 392 3004 for (it = m->pReader->m_mapDisks.begin(); 393 it != m->pReader->m_mapDisks.end();394 ++it, ++i)3005 it != m->pReader->m_mapDisks.end(); 3006 ++it, ++i) 395 3007 { 396 3008 // create a string representing this disk … … 398 3010 char *psz = NULL; 399 3011 RTStrAPrintf(&psz, 400 "%s\t"401 "%RI64\t"402 "%RI64\t"403 "%s\t"404 "%s\t"405 "%RI64\t"406 "%RI64\t"407 "%s",408 d.strDiskId.c_str(),409 d.iCapacity,410 d.iPopulatedSize,411 d.strFormat.c_str(),412 d.strHref.c_str(),413 d.iSize,414 d.iChunkSize,415 d.strCompression.c_str());3012 "%s\t" 3013 "%RI64\t" 3014 "%RI64\t" 3015 "%s\t" 3016 "%s\t" 3017 "%RI64\t" 3018 "%RI64\t" 3019 "%s", 3020 d.strDiskId.c_str(), 3021 d.iCapacity, 3022 d.iPopulatedSize, 3023 d.strFormat.c_str(), 3024 d.strHref.c_str(), 3025 d.iSize, 3026 d.iChunkSize, 3027 d.strCompression.c_str()); 416 3028 Utf8Str utf(psz); 417 3029 Bstr bstr(utf); … … 452 3064 * @return 453 3065 */ 454 STDMETHODIMP Appliance::Read(IN_BSTR path )455 { 456 if (!path) 457 return E_POINTER;3066 STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress) 3067 { 3068 if (!path) return E_POINTER; 3069 CheckComArgOutPointerValid(aProgress); 458 3070 459 3071 AutoCaller autoCaller(this); … … 463 3075 464 3076 if (m->pReader) 465 { 466 delete m->pReader; 467 m->pReader = NULL; 468 } 469 470 Utf8Str strPath(path); 3077 { 3078 delete m->pReader; 3079 m->pReader = NULL; 3080 } 3081 471 3082 // see if we can handle this file; for now we insist it has an ".ovf" extension 3083 Utf8Str strPath (path); 472 3084 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)) 473 3085 return setError(VBOX_E_FILE_ERROR, 474 3086 tr("Appliance file must have .ovf extension")); 475 3087 3088 ComObjPtr<Progress> progress; 3089 HRESULT rc = S_OK; 476 3090 try 477 3091 { 478 m->pReader = new OVFReader(strPath); 479 } 480 catch(xml::Error &x) 481 { 482 return setError(VBOX_E_FILE_ERROR, 483 x.what()); 484 } 3092 /* Parse all necessary info out of the URI */ 3093 parseURI(strPath, m->locInfo); 3094 rc = readImpl(m->locInfo, progress); 3095 } 3096 catch (HRESULT aRC) 3097 { 3098 rc = aRC; 3099 } 3100 3101 if (SUCCEEDED(rc)) 3102 /* Return progress to the caller */ 3103 progress.queryInterfaceTo(aProgress); 485 3104 486 3105 return S_OK; … … 936 3555 } 937 3556 938 struct Appliance::TaskImportMachines939 {940 TaskImportMachines(Appliance *aThat, Progress *aProgress)941 : pAppliance(aThat)942 , progress(aProgress)943 , rc(S_OK)944 {}945 ~TaskImportMachines() {}946 947 HRESULT startThread();948 949 Appliance *pAppliance;950 ComObjPtr<Progress> progress;951 HRESULT rc;952 };953 954 HRESULT Appliance::TaskImportMachines::startThread()955 {956 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportMachines, this,957 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,958 "Appliance::Task");959 ComAssertMsgRCRet(vrc,960 ("Could not create taskThreadImportMachines (%Rrc)\n", vrc), E_FAIL);961 962 return S_OK;963 }964 965 3557 /** 966 3558 * Public method implementation. … … 977 3569 AutoReadLock(this); 978 3570 979 HRESULT rc = S_OK;980 981 3571 if (!m->pReader) 982 3572 return setError(E_FAIL, … … 984 3574 985 3575 ComObjPtr<Progress> progress; 3576 HRESULT rc = S_OK; 986 3577 try 987 3578 { 988 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"), 989 m->pReader->m_strPath.raw()); 990 rc = setUpProgress(progress, progressDesc); 991 if (FAILED(rc)) throw rc; 992 993 /* Initialize our worker task */ 994 std::auto_ptr<TaskImportMachines> task(new TaskImportMachines(this, progress)); 995 //AssertComRCThrowRC (task->autoCaller.rc()); 996 997 rc = task->startThread(); 998 if (FAILED(rc)) throw rc; 999 1000 task.release(); 3579 rc = importImpl(m->locInfo, progress); 1001 3580 } 1002 3581 catch (HRESULT aRC) … … 1012 3591 } 1013 3592 1014 struct MyHardDiskAttachment1015 {1016 Guid uuid;1017 ComPtr<IMachine> pMachine;1018 Bstr controllerType;1019 int32_t lChannel;1020 int32_t lDevice;1021 };1022 1023 /**1024 * Worker thread implementation for ImportMachines().1025 * @param aThread1026 * @param pvUser1027 */1028 /* static */1029 DECLCALLBACK(int) Appliance::taskThreadImportMachines(RTTHREAD /* aThread */, void *pvUser)1030 {1031 std::auto_ptr<TaskImportMachines> task(static_cast<TaskImportMachines*>(pvUser));1032 AssertReturn(task.get(), VERR_GENERAL_FAILURE);1033 1034 Appliance *pAppliance = task->pAppliance;1035 1036 LogFlowFuncEnter();1037 LogFlowFunc(("Appliance %p\n", pAppliance));1038 1039 AutoCaller autoCaller(pAppliance);1040 CheckComRCReturnRC(autoCaller.rc());1041 1042 AutoWriteLock appLock(pAppliance);1043 1044 HRESULT rc = S_OK;1045 1046 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);1047 1048 // rollback for errors:1049 // a list of images that we created/imported1050 list<MyHardDiskAttachment> llHardDiskAttachments;1051 list< ComPtr<IHardDisk> > llHardDisksCreated;1052 list<Guid> llMachinesRegistered;1053 1054 ComPtr<ISession> session;1055 bool fSessionOpen = false;1056 rc = session.createInprocObject(CLSID_Session);1057 CheckComRCReturnRC(rc);1058 1059 const OVFReader reader = *pAppliance->m->pReader;1060 // this is safe to access because this thread only gets started1061 // if pReader != NULL1062 1063 list<VirtualSystem>::const_iterator it;1064 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;1065 /* Iterate through all virtual systems of that appliance */1066 size_t i = 0;1067 for (it = reader.m_llVirtualSystems.begin(),1068 it1 = pAppliance->m->virtualSystemDescriptions.begin();1069 it != reader.m_llVirtualSystems.end();1070 ++it, ++it1, ++i)1071 {1072 const VirtualSystem &vsysThis = *it;1073 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);1074 1075 ComPtr<IMachine> pNewMachine;1076 1077 /* Catch possible errors */1078 try1079 {1080 /* Guest OS type */1081 std::list<VirtualSystemDescriptionEntry*> vsdeOS;1082 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);1083 if (vsdeOS.size() < 1)1084 throw setError(VBOX_E_FILE_ERROR,1085 tr("Missing guest OS type"));1086 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;1087 1088 /* Now that we know the base system get our internal defaults based on that. */1089 ComPtr<IGuestOSType> osType;1090 rc = pVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());1091 if (FAILED(rc)) throw rc;1092 1093 /* Create the machine */1094 /* First get the name */1095 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);1096 if (vsdeName.size() < 1)1097 throw setError(VBOX_E_FILE_ERROR,1098 tr("Missing VM name"));1099 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;1100 rc = pVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),1101 Bstr(), Bstr(),1102 pNewMachine.asOutParam());1103 if (FAILED(rc)) throw rc;1104 1105 // and the description1106 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);1107 if (vsdeDescription.size())1108 {1109 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;1110 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));1111 if (FAILED(rc)) throw rc;1112 }1113 1114 /* CPU count */1115 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType (VirtualSystemDescriptionType_CPU);1116 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL);1117 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox;1118 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str());1119 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount);1120 if (FAILED(rc)) throw rc;1121 bool fEnableIOApic = false;1122 /* We need HWVirt & IO-APIC if more than one CPU is requested */1123 if (tmpCount > 1)1124 {1125 rc = pNewMachine->COMSETTER(HWVirtExEnabled)(TRUE);1126 if (FAILED(rc)) throw rc;1127 1128 fEnableIOApic = true;1129 }1130 1131 /* RAM */1132 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);1133 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);1134 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;1135 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());1136 rc = pNewMachine->COMSETTER(MemorySize)(tt);1137 if (FAILED(rc)) throw rc;1138 1139 /* VRAM */1140 /* Get the recommended VRAM for this guest OS type */1141 ULONG vramVBox;1142 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);1143 if (FAILED(rc)) throw rc;1144 1145 /* Set the VRAM */1146 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);1147 if (FAILED(rc)) throw rc;1148 1149 /* I/O APIC: so far we have no setting for this. Enable it if we1150 import a Windows VM because if if Windows was installed without IOAPIC,1151 it will not mind finding an one later on, but if Windows was installed1152 _with_ an IOAPIC, it will bluescreen if it's not found */1153 Bstr bstrFamilyId;1154 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());1155 if (FAILED(rc)) throw rc;1156 1157 Utf8Str strFamilyId(bstrFamilyId);1158 if (strFamilyId == "Windows")1159 fEnableIOApic = true;1160 1161 /* If IP-APIC should be enabled could be have different reasons.1162 See CPU count & the Win test above. Here we enable it if it was1163 previously requested. */1164 if (fEnableIOApic)1165 {1166 ComPtr<IBIOSSettings> pBIOSSettings;1167 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());1168 if (FAILED(rc)) throw rc;1169 1170 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);1171 if (FAILED(rc)) throw rc;1172 }1173 1174 /* Audio Adapter */1175 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);1176 /* @todo: we support one audio adapter only */1177 if (vsdeAudioAdapter.size() > 0)1178 {1179 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;1180 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)1181 {1182 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());1183 ComPtr<IAudioAdapter> audioAdapter;1184 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());1185 if (FAILED(rc)) throw rc;1186 rc = audioAdapter->COMSETTER(Enabled)(true);1187 if (FAILED(rc)) throw rc;1188 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));1189 if (FAILED(rc)) throw rc;1190 }1191 }1192 1193 #ifdef VBOX_WITH_USB1194 /* USB Controller */1195 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);1196 // USB support is enabled if there's at least one such entry; to disable USB support,1197 // the type of the USB item would have been changed to "ignore"1198 bool fUSBEnabled = vsdeUSBController.size() > 0;1199 1200 ComPtr<IUSBController> usbController;1201 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());1202 if (FAILED(rc)) throw rc;1203 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);1204 if (FAILED(rc)) throw rc;1205 #endif /* VBOX_WITH_USB */1206 1207 /* Change the network adapters */1208 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);1209 if (vsdeNW.size() == 0)1210 {1211 /* No network adapters, so we have to disable our default one */1212 ComPtr<INetworkAdapter> nwVBox;1213 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());1214 if (FAILED(rc)) throw rc;1215 rc = nwVBox->COMSETTER(Enabled)(false);1216 if (FAILED(rc)) throw rc;1217 }1218 else1219 {1220 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;1221 /* Iterate through all network cards. We support 8 network adapters1222 * at the maximum. (@todo: warn if there are more!) */1223 size_t a = 0;1224 for (nwIt = vsdeNW.begin();1225 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);1226 ++nwIt, ++a)1227 {1228 const VirtualSystemDescriptionEntry* pvsys = *nwIt;1229 1230 const Utf8Str &nwTypeVBox = pvsys->strVbox;1231 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());1232 ComPtr<INetworkAdapter> pNetworkAdapter;1233 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());1234 if (FAILED(rc)) throw rc;1235 /* Enable the network card & set the adapter type */1236 rc = pNetworkAdapter->COMSETTER(Enabled)(true);1237 if (FAILED(rc)) throw rc;1238 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));1239 if (FAILED(rc)) throw rc;1240 1241 // default is NAT; change to "bridged" if extra conf says so1242 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))1243 {1244 /* Attach to the right interface */1245 rc = pNetworkAdapter->AttachToBridgedInterface();1246 if (FAILED(rc)) throw rc;1247 ComPtr<IHost> host;1248 rc = pVirtualBox->COMGETTER(Host)(host.asOutParam());1249 if (FAILED(rc)) throw rc;1250 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;1251 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));1252 if (FAILED(rc)) throw rc;1253 /* We search for the first host network interface which1254 * is usable for bridged networking */1255 for (size_t i=0; i < nwInterfaces.size(); ++i)1256 {1257 HostNetworkInterfaceType_T itype;1258 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);1259 if (FAILED(rc)) throw rc;1260 if (itype == HostNetworkInterfaceType_Bridged)1261 {1262 Bstr name;1263 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());1264 if (FAILED(rc)) throw rc;1265 /* Set the interface name to attach to */1266 pNetworkAdapter->COMSETTER(HostInterface)(name);1267 if (FAILED(rc)) throw rc;1268 break;1269 }1270 }1271 }1272 /* Next test for host only interfaces */1273 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))1274 {1275 /* Attach to the right interface */1276 rc = pNetworkAdapter->AttachToHostOnlyInterface();1277 if (FAILED(rc)) throw rc;1278 ComPtr<IHost> host;1279 rc = pVirtualBox->COMGETTER(Host)(host.asOutParam());1280 if (FAILED(rc)) throw rc;1281 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;1282 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));1283 if (FAILED(rc)) throw rc;1284 /* We search for the first host network interface which1285 * is usable for host only networking */1286 for (size_t i=0; i < nwInterfaces.size(); ++i)1287 {1288 HostNetworkInterfaceType_T itype;1289 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);1290 if (FAILED(rc)) throw rc;1291 if (itype == HostNetworkInterfaceType_HostOnly)1292 {1293 Bstr name;1294 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());1295 if (FAILED(rc)) throw rc;1296 /* Set the interface name to attach to */1297 pNetworkAdapter->COMSETTER(HostInterface)(name);1298 if (FAILED(rc)) throw rc;1299 break;1300 }1301 }1302 }1303 }1304 }1305 1306 /* Floppy drive */1307 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);1308 // Floppy support is enabled if there's at least one such entry; to disable floppy support,1309 // the type of the floppy item would have been changed to "ignore"1310 bool fFloppyEnabled = vsdeFloppy.size() > 0;1311 ComPtr<IFloppyDrive> floppyDrive;1312 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());1313 if (FAILED(rc)) throw rc;1314 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);1315 if (FAILED(rc)) throw rc;1316 1317 /* CDROM drive */1318 /* @todo: I can't disable the CDROM. So nothing to do for now */1319 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);1320 1321 /* Hard disk controller IDE */1322 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);1323 if (vsdeHDCIDE.size() > 1)1324 throw setError(VBOX_E_FILE_ERROR,1325 tr("Too many IDE controllers in OVF; VirtualBox only supports one"));1326 if (vsdeHDCIDE.size() == 1)1327 {1328 ComPtr<IStorageController> pController;1329 rc = pNewMachine->GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());1330 if (FAILED(rc)) throw rc;1331 1332 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();1333 if (!strcmp(pcszIDEType, "PIIX3"))1334 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);1335 else if (!strcmp(pcszIDEType, "PIIX4"))1336 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);1337 else if (!strcmp(pcszIDEType, "ICH6"))1338 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);1339 else1340 throw setError(VBOX_E_FILE_ERROR,1341 tr("Invalid IDE controller type \"%s\""),1342 pcszIDEType);1343 if (FAILED(rc)) throw rc;1344 }1345 #ifdef VBOX_WITH_AHCI1346 /* Hard disk controller SATA */1347 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);1348 if (vsdeHDCSATA.size() > 1)1349 throw setError(VBOX_E_FILE_ERROR,1350 tr("Too many SATA controllers in OVF; VirtualBox only supports one"));1351 if (vsdeHDCSATA.size() > 0)1352 {1353 ComPtr<IStorageController> pController;1354 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;1355 if (hdcVBox == "AHCI")1356 {1357 rc = pNewMachine->AddStorageController(Bstr("SATA"), StorageBus_SATA, pController.asOutParam());1358 if (FAILED(rc)) throw rc;1359 }1360 else1361 throw setError(VBOX_E_FILE_ERROR,1362 tr("Invalid SATA controller type \"%s\""),1363 hdcVBox.c_str());1364 }1365 #endif /* VBOX_WITH_AHCI */1366 1367 #ifdef VBOX_WITH_LSILOGIC1368 /* Hard disk controller SCSI */1369 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);1370 if (vsdeHDCSCSI.size() > 1)1371 throw setError(VBOX_E_FILE_ERROR,1372 tr("Too many SCSI controllers in OVF; VirtualBox only supports one"));1373 if (vsdeHDCSCSI.size() > 0)1374 {1375 ComPtr<IStorageController> pController;1376 StorageControllerType_T controllerType;1377 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;1378 if (hdcVBox == "LsiLogic")1379 controllerType = StorageControllerType_LsiLogic;1380 else if (hdcVBox == "BusLogic")1381 controllerType = StorageControllerType_BusLogic;1382 else1383 throw setError(VBOX_E_FILE_ERROR,1384 tr("Invalid SCSI controller type \"%s\""),1385 hdcVBox.c_str());1386 1387 rc = pNewMachine->AddStorageController(Bstr("SCSI"), StorageBus_SCSI, pController.asOutParam());1388 if (FAILED(rc)) throw rc;1389 rc = pController->COMSETTER(ControllerType)(controllerType);1390 if (FAILED(rc)) throw rc;1391 }1392 #endif /* VBOX_WITH_LSILOGIC */1393 1394 /* Now its time to register the machine before we add any hard disks */1395 rc = pVirtualBox->RegisterMachine(pNewMachine);1396 if (FAILED(rc)) throw rc;1397 1398 Bstr newMachineId_;1399 rc = pNewMachine->COMGETTER(Id)(newMachineId_.asOutParam());1400 if (FAILED(rc)) throw rc;1401 Guid newMachineId(newMachineId_);1402 1403 // store new machine for roll-back in case of errors1404 llMachinesRegistered.push_back(newMachineId);1405 1406 /* Create the hard disks & connect them to the appropriate controllers. */1407 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);1408 if (avsdeHDs.size() > 0)1409 {1410 /* If in the next block an error occur we have to deregister1411 the machine, so make an extra try/catch block. */1412 ComPtr<IHardDisk> srcHdVBox;1413 bool fSourceHdNeedsClosing = false;1414 1415 try1416 {1417 /* In order to attach hard disks we need to open a session1418 * for the new machine */1419 rc = pVirtualBox->OpenSession(session, newMachineId_);1420 if (FAILED(rc)) throw rc;1421 fSessionOpen = true;1422 1423 /* The disk image has to be on the same place as the OVF file. So1424 * strip the filename out of the full file path. */1425 Utf8Str strSrcDir(reader.m_strPath);1426 strSrcDir.stripFilename();1427 1428 /* Iterate over all given disk images */1429 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;1430 for (itHD = avsdeHDs.begin();1431 itHD != avsdeHDs.end();1432 ++itHD)1433 {1434 VirtualSystemDescriptionEntry *vsdeHD = *itHD;1435 1436 const char *pcszDstFilePath = vsdeHD->strVbox.c_str();1437 /* Check if the destination file exists already or the1438 * destination path is empty. */1439 if ( !(*pcszDstFilePath)1440 || RTPathExists(pcszDstFilePath)1441 )1442 /* This isn't allowed */1443 throw setError(VBOX_E_FILE_ERROR,1444 tr("Destination file '%s' exists",1445 pcszDstFilePath));1446 1447 /* Find the disk from the OVF's disk list */1448 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);1449 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist1450 in the virtual system's disks map under that ID and also in the global images map. */1451 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);1452 1453 if ( itDiskImage == reader.m_mapDisks.end()1454 || itVirtualDisk == vsysThis.mapVirtualDisks.end()1455 )1456 throw setError(E_FAIL,1457 tr("Internal inconsistency looking up disk images."));1458 1459 const DiskImage &di = itDiskImage->second;1460 const VirtualDisk &vd = itVirtualDisk->second;1461 1462 /* Make sure all target directories exists */1463 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);1464 if (FAILED(rc))1465 throw rc;1466 1467 // subprogress object for hard disk1468 ComPtr<IProgress> pProgress2;1469 1470 ComPtr<IHardDisk> dstHdVBox;1471 /* If strHref is empty we have to create a new file */1472 if (di.strHref.isEmpty())1473 {1474 /* Which format to use? */1475 Bstr srcFormat = L"VDI";1476 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)1477 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))1478 srcFormat = L"VMDK";1479 /* Create an empty hard disk */1480 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());1481 if (FAILED(rc)) throw rc;1482 1483 /* Create a dynamic growing disk image with the given capacity */1484 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, HardDiskVariant_Standard, pProgress2.asOutParam());1485 if (FAILED(rc)) throw rc;1486 1487 /* Advance to the next operation */1488 if (!task->progress.isNull())1489 task->progress->setNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath),1490 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally1491 }1492 else1493 {1494 /* Construct the source file path */1495 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());1496 /* Check if the source file exists */1497 if (!RTPathExists(strSrcFilePath.c_str()))1498 /* This isn't allowed */1499 throw setError(VBOX_E_FILE_ERROR,1500 tr("Source virtual disk image file '%s' doesn't exist"),1501 strSrcFilePath.c_str());1502 1503 /* Clone the disk image (this is necessary cause the id has1504 * to be recreated for the case the same hard disk is1505 * attached already from a previous import) */1506 1507 /* First open the existing disk image */1508 rc = pVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),1509 AccessMode_ReadOnly,1510 false, Bstr(""), false, Bstr(""),1511 srcHdVBox.asOutParam());1512 if (FAILED(rc)) throw rc;1513 fSourceHdNeedsClosing = true;1514 1515 /* We need the format description of the source disk image */1516 Bstr srcFormat;1517 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());1518 if (FAILED(rc)) throw rc;1519 /* Create a new hard disk interface for the destination disk image */1520 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());1521 if (FAILED(rc)) throw rc;1522 /* Clone the source disk image */1523 rc = srcHdVBox->CloneTo(dstHdVBox, HardDiskVariant_Standard, NULL, pProgress2.asOutParam());1524 if (FAILED(rc)) throw rc;1525 1526 /* Advance to the next operation */1527 if (!task->progress.isNull())1528 task->progress->setNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),1529 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally);1530 }1531 1532 // now wait for the background disk operation to complete; this throws HRESULTs on error1533 pAppliance->waitForAsyncProgress(task->progress, pProgress2);1534 1535 if (fSourceHdNeedsClosing)1536 {1537 rc = srcHdVBox->Close();1538 if (FAILED(rc)) throw rc;1539 fSourceHdNeedsClosing = false;1540 }1541 1542 llHardDisksCreated.push_back(dstHdVBox);1543 /* Now use the new uuid to attach the disk image to our new machine */1544 ComPtr<IMachine> sMachine;1545 rc = session->COMGETTER(Machine)(sMachine.asOutParam());1546 if (FAILED(rc)) throw rc;1547 Bstr hdId;1548 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());1549 if (FAILED(rc)) throw rc;1550 1551 /* For now we assume we have one controller of every type only */1552 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;1553 1554 // this is for rollback later1555 MyHardDiskAttachment mhda;1556 mhda.uuid = newMachineId;1557 mhda.pMachine = pNewMachine;1558 1559 switch (hdc.system)1560 {1561 case HardDiskController::IDE:1562 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary1563 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,1564 // the device number can be either 0 or 1, to specify the master or the slave device,1565 // respectively. For the secondary IDE controller, the device number is always 1 because1566 // the master device is reserved for the CD-ROM drive.1567 mhda.controllerType = Bstr("IDE");1568 switch (vd.ulAddressOnParent)1569 {1570 case 0: // interpret this as primary master1571 mhda.lChannel = (long)0;1572 mhda.lDevice = (long)0;1573 break;1574 1575 case 1: // interpret this as primary slave1576 mhda.lChannel = (long)0;1577 mhda.lDevice = (long)1;1578 break;1579 1580 case 2: // interpret this as secondary slave1581 mhda.lChannel = (long)1;1582 mhda.lDevice = (long)1;1583 break;1584 1585 default:1586 throw setError(VBOX_E_NOT_SUPPORTED,1587 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), vd.ulAddressOnParent);1588 break;1589 }1590 break;1591 1592 case HardDiskController::SATA:1593 mhda.controllerType = Bstr("SATA");1594 mhda.lChannel = (long)vd.ulAddressOnParent;1595 mhda.lDevice = (long)0;1596 break;1597 1598 case HardDiskController::SCSI:1599 mhda.controllerType = Bstr("SCSI");1600 mhda.lChannel = (long)vd.ulAddressOnParent;1601 mhda.lDevice = (long)0;1602 break;1603 1604 default: break;1605 }1606 1607 Log(("Attaching disk %s to channel %d on device %d\n", pcszDstFilePath, mhda.lChannel, mhda.lDevice));1608 1609 rc = sMachine->AttachHardDisk(hdId,1610 mhda.controllerType,1611 mhda.lChannel,1612 mhda.lDevice);1613 if (FAILED(rc)) throw rc;1614 1615 llHardDiskAttachments.push_back(mhda);1616 1617 rc = sMachine->SaveSettings();1618 if (FAILED(rc)) throw rc;1619 } // end for (itHD = avsdeHDs.begin();1620 1621 // only now that we're done with all disks, close the session1622 rc = session->Close();1623 if (FAILED(rc)) throw rc;1624 fSessionOpen = false;1625 }1626 catch(HRESULT /* aRC */)1627 {1628 if (fSourceHdNeedsClosing)1629 srcHdVBox->Close();1630 1631 if (fSessionOpen)1632 session->Close();1633 1634 throw;1635 }1636 }1637 }1638 catch(HRESULT aRC)1639 {1640 rc = aRC;1641 }1642 1643 if (FAILED(rc))1644 break;1645 1646 } // for (it = pAppliance->m->llVirtualSystems.begin(),1647 1648 if (FAILED(rc))1649 {1650 // with _whatever_ error we've had, do a complete roll-back of1651 // machines and disks we've created; unfortunately this is1652 // not so trivially done...1653 1654 HRESULT rc2;1655 // detach all hard disks from all machines we created1656 list<MyHardDiskAttachment>::iterator itM;1657 for (itM = llHardDiskAttachments.begin();1658 itM != llHardDiskAttachments.end();1659 ++itM)1660 {1661 const MyHardDiskAttachment &mhda = *itM;1662 rc2 = pVirtualBox->OpenSession(session, Bstr(mhda.uuid));1663 if (SUCCEEDED(rc2))1664 {1665 ComPtr<IMachine> sMachine;1666 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());1667 if (SUCCEEDED(rc2))1668 {1669 rc2 = sMachine->DetachHardDisk(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);1670 rc2 = sMachine->SaveSettings();1671 }1672 session->Close();1673 }1674 }1675 1676 // now clean up all hard disks we created1677 list< ComPtr<IHardDisk> >::iterator itHD;1678 for (itHD = llHardDisksCreated.begin();1679 itHD != llHardDisksCreated.end();1680 ++itHD)1681 {1682 ComPtr<IHardDisk> pDisk = *itHD;1683 ComPtr<IProgress> pProgress;1684 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());1685 rc2 = pProgress->WaitForCompletion(-1);1686 }1687 1688 // finally, deregister and remove all machines1689 list<Guid>::iterator itID;1690 for (itID = llMachinesRegistered.begin();1691 itID != llMachinesRegistered.end();1692 ++itID)1693 {1694 const Guid &guid = *itID;1695 ComPtr<IMachine> failedMachine;1696 rc2 = pVirtualBox->UnregisterMachine(guid.toUtf16(), failedMachine.asOutParam());1697 if (SUCCEEDED(rc2))1698 rc2 = failedMachine->DeleteSettings();1699 }1700 }1701 1702 task->rc = rc;1703 1704 if (!task->progress.isNull())1705 task->progress->notifyComplete(rc);1706 1707 LogFlowFunc(("rc=%Rhrc\n", rc));1708 LogFlowFuncLeave();1709 1710 return VINF_SUCCESS;1711 }1712 1713 struct Appliance::TaskWriteOVF1714 {1715 enum OVFFormat1716 {1717 unspecified,1718 OVF_0_9,1719 OVF_1_01720 };1721 enum TaskType1722 {1723 Write1724 };1725 1726 TaskWriteOVF(const Utf8Str &aFile,1727 OVFFormat aFormat,1728 Appliance *aThat)1729 : strOutputFile(aFile),1730 taskType(Write),1731 storageType(VFSType_File),1732 enFormat(aFormat),1733 pAppliance(aThat),1734 rc(S_OK)1735 {}1736 ~TaskWriteOVF() {}1737 1738 int startThread();1739 static int uploadProgress(unsigned uPercent, void *pvUser);1740 1741 Utf8Str strOutputFile;1742 TaskType taskType;1743 VFSType_T storageType;1744 Utf8Str filepath;1745 Utf8Str hostname;1746 Utf8Str username;1747 Utf8Str password;1748 OVFFormat enFormat;1749 Appliance *pAppliance;1750 ComObjPtr<Progress> progress;1751 HRESULT rc;1752 };1753 1754 int Appliance::TaskWriteOVF::startThread()1755 {1756 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,1757 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,1758 "Appliance::Task");1759 1760 ComAssertMsgRCRet(vrc,1761 ("Could not create taskThreadWriteOVF (%Rrc)\n", vrc), E_FAIL);1762 1763 return S_OK;1764 }1765 1766 /* static */1767 int Appliance::TaskWriteOVF::uploadProgress(unsigned uPercent, void *pvUser)1768 {1769 Appliance::TaskWriteOVF* pTask = *(Appliance::TaskWriteOVF**)pvUser;1770 1771 if (pTask &&1772 !pTask->progress.isNull())1773 {1774 BOOL fCanceled;1775 pTask->progress->COMGETTER(Canceled)(&fCanceled);1776 if (fCanceled)1777 return -1;1778 pTask->progress->setCurrentOperationProgress(uPercent);1779 }1780 return VINF_SUCCESS;1781 }1782 1783 3593 STDMETHODIMP Appliance::CreateVFSExplorer(IN_BSTR aURI, IVFSExplorer **aExplorer) 1784 3594 { 1785 HRESULT rc = S_OK;1786 1787 3595 CheckComArgOutPointerValid(aExplorer); 1788 3596 … … 1792 3600 AutoReadLock(this); 1793 3601 1794 Utf8Str uri(aURI);1795 /* Check which kind of export the user has requested */1796 VFSType_T type = VFSType_File;1797 Utf8Str strProtocol = "";1798 /* Check the URI for the target format */1799 if (uri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */1800 {1801 throw E_NOTIMPL;1802 // type = VFSType_S3;1803 // strProtocol = "SunCloud://";1804 }1805 else if (uri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */1806 {1807 throw E_NOTIMPL;1808 // type = VFSType_S3;1809 // strProtocol = "S3://";1810 }1811 else if (uri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */1812 throw E_NOTIMPL;1813 1814 Utf8Str strFilepath;1815 Utf8Str strHostname;1816 Utf8Str strUsername;1817 Utf8Str strPassword;1818 parseURI(uri, strProtocol, strFilepath, strHostname, strUsername, strPassword);1819 1820 3602 ComObjPtr<VFSExplorer> explorer; 1821 explorer.createObject(); 1822 1823 rc = explorer->init(type, strFilepath, strHostname, strUsername, strPassword, mVirtualBox); 3603 HRESULT rc = S_OK; 3604 try 3605 { 3606 Utf8Str uri(aURI); 3607 /* Check which kind of export the user has requested */ 3608 LocationInfo li; 3609 parseURI(uri, li); 3610 /* Create the explorer object */ 3611 explorer.createObject(); 3612 rc = explorer->init(li.storageType, li.strPath, li.strHostname, li.strUsername, li.strPassword, mVirtualBox); 3613 } 3614 catch (HRESULT aRC) 3615 { 3616 rc = aRC; 3617 } 1824 3618 1825 3619 if (SUCCEEDED(rc)) … … 1832 3626 STDMETHODIMP Appliance::Write(IN_BSTR format, IN_BSTR path, IProgress **aProgress) 1833 3627 { 1834 HRESULT rc = S_OK; 1835 3628 if (!path) return E_POINTER; 1836 3629 CheckComArgOutPointerValid(aProgress); 1837 3630 … … 1847 3640 tr("Appliance file must have .ovf extension")); 1848 3641 1849 ComObjPtr<Progress> progress;1850 3642 Utf8Str strFormat(format); 1851 Task WriteOVF::OVFFormat ovfF;3643 TaskExportOVF::OVFFormat ovfF; 1852 3644 if (strFormat == "ovf-0.9") 1853 ovfF = Task WriteOVF::OVF_0_9;3645 ovfF = TaskExportOVF::OVF_0_9; 1854 3646 else if (strFormat == "ovf-1.0") 1855 ovfF = Task WriteOVF::OVF_1_0;3647 ovfF = TaskExportOVF::OVF_1_0; 1856 3648 else 1857 3649 return setError(VBOX_E_FILE_ERROR, 1858 3650 tr("Invalid format \"%s\" specified"), strFormat.c_str()); 1859 3651 1860 rc = writeImpl(ovfF, strPath, progress); 3652 ComObjPtr<Progress> progress; 3653 HRESULT rc = S_OK; 3654 try 3655 { 3656 /* Parse all necessary info out of the URI */ 3657 parseURI(strPath, m->locInfo); 3658 rc = writeImpl(ovfF, m->locInfo, progress); 3659 } 3660 catch (HRESULT aRC) 3661 { 3662 rc = aRC; 3663 } 1861 3664 1862 3665 if (SUCCEEDED(rc)) … … 1865 3668 1866 3669 return rc; 1867 }1868 1869 void Appliance::parseURI(Utf8Str strUri, const Utf8Str &strProtocol, Utf8Str &strFilepath, Utf8Str &strHostname, Utf8Str &strUsername, Utf8Str &strPassword)1870 {1871 /* Remove the protocol */1872 if (strUri.startsWith(strProtocol, Utf8Str::CaseInsensitive))1873 strUri = strUri.substr(strProtocol.length());1874 size_t uppos = strUri.find("@");1875 if (uppos != Utf8Str::npos)1876 {1877 strUsername = strUri.substr(0, uppos);1878 strUri = strUri.substr(uppos + 1);1879 size_t upos = strUsername.find(":");1880 if (upos != Utf8Str::npos)1881 {1882 strPassword = strUsername.substr(upos + 1);1883 strUsername = strUsername.substr(0, upos);1884 }1885 }1886 size_t hpos = strUri.find("/");1887 if (hpos != Utf8Str::npos)1888 {1889 strHostname = strUri.substr(0, hpos);1890 strUri = strUri.substr(hpos);1891 }1892 strFilepath = strUri;1893 }1894 1895 HRESULT Appliance::writeImpl(int aFormat,1896 const Utf8Str &aPath,1897 ComObjPtr<Progress> &aProgress)1898 {1899 HRESULT rc = S_OK;1900 try1901 {1902 // m->strPath = aPath;1903 1904 /* Initialize our worker task */1905 std::auto_ptr<TaskWriteOVF> task(new TaskWriteOVF(aPath,1906 (TaskWriteOVF::OVFFormat)aFormat,1907 this));1908 1909 /* Check which kind of export the user has requested */1910 Utf8Str strProtocol = "";1911 /* Check the URI for the target format */1912 if (aPath.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */1913 {1914 task->storageType = VFSType_S3;1915 strProtocol = "SunCloud://";1916 }1917 else if (aPath.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */1918 {1919 task->storageType = VFSType_S3;1920 strProtocol = "S3://";1921 }1922 else if (aPath.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */1923 throw E_NOTIMPL;1924 1925 parseURI(aPath, strProtocol, task->filepath, task->hostname, task->username, task->password);1926 Bstr progressDesc = BstrFmt(tr("Export appliance '%s'"),1927 task->filepath.c_str());1928 1929 /* todo: This progress init stuff should be done a little bit more generic */1930 if (task->storageType == VFSType_S3)1931 rc = setUpProgressUpload(aProgress, progressDesc);1932 else1933 rc = setUpProgress(aProgress, progressDesc);1934 if (FAILED(rc)) throw rc;1935 1936 task->progress = aProgress;1937 1938 rc = task->startThread();1939 CheckComRCThrowRC(rc);1940 1941 /* Don't destruct on success */1942 task.release();1943 }1944 catch (HRESULT aRC)1945 {1946 rc = aRC;1947 }1948 1949 return rc;1950 }1951 1952 DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)1953 {1954 std::auto_ptr<TaskWriteOVF> task(static_cast<TaskWriteOVF*>(pvUser));1955 AssertReturn(task.get(), VERR_GENERAL_FAILURE);1956 1957 Appliance *pAppliance = task->pAppliance;1958 1959 LogFlowFuncEnter();1960 LogFlowFunc(("Appliance %p\n", pAppliance));1961 1962 HRESULT rc = S_OK;1963 1964 switch(task->taskType)1965 {1966 case TaskWriteOVF::Write:1967 {1968 if (task->storageType == VFSType_File)1969 rc = pAppliance->writeFS(task.get());1970 else if (task->storageType == VFSType_S3)1971 rc = pAppliance->writeS3(task.get());1972 break;1973 }1974 }1975 1976 LogFlowFunc(("rc=%Rhrc\n", rc));1977 LogFlowFuncLeave();1978 1979 return VINF_SUCCESS;1980 }1981 1982 /**1983 * Worker thread implementation for Write() (ovf writer).1984 * @param aThread1985 * @param pvUser1986 */1987 /* static */1988 int Appliance::writeFS(TaskWriteOVF *pTask)1989 {1990 LogFlowFuncEnter();1991 LogFlowFunc(("Appliance %p\n", this));1992 1993 AutoCaller autoCaller(this);1994 CheckComRCReturnRC(autoCaller.rc());1995 1996 AutoWriteLock appLock(this);1997 1998 HRESULT rc = S_OK;1999 2000 try2001 {2002 xml::Document doc;2003 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");2004 2005 pelmRoot->setAttribute("ovf:version", (pTask->enFormat == TaskWriteOVF::OVF_1_0) ? "1.0" : "0.9");2006 pelmRoot->setAttribute("xml:lang", "en-US");2007 2008 Utf8Str strNamespace = (pTask->enFormat == TaskWriteOVF::OVF_0_9)2009 ? "http://www.vmware.com/schema/ovf/1/envelope" // 0.92010 : "http://schemas.dmtf.org/ovf/envelope/1"; // 1.02011 pelmRoot->setAttribute("xmlns", strNamespace);2012 pelmRoot->setAttribute("xmlns:ovf", strNamespace);2013 2014 // pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");2015 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");2016 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");2017 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");2018 // pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");2019 2020 // <Envelope>/<References>2021 xml::ElementNode *pelmReferences = pelmRoot->createChild("References"); // 0.9 and 1.02022 2023 /* <Envelope>/<DiskSection>:2024 <DiskSection>2025 <Info>List of the virtual disks used in the package</Info>2026 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>2027 </DiskSection> */2028 xml::ElementNode *pelmDiskSection;2029 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2030 {2031 // <Section xsi:type="ovf:DiskSection_Type">2032 pelmDiskSection = pelmRoot->createChild("Section");2033 pelmDiskSection->setAttribute("xsi:type", "ovf:DiskSection_Type");2034 }2035 else2036 pelmDiskSection = pelmRoot->createChild("DiskSection");2037 2038 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");2039 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");2040 // for now, set up a map so we have a list of unique disk names (to make2041 // sure the same disk name is only added once)2042 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks;2043 2044 /* <Envelope>/<NetworkSection>:2045 <NetworkSection>2046 <Info>Logical networks used in the package</Info>2047 <Network ovf:name="VM Network">2048 <Description>The network that the LAMP Service will be available on</Description>2049 </Network>2050 </NetworkSection> */2051 xml::ElementNode *pelmNetworkSection;2052 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2053 {2054 // <Section xsi:type="ovf:NetworkSection_Type">2055 pelmNetworkSection = pelmRoot->createChild("Section");2056 pelmNetworkSection->setAttribute("xsi:type", "ovf:NetworkSection_Type");2057 }2058 else2059 pelmNetworkSection = pelmRoot->createChild("NetworkSection");2060 2061 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");2062 pelmNetworkSectionInfo->addContent("Logical networks used in the package");2063 // for now, set up a map so we have a list of unique network names (to make2064 // sure the same network name is only added once)2065 map<Utf8Str, bool> mapNetworks;2066 // we fill this later below when we iterate over the networks2067 2068 // and here come the virtual systems:2069 2070 // write a collection if we have more than one virtual system _and_ we're2071 // writing OVF 1.0; otherwise fail since ovftool can't import more than2072 // one machine, it seems2073 xml::ElementNode *pelmToAddVirtualSystemsTo;2074 if (m->virtualSystemDescriptions.size() > 1)2075 {2076 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2077 throw setError(VBOX_E_FILE_ERROR,2078 tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0"));2079 2080 pelmToAddVirtualSystemsTo = pelmRoot->createChild("VirtualSystemCollection");2081 /* xml::AttributeNode *pattrVirtualSystemCollectionId = */ pelmToAddVirtualSystemsTo->setAttribute("ovf:name", "ExportedVirtualBoxMachines"); // whatever2082 }2083 else2084 pelmToAddVirtualSystemsTo = pelmRoot; // add virtual system directly under root element2085 2086 uint32_t cDisks = 0;2087 2088 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;2089 /* Iterate through all virtual systems of that appliance */2090 for (it = m->virtualSystemDescriptions.begin();2091 it != m->virtualSystemDescriptions.end();2092 ++it)2093 {2094 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);2095 2096 xml::ElementNode *pelmVirtualSystem;2097 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2098 {2099 // <Section xsi:type="ovf:NetworkSection_Type">2100 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("Content");2101 pelmVirtualSystem->setAttribute("xsi:type", "ovf:VirtualSystem_Type");2102 }2103 else2104 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("VirtualSystem");2105 2106 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");2107 2108 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);2109 if (llName.size() != 1)2110 throw setError(VBOX_E_NOT_SUPPORTED,2111 tr("Missing VM name"));2112 Utf8Str &strVMName = llName.front()->strVbox;2113 pelmVirtualSystem->setAttribute("ovf:id", strVMName);2114 2115 // product info2116 std::list<VirtualSystemDescriptionEntry*> llProduct = vsdescThis->findByType(VirtualSystemDescriptionType_Product);2117 std::list<VirtualSystemDescriptionEntry*> llProductUrl = vsdescThis->findByType(VirtualSystemDescriptionType_ProductUrl);2118 std::list<VirtualSystemDescriptionEntry*> llVendor = vsdescThis->findByType(VirtualSystemDescriptionType_Vendor);2119 std::list<VirtualSystemDescriptionEntry*> llVendorUrl = vsdescThis->findByType(VirtualSystemDescriptionType_VendorUrl);2120 std::list<VirtualSystemDescriptionEntry*> llVersion = vsdescThis->findByType(VirtualSystemDescriptionType_Version);2121 bool fProduct = llProduct.size() && !llProduct.front()->strVbox.isEmpty();2122 bool fProductUrl = llProductUrl.size() && !llProductUrl.front()->strVbox.isEmpty();2123 bool fVendor = llVendor.size() && !llVendor.front()->strVbox.isEmpty();2124 bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.front()->strVbox.isEmpty();2125 bool fVersion = llVersion.size() && !llVersion.front()->strVbox.isEmpty();2126 if (fProduct ||2127 fProductUrl ||2128 fVersion ||2129 fVendorUrl ||2130 fVersion)2131 {2132 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">2133 <Info>Meta-information about the installed software</Info>2134 <Product>VAtest</Product>2135 <Vendor>SUN Microsystems</Vendor>2136 <Version>10.0</Version>2137 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>2138 <VendorUrl>http://www.sun.com</VendorUrl>2139 </Section> */2140 xml::ElementNode *pelmAnnotationSection;2141 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2142 {2143 // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">2144 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");2145 pelmAnnotationSection->setAttribute("xsi:type", "ovf:ProductSection_Type");2146 }2147 else2148 pelmAnnotationSection = pelmVirtualSystem->createChild("ProductSection");2149 2150 pelmAnnotationSection->createChild("Info")->addContent("Meta-information about the installed software");2151 if (fProduct)2152 pelmAnnotationSection->createChild("Product")->addContent(llProduct.front()->strVbox);2153 if (fVendor)2154 pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.front()->strVbox);2155 if (fVersion)2156 pelmAnnotationSection->createChild("Version")->addContent(llVersion.front()->strVbox);2157 if (fProductUrl)2158 pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.front()->strVbox);2159 if (fVendorUrl)2160 pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.front()->strVbox);2161 }2162 2163 // description2164 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);2165 if (llDescription.size() &&2166 !llDescription.front()->strVbox.isEmpty())2167 {2168 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">2169 <Info>A human-readable annotation</Info>2170 <Annotation>Plan 9</Annotation>2171 </Section> */2172 xml::ElementNode *pelmAnnotationSection;2173 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2174 {2175 // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">2176 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");2177 pelmAnnotationSection->setAttribute("xsi:type", "ovf:AnnotationSection_Type");2178 }2179 else2180 pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");2181 2182 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");2183 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVbox);2184 }2185 2186 // license2187 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License);2188 if (llLicense.size() &&2189 !llLicense.front()->strVbox.isEmpty())2190 {2191 /* <EulaSection>2192 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>2193 <License ovf:msgid="1">License terms can go in here.</License>2194 </EulaSection> */2195 xml::ElementNode *pelmEulaSection;2196 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2197 {2198 pelmEulaSection = pelmVirtualSystem->createChild("Section");2199 pelmEulaSection->setAttribute("xsi:type", "ovf:EulaSection_Type");2200 }2201 else2202 pelmEulaSection = pelmVirtualSystem->createChild("EulaSection");2203 2204 pelmEulaSection->createChild("Info")->addContent("License agreement for the virtual system");2205 pelmEulaSection->createChild("License")->addContent(llLicense.front()->strVbox);2206 }2207 2208 // operating system2209 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);2210 if (llOS.size() != 1)2211 throw setError(VBOX_E_NOT_SUPPORTED,2212 tr("Missing OS type"));2213 /* <OperatingSystemSection ovf:id="82">2214 <Info>Guest Operating System</Info>2215 <Description>Linux 2.6.x</Description>2216 </OperatingSystemSection> */2217 xml::ElementNode *pelmOperatingSystemSection;2218 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2219 {2220 pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section");2221 pelmOperatingSystemSection->setAttribute("xsi:type", "ovf:OperatingSystemSection_Type");2222 }2223 else2224 pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");2225 2226 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);2227 pelmOperatingSystemSection->createChild("Info")->addContent("The kind of installed guest operating system");2228 Utf8Str strOSDesc;2229 convertCIMOSType2VBoxOSType(strOSDesc, (CIMOSType_T)llOS.front()->strOvf.toInt32(), "");2230 pelmOperatingSystemSection->createChild("Description")->addContent(strOSDesc);2231 2232 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">2233 xml::ElementNode *pelmVirtualHardwareSection;2234 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2235 {2236 // <Section xsi:type="ovf:VirtualHardwareSection_Type">2237 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("Section");2238 pelmVirtualHardwareSection->setAttribute("xsi:type", "ovf:VirtualHardwareSection_Type");2239 }2240 else2241 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");2242 2243 pelmVirtualHardwareSection->createChild("Info")->addContent("Virtual hardware requirements for a virtual machine");2244 2245 /* <System>2246 <vssd:Description>Description of the virtual hardware section.</vssd:Description>2247 <vssd:ElementName>vmware</vssd:ElementName>2248 <vssd:InstanceID>1</vssd:InstanceID>2249 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>2250 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>2251 </System> */2252 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");2253 2254 pelmSystem->createChild("vssd:ElementName")->addContent("Virtual Hardware Family"); // required OVF 1.02255 2256 // <vssd:InstanceId>0</vssd:InstanceId>2257 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2258 pelmSystem->createChild("vssd:InstanceId")->addContent("0");2259 else // capitalization changed...2260 pelmSystem->createChild("vssd:InstanceID")->addContent("0");2261 2262 // <vssd:VirtualSystemIdentifier>VAtest</vssd:VirtualSystemIdentifier>2263 pelmSystem->createChild("vssd:VirtualSystemIdentifier")->addContent(strVMName);2264 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>2265 const char *pcszHardware = "virtualbox-2.2";2266 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2267 // pretend to be vmware compatible then2268 pcszHardware = "vmx-6";2269 pelmSystem->createChild("vssd:VirtualSystemType")->addContent(pcszHardware);2270 2271 // loop thru all description entries twice; once to write out all2272 // devices _except_ disk images, and a second time to assign the2273 // disk images; this is because disk images need to reference2274 // IDE controllers, and we can't know their instance IDs without2275 // assigning them first2276 2277 uint32_t idIDEController = 0;2278 int32_t lIDEControllerIndex = 0;2279 uint32_t idSATAController = 0;2280 int32_t lSATAControllerIndex = 0;2281 uint32_t idSCSIController = 0;2282 int32_t lSCSIControllerIndex = 0;2283 2284 uint32_t ulInstanceID = 1;2285 2286 for (size_t uLoop = 1;2287 uLoop <= 2;2288 ++uLoop)2289 {2290 int32_t lIndexThis = 0;2291 list<VirtualSystemDescriptionEntry>::const_iterator itD;2292 for (itD = vsdescThis->m->llDescriptions.begin();2293 itD != vsdescThis->m->llDescriptions.end();2294 ++itD, ++lIndexThis)2295 {2296 const VirtualSystemDescriptionEntry &desc = *itD;2297 2298 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff2299 Utf8Str strResourceSubType;2300 2301 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block2302 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block2303 2304 uint32_t ulParent = 0;2305 2306 int32_t lVirtualQuantity = -1;2307 Utf8Str strAllocationUnits;2308 2309 int32_t lAddress = -1;2310 int32_t lBusNumber = -1;2311 int32_t lAddressOnParent = -1;2312 2313 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"2314 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block2315 Utf8Str strHostResource;2316 2317 uint64_t uTemp;2318 2319 switch (desc.type)2320 {2321 case VirtualSystemDescriptionType_CPU:2322 /* <Item>2323 <rasd:Caption>1 virtual CPU</rasd:Caption>2324 <rasd:Description>Number of virtual CPUs</rasd:Description>2325 <rasd:ElementName>virtual CPU</rasd:ElementName>2326 <rasd:InstanceID>1</rasd:InstanceID>2327 <rasd:ResourceType>3</rasd:ResourceType>2328 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>2329 </Item> */2330 if (uLoop == 1)2331 {2332 strDescription = "Number of virtual CPUs";2333 type = OVFResourceType_Processor; // 32334 desc.strVbox.toInt(uTemp);2335 lVirtualQuantity = uTemp;2336 strCaption = Utf8StrFmt("%d virtual CPU", lVirtualQuantity); // without this ovftool won't eat the item2337 }2338 break;2339 2340 case VirtualSystemDescriptionType_Memory:2341 /* <Item>2342 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>2343 <rasd:Caption>256 MB of memory</rasd:Caption>2344 <rasd:Description>Memory Size</rasd:Description>2345 <rasd:ElementName>Memory</rasd:ElementName>2346 <rasd:InstanceID>2</rasd:InstanceID>2347 <rasd:ResourceType>4</rasd:ResourceType>2348 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>2349 </Item> */2350 if (uLoop == 1)2351 {2352 strDescription = "Memory Size";2353 type = OVFResourceType_Memory; // 42354 desc.strVbox.toInt(uTemp);2355 lVirtualQuantity = (int32_t)(uTemp / _1M);2356 strAllocationUnits = "MegaBytes";2357 strCaption = Utf8StrFmt("%d MB of memory", lVirtualQuantity); // without this ovftool won't eat the item2358 }2359 break;2360 2361 case VirtualSystemDescriptionType_HardDiskControllerIDE:2362 /* <Item>2363 <rasd:Caption>ideController1</rasd:Caption>2364 <rasd:Description>IDE Controller</rasd:Description>2365 <rasd:InstanceId>5</rasd:InstanceId>2366 <rasd:ResourceType>5</rasd:ResourceType>2367 <rasd:Address>1</rasd:Address>2368 <rasd:BusNumber>1</rasd:BusNumber>2369 </Item> */2370 if (uLoop == 1)2371 {2372 strDescription = "IDE Controller";2373 strCaption = "ideController0";2374 type = OVFResourceType_IDEController; // 52375 strResourceSubType = desc.strVbox;2376 // it seems that OVFTool always writes these two, and since we can only2377 // have one IDE controller, we'll use this as well2378 lAddress = 1;2379 lBusNumber = 1;2380 2381 // remember this ID2382 idIDEController = ulInstanceID;2383 lIDEControllerIndex = lIndexThis;2384 }2385 break;2386 2387 case VirtualSystemDescriptionType_HardDiskControllerSATA:2388 /* <Item>2389 <rasd:Caption>sataController0</rasd:Caption>2390 <rasd:Description>SATA Controller</rasd:Description>2391 <rasd:InstanceId>4</rasd:InstanceId>2392 <rasd:ResourceType>20</rasd:ResourceType>2393 <rasd:ResourceSubType>ahci</rasd:ResourceSubType>2394 <rasd:Address>0</rasd:Address>2395 <rasd:BusNumber>0</rasd:BusNumber>2396 </Item>2397 */2398 if (uLoop == 1)2399 {2400 strDescription = "SATA Controller";2401 strCaption = "sataController0";2402 type = OVFResourceType_OtherStorageDevice; // 202403 // it seems that OVFTool always writes these two, and since we can only2404 // have one SATA controller, we'll use this as well2405 lAddress = 0;2406 lBusNumber = 0;2407 2408 if ( desc.strVbox.isEmpty() // AHCI is the default in VirtualBox2409 || (!desc.strVbox.compare("ahci", Utf8Str::CaseInsensitive))2410 )2411 strResourceSubType = "AHCI";2412 else2413 throw setError(VBOX_E_NOT_SUPPORTED,2414 tr("Invalid config string \"%s\" in SATA controller"), desc.strVbox.c_str());2415 2416 // remember this ID2417 idSATAController = ulInstanceID;2418 lSATAControllerIndex = lIndexThis;2419 }2420 break;2421 2422 case VirtualSystemDescriptionType_HardDiskControllerSCSI:2423 /* <Item>2424 <rasd:Caption>scsiController0</rasd:Caption>2425 <rasd:Description>SCSI Controller</rasd:Description>2426 <rasd:InstanceId>4</rasd:InstanceId>2427 <rasd:ResourceType>6</rasd:ResourceType>2428 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>2429 <rasd:Address>0</rasd:Address>2430 <rasd:BusNumber>0</rasd:BusNumber>2431 </Item>2432 */2433 if (uLoop == 1)2434 {2435 strDescription = "SCSI Controller";2436 strCaption = "scsiController0";2437 type = OVFResourceType_ParallelSCSIHBA; // 62438 // it seems that OVFTool always writes these two, and since we can only2439 // have one SATA controller, we'll use this as well2440 lAddress = 0;2441 lBusNumber = 0;2442 2443 if ( desc.strVbox.isEmpty() // LsiLogic is the default in VirtualBox2444 || (!desc.strVbox.compare("lsilogic", Utf8Str::CaseInsensitive))2445 )2446 strResourceSubType = "lsilogic";2447 else if (!desc.strVbox.compare("buslogic", Utf8Str::CaseInsensitive))2448 strResourceSubType = "buslogic";2449 else2450 throw setError(VBOX_E_NOT_SUPPORTED,2451 tr("Invalid config string \"%s\" in SCSI controller"), desc.strVbox.c_str());2452 2453 // remember this ID2454 idSCSIController = ulInstanceID;2455 lSCSIControllerIndex = lIndexThis;2456 }2457 break;2458 2459 case VirtualSystemDescriptionType_HardDiskImage:2460 /* <Item>2461 <rasd:Caption>disk1</rasd:Caption>2462 <rasd:InstanceId>8</rasd:InstanceId>2463 <rasd:ResourceType>17</rasd:ResourceType>2464 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>2465 <rasd:Parent>4</rasd:Parent>2466 <rasd:AddressOnParent>0</rasd:AddressOnParent>2467 </Item> */2468 if (uLoop == 2)2469 {2470 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);2471 2472 strDescription = "Disk Image";2473 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else2474 type = OVFResourceType_HardDisk; // 172475 2476 // the following references the "<Disks>" XML block2477 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());2478 2479 // controller=<index>;channel=<c>2480 size_t pos1 = desc.strExtraConfig.find("controller=");2481 size_t pos2 = desc.strExtraConfig.find("channel=");2482 if (pos1 != Utf8Str::npos)2483 {2484 int32_t lControllerIndex = -1;2485 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);2486 if (lControllerIndex == lIDEControllerIndex)2487 ulParent = idIDEController;2488 else if (lControllerIndex == lSCSIControllerIndex)2489 ulParent = idSCSIController;2490 else if (lControllerIndex == lSATAControllerIndex)2491 ulParent = idSATAController;2492 }2493 if (pos2 != Utf8Str::npos)2494 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);2495 2496 if ( !ulParent2497 || lAddressOnParent == -12498 )2499 throw setError(VBOX_E_NOT_SUPPORTED,2500 tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfig.c_str());2501 2502 mapDisks[strDiskID] = &desc;2503 }2504 break;2505 2506 case VirtualSystemDescriptionType_Floppy:2507 if (uLoop == 1)2508 {2509 strDescription = "Floppy Drive";2510 strCaption = "floppy0"; // this is what OVFTool writes2511 type = OVFResourceType_FloppyDrive; // 142512 lAutomaticAllocation = 0;2513 lAddressOnParent = 0; // this is what OVFTool writes2514 }2515 break;2516 2517 case VirtualSystemDescriptionType_CDROM:2518 if (uLoop == 2)2519 {2520 // we can't have a CD without an IDE controller2521 if (!idIDEController)2522 throw setError(VBOX_E_NOT_SUPPORTED,2523 tr("Can't have CD-ROM without IDE controller"));2524 2525 strDescription = "CD-ROM Drive";2526 strCaption = "cdrom1"; // this is what OVFTool writes2527 type = OVFResourceType_CDDrive; // 152528 lAutomaticAllocation = 1;2529 ulParent = idIDEController;2530 lAddressOnParent = 0; // this is what OVFTool writes2531 }2532 break;2533 2534 case VirtualSystemDescriptionType_NetworkAdapter:2535 /* <Item>2536 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>2537 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>2538 <rasd:Connection>VM Network</rasd:Connection>2539 <rasd:ElementName>VM network</rasd:ElementName>2540 <rasd:InstanceID>3</rasd:InstanceID>2541 <rasd:ResourceType>10</rasd:ResourceType>2542 </Item> */2543 if (uLoop == 1)2544 {2545 lAutomaticAllocation = 1;2546 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());2547 type = OVFResourceType_EthernetAdapter; // 102548 /* Set the hardware type to something useful.2549 * To be compatible with vmware & others we set2550 * PCNet32 for our PCNet types & E1000 for the2551 * E1000 cards. */2552 switch (desc.strVbox.toInt32())2553 {2554 case NetworkAdapterType_Am79C970A:2555 case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break;2556 #ifdef VBOX_WITH_E10002557 case NetworkAdapterType_I82540EM:2558 case NetworkAdapterType_I82545EM:2559 case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break;2560 #endif /* VBOX_WITH_E1000 */2561 }2562 strConnection = desc.strOvf;2563 2564 mapNetworks[desc.strOvf] = true;2565 }2566 break;2567 2568 case VirtualSystemDescriptionType_USBController:2569 /* <Item ovf:required="false">2570 <rasd:Caption>usb</rasd:Caption>2571 <rasd:Description>USB Controller</rasd:Description>2572 <rasd:InstanceId>3</rasd:InstanceId>2573 <rasd:ResourceType>23</rasd:ResourceType>2574 <rasd:Address>0</rasd:Address>2575 <rasd:BusNumber>0</rasd:BusNumber>2576 </Item> */2577 if (uLoop == 1)2578 {2579 strDescription = "USB Controller";2580 strCaption = "usb";2581 type = OVFResourceType_USBController; // 232582 lAddress = 0; // this is what OVFTool writes2583 lBusNumber = 0; // this is what OVFTool writes2584 }2585 break;2586 2587 case VirtualSystemDescriptionType_SoundCard:2588 /* <Item ovf:required="false">2589 <rasd:Caption>sound</rasd:Caption>2590 <rasd:Description>Sound Card</rasd:Description>2591 <rasd:InstanceId>10</rasd:InstanceId>2592 <rasd:ResourceType>35</rasd:ResourceType>2593 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>2594 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>2595 <rasd:AddressOnParent>3</rasd:AddressOnParent>2596 </Item> */2597 if (uLoop == 1)2598 {2599 strDescription = "Sound Card";2600 strCaption = "sound";2601 type = OVFResourceType_SoundCard; // 352602 strResourceSubType = desc.strOvf; // e.g. ensoniq13712603 lAutomaticAllocation = 0;2604 lAddressOnParent = 3; // what gives? this is what OVFTool writes2605 }2606 break;2607 }2608 2609 if (type)2610 {2611 xml::ElementNode *pItem;2612 2613 pItem = pelmVirtualHardwareSection->createChild("Item");2614 2615 // NOTE: do not change the order of these items without good reason! While we don't care2616 // about ordering, VMware's ovftool does and fails if the items are not written in2617 // exactly this order, as stupid as it seems.2618 2619 if (!strCaption.isEmpty())2620 {2621 pItem->createChild("rasd:Caption")->addContent(strCaption);2622 if (pTask->enFormat == TaskWriteOVF::OVF_1_0)2623 pItem->createChild("rasd:ElementName")->addContent(strCaption);2624 }2625 2626 if (!strDescription.isEmpty())2627 pItem->createChild("rasd:Description")->addContent(strDescription);2628 2629 // <rasd:InstanceID>1</rasd:InstanceID>2630 xml::ElementNode *pelmInstanceID;2631 if (pTask->enFormat == TaskWriteOVF::OVF_0_9)2632 pelmInstanceID = pItem->createChild("rasd:InstanceId");2633 else2634 pelmInstanceID = pItem->createChild("rasd:InstanceID"); // capitalization changed...2635 pelmInstanceID->addContent(Utf8StrFmt("%d", ulInstanceID++));2636 2637 // <rasd:ResourceType>3</rasd:ResourceType>2638 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));2639 if (!strResourceSubType.isEmpty())2640 pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);2641 2642 if (!strHostResource.isEmpty())2643 pItem->createChild("rasd:HostResource")->addContent(strHostResource);2644 2645 if (!strAllocationUnits.isEmpty())2646 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);2647 2648 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>2649 if (lVirtualQuantity != -1)2650 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));2651 2652 if (lAutomaticAllocation != -1)2653 pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );2654 2655 if (!strConnection.isEmpty())2656 pItem->createChild("rasd:Connection")->addContent(strConnection);2657 2658 if (lAddress != -1)2659 pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));2660 2661 if (lBusNumber != -1)2662 if (pTask->enFormat == TaskWriteOVF::OVF_0_9) // BusNumber is invalid OVF 1.0 so only write it in 0.9 mode for OVFTool compatibility2663 pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));2664 2665 if (ulParent)2666 pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));2667 if (lAddressOnParent != -1)2668 pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));2669 }2670 }2671 } // for (size_t uLoop = 0; ...2672 }2673 2674 // finally, fill in the network section we set up empty above according2675 // to the networks we found with the hardware items2676 map<Utf8Str, bool>::const_iterator itN;2677 for (itN = mapNetworks.begin();2678 itN != mapNetworks.end();2679 ++itN)2680 {2681 const Utf8Str &strNetwork = itN->first;2682 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");2683 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());2684 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");2685 }2686 2687 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;2688 uint32_t ulFile = 1;2689 for (itS = mapDisks.begin();2690 itS != mapDisks.end();2691 ++itS)2692 {2693 const Utf8Str &strDiskID = itS->first;2694 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;2695 2696 // source path: where the VBox image is2697 const Utf8Str &strSrcFilePath = pDiskEntry->strVbox;2698 Bstr bstrSrcFilePath(strSrcFilePath);2699 if (!RTPathExists(strSrcFilePath.c_str()))2700 /* This isn't allowed */2701 throw setError(VBOX_E_FILE_ERROR,2702 tr("Source virtual disk image file '%s' doesn't exist"),2703 strSrcFilePath.c_str());2704 2705 // output filename2706 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;2707 // target path needs to be composed from where the output OVF is2708 Utf8Str strTargetFilePath(pTask->strOutputFile);2709 strTargetFilePath.stripFilename();2710 strTargetFilePath.append("/");2711 strTargetFilePath.append(strTargetFileNameOnly);2712 2713 // clone the disk:2714 ComPtr<IHardDisk> pSourceDisk;2715 ComPtr<IHardDisk> pTargetDisk;2716 ComPtr<IProgress> pProgress2;2717 2718 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));2719 rc = mVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());2720 if (FAILED(rc)) throw rc;2721 2722 /* We are always exporting to vmdfk stream optimized for now */2723 Bstr bstrSrcFormat = L"VMDK";2724 2725 // create a new hard disk interface for the destination disk image2726 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));2727 rc = mVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());2728 if (FAILED(rc)) throw rc;2729 2730 // the target disk is now registered and needs to be removed again,2731 // both after successful cloning or if anything goes bad!2732 try2733 {2734 // create a flat copy of the source disk image2735 rc = pSourceDisk->CloneTo(pTargetDisk, HardDiskVariant_VmdkStreamOptimized, NULL, pProgress2.asOutParam());2736 if (FAILED(rc)) throw rc;2737 2738 // advance to the next operation2739 if (!pTask->progress.isNull())2740 pTask->progress->setNextOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()),2741 pDiskEntry->ulSizeMB); // operation's weight, as set up with the IProgress originally);2742 2743 // now wait for the background disk operation to complete; this throws HRESULTs on error2744 waitForAsyncProgress(pTask->progress, pProgress2);2745 }2746 catch (HRESULT rc3)2747 {2748 // upon error after registering, close the disk or2749 // it'll stick in the registry forever2750 pTargetDisk->Close();2751 throw;2752 }2753 2754 // we need the following for the XML2755 uint64_t cbFile = 0; // actual file size2756 rc = pTargetDisk->COMGETTER(Size)(&cbFile);2757 if (FAILED(rc)) throw rc;2758 2759 ULONG64 cbCapacity = 0; // size reported to guest2760 rc = pTargetDisk->COMGETTER(LogicalSize)(&cbCapacity);2761 if (FAILED(rc)) throw rc;2762 // capacity is reported in megabytes, so...2763 cbCapacity *= _1M;2764 2765 // upon success, close the disk as well2766 rc = pTargetDisk->Close();2767 if (FAILED(rc)) throw rc;2768 2769 // now handle the XML for the disk:2770 Utf8StrFmt strFileRef("file%RI32", ulFile++);2771 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>2772 xml::ElementNode *pelmFile = pelmReferences->createChild("File");2773 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);2774 pelmFile->setAttribute("ovf:id", strFileRef);2775 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());2776 2777 // add disk to XML Disks section2778 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/>2779 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");2780 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());2781 pelmDisk->setAttribute("ovf:diskId", strDiskID);2782 pelmDisk->setAttribute("ovf:fileRef", strFileRef);2783 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#sparse"); // must be sparse or ovftool chokes2784 }2785 2786 // now go write the XML2787 xml::XmlFileWriter writer(doc);2788 writer.write(pTask->strOutputFile.c_str());2789 }2790 catch(xml::Error &x)2791 {2792 rc = setError(VBOX_E_FILE_ERROR,2793 x.what());2794 }2795 catch(HRESULT aRC)2796 {2797 rc = aRC;2798 }2799 2800 pTask->rc = rc;2801 2802 if (!pTask->progress.isNull())2803 pTask->progress->notifyComplete(rc);2804 2805 LogFlowFunc(("rc=%Rhrc\n", rc));2806 LogFlowFuncLeave();2807 2808 return VINF_SUCCESS;2809 }2810 2811 /**2812 * Worker thread implementation for Upload() (ovf uploader).2813 * @param aThread2814 * @param pvUser2815 */2816 /* static */2817 int Appliance::writeS3(TaskWriteOVF *pTask)2818 {2819 LogFlowFuncEnter();2820 LogFlowFunc(("Appliance %p\n", this));2821 2822 AutoCaller autoCaller(this);2823 CheckComRCReturnRC(autoCaller.rc());2824 2825 HRESULT rc = S_OK;2826 2827 AutoWriteLock appLock(this);2828 2829 /* Buckets are S3 specific. So parse the bucket out of the file path */2830 Utf8Str tmpPath = pTask->filepath;2831 if (!tmpPath.startsWith("/"))2832 return setError(E_INVALIDARG,2833 tr("The path '%s' must start with /"), tmpPath.c_str());2834 Utf8Str bucket;2835 size_t bpos = tmpPath.find("/", 1);2836 if (bpos != Utf8Str::npos)2837 {2838 bucket = tmpPath.substr(1, bpos - 1); /* The bucket without any slashes */2839 tmpPath = tmpPath.substr(bpos); /* The rest of the file path */2840 }2841 /* If there is no bucket name provided reject the upload */2842 if (bucket.isEmpty())2843 return setError(E_INVALIDARG,2844 tr("You doesn't provide a bucket name in the URI"), tmpPath.c_str());2845 2846 int vrc = VINF_SUCCESS;2847 RTS3 hS3 = NULL;2848 char szOSTmpDir[RTPATH_MAX];2849 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));2850 /* The template for the temporary directory created below */2851 char *pszTmpDir;2852 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);2853 list< pair<Utf8Str, ULONG> > filesList;2854 2855 // todo:2856 // - getting the tmp directory (especially on win)2857 // - usable error codes2858 // - seems snapshot filenames are problematic {uuid}.vdi2859 try2860 {2861 /* We need a temporary directory which we can put the OVF file & all2862 * disk images in */2863 vrc = RTDirCreateTemp(pszTmpDir);2864 if (RT_FAILURE(rc))2865 throw setError(VBOX_E_FILE_ERROR,2866 tr("Cannot create temporary directory '%s'"), pszTmpDir);2867 2868 /* The temporary name of the target OVF file */2869 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath));2870 2871 /* Prepare the temporary writing of the OVF */2872 ComObjPtr<Progress> progress;2873 rc = writeImpl(pTask->enFormat, strTmpOvf.c_str(), progress);2874 if (FAILED(rc)) throw rc;2875 2876 /* Unlock the appliance for the writing thread */2877 appLock.unlock();2878 /* Wait until the writing is done, but report the progress back to the2879 caller */2880 ComPtr<IProgress> progressInt(progress);2881 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */2882 2883 /* Again lock the appliance for the next steps */2884 appLock.lock();2885 2886 vrc = RTPathExists(strTmpOvf.c_str()); /* Paranoid check */2887 if(RT_FAILURE(vrc))2888 throw setError(VBOX_E_FILE_ERROR,2889 tr("Cannot find source file '%s'"), strTmpOvf.c_str());2890 /* Add the OVF file */2891 filesList.push_back(pair<Utf8Str, ULONG>(strTmpOvf, m->ulWeightPerOperation)); /* Use 1% of the total for the OVF file upload */2892 2893 /* Now add every disks of every virtual system */2894 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;2895 for (it = m->virtualSystemDescriptions.begin();2896 it != m->virtualSystemDescriptions.end();2897 ++it)2898 {2899 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);2900 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);2901 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;2902 for (itH = avsdeHDs.begin();2903 itH != avsdeHDs.end();2904 ++itH)2905 {2906 const Utf8Str &strTargetFileNameOnly = (*itH)->strOvf;2907 /* Target path needs to be composed from where the output OVF is */2908 Utf8Str strTargetFilePath(pTask->strOutputFile);2909 strTargetFilePath.stripFilename();2910 strTargetFilePath.append("/");2911 strTargetFilePath.append(strTargetFileNameOnly);2912 vrc = RTPathExists(strTargetFilePath.c_str()); /* Paranoid check */2913 if(RT_FAILURE(vrc))2914 throw setError(VBOX_E_FILE_ERROR,2915 tr("Cannot find source file '%s'"), strTargetFilePath.c_str());2916 filesList.push_back(pair<Utf8Str, ULONG>(strTargetFilePath, (*itH)->ulSizeMB));2917 }2918 }2919 /* Next we have to upload the OVF & all disk images */2920 vrc = RTS3Create(&hS3, pTask->username.c_str(), pTask->password.c_str(), pTask->hostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);2921 if(RT_FAILURE(vrc))2922 throw setError(VBOX_E_IPRT_ERROR,2923 tr("Cannot create S3 service handler"));2924 RTS3SetProgressCallback(hS3, pTask->uploadProgress, &pTask);2925 2926 /* Upload all files */2927 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)2928 {2929 const pair<Utf8Str, ULONG> &s = (*it1);2930 char *pszFilename = RTPathFilename(s.first.c_str());2931 /* Advance to the next operation */2932 if (!pTask->progress.isNull())2933 pTask->progress->setNextOperation(BstrFmt(tr("Uploading file '%s'"), pszFilename), s.second);2934 vrc = RTS3PutKey(hS3, bucket.c_str(), pszFilename, s.first.c_str());2935 if (RT_FAILURE(vrc))2936 {2937 if(vrc == VERR_S3_CANCELED)2938 break;2939 else if(vrc == VERR_S3_ACCESS_DENIED)2940 throw setError(E_ACCESSDENIED,2941 tr("Cannot upload file '%s' to S3 storage server (Access denied)"), pszFilename);2942 else if(vrc == VERR_S3_NOT_FOUND)2943 throw setError(VBOX_E_FILE_ERROR,2944 tr("Cannot upload file '%s' to S3 storage server (File not found)"), pszFilename);2945 else2946 throw setError(VBOX_E_IPRT_ERROR,2947 tr("Cannot upload file '%s' to S3 storage server (%Rrc)"), pszFilename, vrc);2948 }2949 }2950 2951 }2952 catch(HRESULT aRC)2953 {2954 rc = aRC;2955 }2956 /* Cleanup */2957 if (hS3)2958 RTS3Destroy(hS3);2959 /* Delete all files which where temporary created */2960 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)2961 {2962 const pair<Utf8Str, ULONG> &s = (*it1);2963 vrc = RTFileDelete(s.first.c_str());2964 if(RT_FAILURE(vrc))2965 rc = setError(VBOX_E_FILE_ERROR,2966 tr("Cannot delete file '%s' (%Rrc)"), s.first.c_str(), vrc);2967 }2968 /* Delete the temporary directory */2969 if (RTPathExists(pszTmpDir))2970 {2971 vrc = RTDirRemove(pszTmpDir);2972 if(RT_FAILURE(vrc))2973 rc = setError(VBOX_E_FILE_ERROR,2974 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);2975 }2976 if (pszTmpDir)2977 RTStrFree(pszTmpDir);2978 2979 pTask->rc = rc;2980 2981 if (!pTask->progress.isNull())2982 pTask->progress->notifyComplete(rc);2983 2984 LogFlowFunc(("rc=%Rhrc\n", rc));2985 LogFlowFuncLeave();2986 2987 return VINF_SUCCESS;2988 3670 } 2989 3671 … … 3019 3701 } 3020 3702 3021 HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const3022 {3023 IMachine *machine = NULL;3024 char *tmpName = RTStrDup(aName.c_str());3025 int i = 1;3026 /* @todo: Maybe too cost-intensive; try to find a lighter way */3027 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)3028 {3029 RTStrFree(tmpName);3030 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);3031 ++i;3032 }3033 aName = tmpName;3034 RTStrFree(tmpName);3035 3036 return S_OK;3037 }3038 3039 HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const3040 {3041 IHardDisk *harddisk = NULL;3042 char *tmpName = RTStrDup(aName.c_str());3043 int i = 1;3044 /* Check if the file exists or if a file with this path is registered3045 * already */3046 /* @todo: Maybe too cost-intensive; try to find a lighter way */3047 while (RTPathExists(tmpName) ||3048 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)3049 {3050 RTStrFree(tmpName);3051 char *tmpDir = RTStrDup(aName.c_str());3052 RTPathStripFilename(tmpDir);;3053 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));3054 RTPathStripExt(tmpFile);3055 const char *tmpExt = RTPathExt(aName.c_str());3056 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);3057 RTStrFree(tmpFile);3058 RTStrFree(tmpDir);3059 ++i;3060 }3061 aName = tmpName;3062 RTStrFree(tmpName);3063 3064 return S_OK;3065 }3066 3067 /**3068 * Sets up the given progress object so that it represents disk images accurately3069 * during importMachines() and write().3070 * @param pProgress3071 * @param bstrDescription3072 * @return3073 */3074 HRESULT Appliance::setUpProgress(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)3075 {3076 HRESULT rc;3077 3078 /* Create the progress object */3079 pProgress.createObject();3080 3081 // weigh the disk images according to their sizes3082 uint32_t ulTotalMB = 0;3083 uint32_t cDisks = 0;3084 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;3085 for (it = m->virtualSystemDescriptions.begin();3086 it != m->virtualSystemDescriptions.end();3087 ++it)3088 {3089 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);3090 /* One for every hard disk of the Virtual System */3091 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);3092 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;3093 for (itH = avsdeHDs.begin();3094 itH != avsdeHDs.end();3095 ++itH)3096 {3097 const VirtualSystemDescriptionEntry *pHD = *itH;3098 ulTotalMB += pHD->ulSizeMB;3099 ++cDisks;3100 }3101 }3102 3103 ULONG cOperations = 1 + cDisks; // one op per disk plus 1 for the XML3104 3105 ULONG ulTotalOperationsWeight;3106 if (ulTotalMB)3107 {3108 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for the XML3109 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;3110 }3111 else3112 {3113 // no disks to export:3114 ulTotalOperationsWeight = 1;3115 m->ulWeightPerOperation = 1;3116 }3117 3118 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",3119 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));3120 3121 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),3122 bstrDescription,3123 TRUE /* aCancelable */,3124 cOperations, // ULONG cOperations,3125 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,3126 bstrDescription, // CBSTR bstrFirstOperationDescription,3127 m->ulWeightPerOperation); // ULONG ulFirstOperationWeight,3128 return rc;3129 }3130 3131 HRESULT Appliance::setUpProgressUpload(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)3132 {3133 HRESULT rc;3134 3135 /* Create the progress object */3136 pProgress.createObject();3137 3138 // weigh the disk images according to their sizes3139 uint32_t ulTotalMB = 0;3140 uint32_t cDisks = 0;3141 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;3142 for (it = m->virtualSystemDescriptions.begin();3143 it != m->virtualSystemDescriptions.end();3144 ++it)3145 {3146 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);3147 /* One for every hard disk of the Virtual System */3148 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);3149 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;3150 for (itH = avsdeHDs.begin();3151 itH != avsdeHDs.end();3152 ++itH)3153 {3154 const VirtualSystemDescriptionEntry *pHD = *itH;3155 ulTotalMB += pHD->ulSizeMB;3156 ++cDisks;3157 }3158 }3159 3160 ULONG cOperations = 1 + 1 + cDisks; // one op per disk plus 1 for the OVF & 1 plus to the temporary creation */3161 3162 ULONG ulTotalOperationsWeight;3163 if (ulTotalMB)3164 {3165 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)3166 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;3167 }3168 else3169 {3170 // no disks to export:3171 ulTotalOperationsWeight = 1;3172 m->ulWeightPerOperation = 1;3173 }3174 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */3175 ulTotalOperationsWeight += ulOVFCreationWeight;3176 3177 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",3178 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));3179 3180 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),3181 bstrDescription,3182 TRUE /* aCancelable */,3183 cOperations, // ULONG cOperations,3184 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,3185 bstrDescription, // CBSTR bstrFirstOperationDescription,3186 ulOVFCreationWeight); // ULONG ulFirstOperationWeight,3187 return rc;3188 }3189 3190 /**3191 * Called from the import and export background threads to synchronize the second3192 * background disk thread's progress object with the current progress object so3193 * that the user interface sees progress correctly and that cancel signals are3194 * passed on to the second thread.3195 * @param pProgressThis Progress object of the current thread.3196 * @param pProgressAsync Progress object of asynchronous task running in background.3197 */3198 void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis,3199 ComPtr<IProgress> &pProgressAsync)3200 {3201 HRESULT rc;3202 3203 // now loop until the asynchronous operation completes and then report its result3204 BOOL fCompleted;3205 BOOL fCanceled;3206 ULONG currentPercent;3207 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted))))3208 {3209 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled);3210 if (FAILED(rc)) throw rc;3211 if (fCanceled)3212 {3213 pProgressAsync->Cancel();3214 break;3215 }3216 3217 rc = pProgressAsync->COMGETTER(Percent(¤tPercent));3218 if (FAILED(rc)) throw rc;3219 if (!pProgressThis.isNull())3220 pProgressThis->setCurrentOperationProgress(currentPercent);3221 if (fCompleted)3222 break;3223 3224 /* Make sure the loop is not too tight */3225 rc = pProgressAsync->WaitForCompletion(100);3226 if (FAILED(rc)) throw rc;3227 }3228 // report result of asynchronous operation3229 LONG iRc;3230 rc = pProgressAsync->COMGETTER(ResultCode)(&iRc);3231 if (FAILED(rc)) throw rc;3232 3233 3234 // if the thread of the progress object has an error, then3235 // retrieve the error info from there, or it'll be lost3236 if (FAILED(iRc))3237 {3238 ProgressErrorInfo info(pProgressAsync);3239 Utf8Str str(info.getText());3240 const char *pcsz = str.c_str();3241 HRESULT rc2 = setError(iRc, pcsz);3242 throw rc2;3243 }3244 }3245 3246 void Appliance::addWarning(const char* aWarning, ...)3247 {3248 va_list args;3249 va_start(args, aWarning);3250 Utf8StrFmtVA str(aWarning, args);3251 va_end(args);3252 m->llWarnings.push_back(str);3253 }3254 3255 3703 //////////////////////////////////////////////////////////////////////////////// 3256 3704 // … … 3260 3708 3261 3709 DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription) 3262 struct shutup3 {};3263 3710 3264 3711 /**
Note:
See TracChangeset
for help on using the changeset viewer.