Changeset 33289 in vbox
- Timestamp:
- Oct 21, 2010 10:00:15 AM (14 years ago)
- Location:
- trunk
- Files:
-
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/iprt/manifest.h
r33057 r33289 44 44 { 45 45 /** The filename. */ 46 c har *pszTestFile;46 const char *pszTestFile; 47 47 /** The SHA1 digest of the file. */ 48 c har *pszTestDigest;48 const char *pszTestDigest; 49 49 } RTMANIFESTTEST; 50 50 /** Pointer to the input structure. */ … … 106 106 107 107 /** 108 * Verify the given SHA1 digests against the entries in the manifest file in 109 * memory. 110 * 111 * @returns iprt status code. 112 * 113 * @param pvBuf Pointer to memory buffer of the manifest file. 114 * @param cbSize Size of the memory buffer. 115 * @param paTests Array of file names and digests. 116 * @param cTest Number of entries in paTests. 117 * @param piFailed A index to paTests in the 118 * VERR_MANIFEST_DIGEST_MISMATCH error case 119 * (optional). 120 */ 121 RTR3DECL(int) RTManifestVerifyFilesBuf(void *pvBuf, size_t cbSize, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed); 122 123 /** 108 124 * Creates a manifest file in memory for a set of files. The manifest file 109 125 * contains SHA1 sums of every provided file and could be used to verify the … … 114 130 * @param ppvBuf Pointer to resulting memory buffer. 115 131 * @param pcbSize Pointer for the size of the memory buffer. 116 * @param papszFileNames Array of file names. 117 * @param papszFileDigests Array of file digests. 118 * @param cFiles Number of entries in papszFileNames and papszFileDigests. 132 * @param paFiles Array of file names and digests. 133 * @param cFiles Number of entries in paFiles. 119 134 */ 120 RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, const char * const *papszFileNames, const char * const *papszFileDigests, size_t cFiles);135 RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, PRTMANIFESTTEST paFiles, size_t cFiles); 121 136 122 137 /** @} */ -
trunk/include/iprt/tar.h
r32751 r33289 64 64 * @param fMode Open flags, i.e a combination of the RTFILE_O_* defines. 65 65 * The ACCESS, ACTION and DENY flags are mandatory! 66 */ 67 RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char* pszTarname, uint32_t fMode); 66 * @param fStream Open the file in stream mode. Within this mode no 67 * seeking is allowed. Use this together with 68 * RTTarFileCurrent, RTTarFileOpenCurrent, 69 * RTTarFileSeekNextFile and the read method to 70 * sequential read a tar file. Currently ignored with 71 * RTFILE_O_WRITE. 72 */ 73 RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char* pszTarname, uint32_t fMode, bool fStream); 68 74 69 75 /** … … 380 386 RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser); 381 387 388 /****************************************************************************** 389 * Streaming Functions * 390 ******************************************************************************/ 391 392 /** 393 * Return the filename where RTTar currently stays at. 394 * 395 * @returns IPRT status code. 396 * 397 * @param hTar Handle to the RTTAR interface. 398 * @param ppszFilename On success the filename. 399 */ 400 RTR3DECL(int) RTTarCurrentFile(RTTAR hTar, char **ppszFilename); 401 402 /** 403 * Jumps to the next file from the current RTTar position. 404 * 405 * @returns IPRT status code. 406 * 407 * @param hTar Handle to the RTTAR interface. 408 */ 409 RTR3DECL(int) RTTarSeekNextFile(RTTAR hTar); 410 411 /** 412 * Opens the file where RTTar currently stays at. 413 * 414 * @returns IPRT status code. 415 * 416 * @param hTar Handle to the RTTAR interface. 417 * @param phFile Where to store the handle to the opened file. 418 * @param ppszFilename On success the filename. 419 * @param fOpen Open flags, i.e a combination of the RTFILE_O_* defines. 420 * The ACCESS, ACTION flags are mandatory! Currently 421 * only RTFILE_O_OPEN | RTFILE_O_READ is supported. 422 */ 423 RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **ppszFilename, uint32_t fOpen); 424 425 382 426 /** @} */ 383 427 -
trunk/src/VBox/Main/ApplianceImpl.cpp
r33238 r33289 19 19 #include <iprt/path.h> 20 20 #include <iprt/cpp/utils.h> 21 #include <iprt/tar.h>22 21 23 22 #include <VBox/com/array.h> … … 633 632 634 633 /** 634 * Called from Appliance::importImpl() and Appliance::writeImpl() to set up a 635 * progress object with the proper weights and maximum progress values. 636 * 637 * @param pProgress 638 * @param bstrDescription 639 * @param mode 640 * @return 641 */ 642 HRESULT Appliance::setUpProgress(ComObjPtr<Progress> &pProgress, 643 const Bstr &bstrDescription, 644 SetUpProgressMode mode) 645 { 646 HRESULT rc; 647 648 /* Create the progress object */ 649 pProgress.createObject(); 650 651 // compute the disks weight (this sets ulTotalDisksMB and cDisks in the instance data) 652 disksWeight(); 653 654 m->ulWeightForManifestOperation = 0; 655 656 ULONG cOperations; 657 ULONG ulTotalOperationsWeight; 658 659 cOperations = 1 // one for XML setup 660 + m->cDisks; // plus one per disk 661 if (m->ulTotalDisksMB) 662 { 663 m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress for the XML 664 ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation; 665 } 666 else 667 { 668 // no disks to export: 669 m->ulWeightForXmlOperation = 1; 670 ulTotalOperationsWeight = 1; 671 } 672 673 switch (mode) 674 { 675 case ImportFile: 676 { 677 break; 678 } 679 case WriteFile: 680 { 681 // assume that creating the manifest will take .1% of the time it takes to export the disks 682 if (m->fManifest) 683 { 684 ++cOperations; // another one for creating the manifest 685 686 m->ulWeightForManifestOperation = (ULONG)((double)m->ulTotalDisksMB * .1 / 100); // use .5% of the progress for the manifest 687 ulTotalOperationsWeight += m->ulWeightForManifestOperation; 688 } 689 break; 690 } 691 case ImportS3: 692 { 693 cOperations += 1 + 1; // another one for the manifest file & another one for the import 694 ulTotalOperationsWeight = m->ulTotalDisksMB; 695 if (!m->ulTotalDisksMB) 696 // no disks to export: 697 ulTotalOperationsWeight = 1; 698 699 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import 700 ulTotalOperationsWeight += ulImportWeight; 701 702 m->ulWeightForXmlOperation = ulImportWeight; /* save for using later */ 703 704 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init 705 ulTotalOperationsWeight += ulInitWeight; 706 break; 707 } 708 case WriteS3: 709 { 710 cOperations += 1 + 1; // another one for the mf & another one for temporary creation 711 712 if (m->ulTotalDisksMB) 713 { 714 m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress for OVF file upload (we didn't know the size at this point) 715 ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation; 716 } 717 else 718 { 719 // no disks to export: 720 ulTotalOperationsWeight = 1; 721 m->ulWeightForXmlOperation = 1; 722 } 723 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */ 724 ulTotalOperationsWeight += ulOVFCreationWeight; 725 break; 726 } 727 } 728 729 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightForXmlOperation = %d\n", 730 m->ulTotalDisksMB, m->cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightForXmlOperation)); 731 732 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), 733 bstrDescription.raw(), 734 TRUE /* aCancelable */, 735 cOperations, // ULONG cOperations, 736 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight, 737 bstrDescription.raw(), // CBSTR bstrFirstOperationDescription, 738 m->ulWeightForXmlOperation); // ULONG ulFirstOperationWeight, 739 return rc; 740 } 741 742 /** 635 743 * Called from the import and export background threads to synchronize the second 636 744 * background disk thread's progress object with the current progress object so … … 746 854 } 747 855 748 /**749 * Called from Appliance::importImpl() and Appliance::writeImpl() to set up a750 * progress object with the proper weights and maximum progress values.751 *752 * @param pProgress753 * @param bstrDescription754 * @param mode755 * @return756 */757 HRESULT Appliance::setUpProgress(const LocationInfo &locInfo,758 ComObjPtr<Progress> &pProgress,759 const Bstr &bstrDescription,760 SetUpProgressMode mode)761 {762 HRESULT rc;763 764 /* Create the progress object */765 pProgress.createObject();766 767 // compute the disks weight (this sets ulTotalDisksMB and cDisks in the instance data)768 disksWeight();769 770 m->ulWeightForManifestOperation = 0;771 772 ULONG cOperations;773 ULONG ulTotalOperationsWeight;774 775 cOperations = 1 // one for XML setup776 + m->cDisks; // plus one per disk777 if (m->ulTotalDisksMB)778 {779 m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress for the XML780 ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation;781 }782 else783 {784 // no disks to export:785 m->ulWeightForXmlOperation = 1;786 ulTotalOperationsWeight = 1;787 }788 789 bool fOVA = locInfo.strPath.endsWith(".ova", Utf8Str::CaseInsensitive);790 switch (mode)791 {792 case ImportFileNoManifest:793 {794 if (fOVA)795 {796 // Another operation for packing797 ++cOperations;798 799 // assume that packing the files into the archive has the same weight than creating all files in the ovf exporting step800 ulTotalOperationsWeight += m->ulTotalDisksMB;801 }802 break;803 }804 case ImportFileWithManifest:805 {806 ++cOperations; // another one for creating the manifest807 808 // assume that creating the manifest will take 10% of the time it takes to export the disks809 m->ulWeightForManifestOperation = m->ulTotalDisksMB / 10;810 ulTotalOperationsWeight += m->ulWeightForManifestOperation;811 if (fOVA)812 {813 // Another operation for packing814 ++cOperations;815 816 // assume that packing the files into the archive has the same weight than creating all files in the ovf exporting step817 ulTotalOperationsWeight += m->ulTotalDisksMB;818 }819 break;820 }821 case WriteFile:822 {823 // assume that creating the manifest will take .1% of the time it takes to export the disks824 if (m->fManifest)825 {826 ++cOperations; // another one for creating the manifest827 828 m->ulWeightForManifestOperation = (ULONG)((double)m->ulTotalDisksMB * .1 / 100); // use .5% of the progress for the manifest829 ulTotalOperationsWeight += m->ulWeightForManifestOperation;830 }831 break;832 }833 case ImportS3:834 {835 cOperations += 1 + 1; // another one for the manifest file & another one for the import836 ulTotalOperationsWeight = m->ulTotalDisksMB;837 if (!m->ulTotalDisksMB)838 // no disks to export:839 ulTotalOperationsWeight = 1;840 841 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import842 ulTotalOperationsWeight += ulImportWeight;843 844 m->ulWeightForXmlOperation = ulImportWeight; /* save for using later */845 846 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init847 ulTotalOperationsWeight += ulInitWeight;848 break;849 }850 case WriteS3:851 {852 cOperations += 1 + 1; // another one for the mf & another one for temporary creation853 854 if (m->ulTotalDisksMB)855 {856 m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100); // use 1% of the progress for OVF file upload (we didn't know the size at this point)857 ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation;858 }859 else860 {861 // no disks to export:862 ulTotalOperationsWeight = 1;863 m->ulWeightForXmlOperation = 1;864 }865 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */866 ulTotalOperationsWeight += ulOVFCreationWeight;867 break;868 }869 }870 871 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightForXmlOperation = %d\n",872 m->ulTotalDisksMB, m->cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightForXmlOperation));873 874 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),875 bstrDescription.raw(),876 TRUE /* aCancelable */,877 cOperations, // ULONG cOperations,878 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,879 bstrDescription.raw(), // CBSTR bstrFirstOperationDescription,880 m->ulWeightForXmlOperation); // ULONG ulFirstOperationWeight,881 return rc;882 }883 884 856 void Appliance::parseURI(Utf8Str strUri, LocationInfo &locInfo) const 885 857 { … … 947 919 } 948 920 949 Utf8Str Appliance::manifestFileName(const Utf8Str& aPath) const950 {951 Utf8Str strTmpPath = aPath;952 /* Get the name part */953 char *pszMfName = RTStrDup(RTPathFilename(strTmpPath.c_str()));954 /* Strip any extensions */955 RTPathStripExt(pszMfName);956 /* Path without the filename */957 strTmpPath.stripFilename();958 /* Format the manifest path */959 Utf8StrFmt strMfFile("%s/%s.mf", strTmpPath.c_str(), pszMfName);960 RTStrFree(pszMfName);961 return strMfFile;962 }963 964 921 /** 965 922 * … … 1005 962 case TaskOVF::Read: 1006 963 if (task->locInfo.storageType == VFSType_File) 1007 taskrc = pAppliance->readFS(task ->locInfo, task->pProgress);964 taskrc = pAppliance->readFS(task.get()); 1008 965 else if (task->locInfo.storageType == VFSType_S3) 1009 966 taskrc = pAppliance->readS3(task.get()); -
trunk/src/VBox/Main/ApplianceImplExport.cpp
r33232 r33289 613 613 //////////////////////////////////////////////////////////////////////////////// 614 614 615 /******************************************************************************* 616 * Export stuff 617 ******************************************************************************/ 618 615 619 /** 616 620 * Implementation for writing out the OVF to disk. This starts a new thread which will call … … 636 640 try 637 641 { 638 rc = setUpProgress(aLocInfo, 639 aProgress, 642 rc = setUpProgress(aProgress, 640 643 BstrFmt(tr("Export appliance '%s'"), aLocInfo.strPath.c_str()), 641 644 (aLocInfo.storageType == VFSType_File) ? WriteFile : WriteS3); … … 1658 1661 1659 1662 RTTAR tar; 1660 int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL );1663 int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL, false); 1661 1664 if (RT_FAILURE(vrc)) 1662 1665 return setError(VBOX_E_FILE_ERROR, … … 1719 1722 HRESULT rc = S_OK; 1720 1723 1721 typedef pair<Utf8Str, Utf8Str> STRPAIR;1722 1724 list<STRPAIR> fileList; 1723 1725 try … … 1837 1839 { 1838 1840 // Create & write the manifest file 1839 Utf8Str strMfFilePath = manifestFileName(pTask->locInfo.strPath.c_str());1841 Utf8Str strMfFilePath = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf"); 1840 1842 Utf8Str strMfFileName = Utf8Str(strMfFilePath) 1841 1843 .stripPath(); 1842 1844 pTask->pProgress->SetNextOperation(BstrFmt(tr("Creating manifest file '%s'"), strMfFileName.c_str()).raw(), 1843 1845 m->ulWeightForManifestOperation); // operation's weight, as set up with the IProgress originally); 1844 const char** ppManifestFiles = (const char**)RTMemAlloc(sizeof(char*) * fileList.size()); 1845 const char** ppManifestDigests = (const char**)RTMemAlloc(sizeof(char*) * fileList.size()); 1846 PRTMANIFESTTEST paManifestFiles = (PRTMANIFESTTEST)RTMemAlloc(sizeof(RTMANIFESTTEST) * fileList.size()); 1846 1847 size_t i = 0; 1847 1848 list<STRPAIR>::const_iterator it1; … … 1850 1851 ++it1, ++i) 1851 1852 { 1852 p pManifestFiles[i]= (*it1).first.c_str();1853 p pManifestDigests[i]= (*it1).second.c_str();1853 paManifestFiles[i].pszTestFile = (*it1).first.c_str(); 1854 paManifestFiles[i].pszTestDigest = (*it1).second.c_str(); 1854 1855 } 1855 1856 void *pvBuf; 1856 1857 size_t cbSize; 1857 vrc = RTManifestWriteFilesBuf(&pvBuf, &cbSize, ppManifestFiles, ppManifestDigests, fileList.size()); 1858 RTMemFree(ppManifestFiles); 1859 RTMemFree(ppManifestDigests); 1858 vrc = RTManifestWriteFilesBuf(&pvBuf, &cbSize, paManifestFiles, fileList.size()); 1859 RTMemFree(paManifestFiles); 1860 1860 if (RT_FAILURE(vrc)) 1861 1861 throw setError(VBOX_E_FILE_ERROR, … … 1975 1975 if (m->fManifest) 1976 1976 { 1977 Utf8Str strMfFile = manifestFileName(strTmpOvf);1977 Utf8Str strMfFile = Utf8Str(strTmpOvf).stripExt().append(".mf"); 1978 1978 filesList.push_back(pair<Utf8Str, ULONG>(strMfFile , m->ulWeightForXmlOperation)); /* Use 1% of the total for the manifest file upload */ 1979 1979 } -
trunk/src/VBox/Main/ApplianceImplIO.cpp
r33090 r33289 31 31 #include <iprt/semaphore.h> 32 32 #include <iprt/stream.h> 33 #include <iprt/circbuf.h> 33 34 #include <VBox/VBoxHDD.h> 34 35 … … 57 58 /** Completion callback. */ 58 59 PFNVDCOMPLETED pfnCompleted; 60 /** Our own storage handle. */ 61 PSHA1STORAGE pSha1Storage; 59 62 /** Storage handle for the next callback in chain. */ 60 63 void *pvStorage; … … 83 86 /** SHA1 calculation context. */ 84 87 RTSHA1CONTEXT ctx; 88 /* Circular buffer in read mode. */ 89 PRTCIRCBUF pCircBuf; 90 /* Are we reached end of file. */ 91 volatile bool fEOF; 92 // uint64_t calls; 93 // uint64_t waits; 85 94 } SHA1STORAGEINTERNAL, *PSHA1STORAGEINTERNAL; 86 95 … … 92 101 #define STATUS_CALC UINT32_C(1) 93 102 #define STATUS_END UINT32_C(3) 103 #define STATUS_READ UINT32_C(4) 94 104 95 105 /* Enable for getting some flow history. */ … … 227 237 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); 228 238 229 DEBUG_PRINT_FLOW();239 // DEBUG_PRINT_FLOW(); 230 240 231 241 PRTFILESTORAGEINTERNAL pInt = (PRTFILESTORAGEINTERNAL)pvStorage; … … 257 267 AssertPtrReturn(ppInt, VERR_INVALID_POINTER); 258 268 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER); 269 // AssertReturn(!(fOpen & RTFILE_O_READWRITE), VERR_INVALID_PARAMETER); 259 270 260 271 RTTAR tar = (RTTAR)pvUser; … … 268 279 pInt->pfnCompleted = pfnCompleted; 269 280 270 int rc = RTTarFileOpen(tar, &pInt->file, RTPathFilename(pszLocation), fOpen); 281 int rc = VINF_SUCCESS; 282 283 if ( fOpen & RTFILE_O_READ 284 && !(fOpen & RTFILE_O_WRITE)) 285 { 286 /* Read only is a little bit more complicated than writing, cause we 287 * need streaming functionality. First try to open the file on the 288 * current file position. If this is the file the caller requested, we 289 * are fine. If not seek to the next file in the stream and check 290 * again. This is repeated until EOF of the OVA. */ 291 /* 292 * 293 * 294 * TODO: recheck this with more VDMKs (or what else) in an test OVA. 295 * 296 * 297 */ 298 bool fFound = false; 299 for(;;) 300 { 301 char *pszFilename = 0; 302 rc = RTTarCurrentFile(tar, &pszFilename); 303 if (RT_SUCCESS(rc)) 304 { 305 fFound = !strcmp(pszFilename, RTPathFilename(pszLocation)); 306 RTStrFree(pszFilename); 307 if (fFound) 308 break; 309 else 310 { 311 rc = RTTarSeekNextFile(tar); 312 if (RT_FAILURE(rc)) 313 break; 314 } 315 }else 316 break; 317 } 318 if (fFound) 319 rc = RTTarFileOpenCurrentFile(tar, &pInt->file, 0, fOpen); 320 } 321 else 322 rc = RTTarFileOpen(tar, &pInt->file, RTPathFilename(pszLocation), fOpen); 271 323 272 324 if (RT_FAILURE(rc)) … … 425 477 /* What should we do next? */ 426 478 uint32_t u32Status = ASMAtomicReadU32(&pInt->u32Status); 479 // RTPrintf("status: %d\n", u32Status); 427 480 switch (u32Status) 428 481 { … … 442 495 /* Reset the thread status and signal the main thread that we 443 496 are finished. */ 444 ASMAtomic WriteU32(&pInt->u32Status, STATUS_WAIT);497 ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WAIT, STATUS_CALC); 445 498 rc = RTSemEventSignal(pInt->calcFinishedEvent); 446 499 break; … … 452 505 break; 453 506 } 507 case STATUS_READ: 508 { 509 PVDINTERFACE pIO = VDInterfaceGet(pInt->pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO); 510 AssertPtrReturn(pIO, VERR_INVALID_PARAMETER); 511 PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO); 512 AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER); 513 514 size_t cbAvail = RTCircBufFree(pInt->pCircBuf); 515 // RTPrintf("############################################################################### th: avail %ld\n", cbAvail); 516 517 /* ************ CHECK for 0 */ 518 /* First loop over all the available memory in the circular 519 * memory buffer (could be turn around at the end). */ 520 size_t cbMemAllWrite = 0; 521 bool fStop = false; 522 bool fEOF = false; 523 for(;;) 524 { 525 if ( cbMemAllWrite == cbAvail 526 || fStop == true) 527 break; 528 char *pcBuf; 529 size_t cbMemToWrite = cbAvail - cbMemAllWrite; 530 size_t cbMemWrite = 0; 531 /* Try to acquire all the free space of the circular buffer. */ 532 RTCircBufAcquireWriteBlock(pInt->pCircBuf, cbMemToWrite, (void**)&pcBuf, &cbMemWrite); 533 /* Second, read as long as we filled all the memory. The 534 * read method could also split the reads up into to 535 * smaller parts. */ 536 size_t cbAllRead = 0; 537 for(;;) 538 { 539 if (cbAllRead == cbMemWrite) 540 break; 541 size_t cbToRead = cbMemWrite - cbAllRead; 542 size_t cbRead = 0; 543 rc = pCallbacks->pfnReadSync(pIO->pvUser, pInt->pvStorage, pInt->cbCurFile, &pcBuf[cbAllRead], cbToRead, &cbRead); 544 // RTPrintf ("%lu %lu %lu %Rrc\n", pInt->cbCurFile, cbToRead, cbRead, rc); 545 if (RT_FAILURE(rc)) 546 { 547 fLoop = false; 548 fStop = true; 549 break; 550 } 551 if (cbRead == 0) 552 { 553 fStop = true; 554 fLoop = false; 555 fEOF = true; 556 // RTPrintf("EOF\n"); 557 break; 558 } 559 cbAllRead += cbRead; 560 pInt->cbCurFile += cbRead; 561 } 562 /* Update the SHA1 context with the next data block. */ 563 RTSha1Update(&pInt->ctx, pcBuf, cbAllRead); 564 /* Mark the block as full. */ 565 RTCircBufReleaseWriteBlock(pInt->pCircBuf, cbAllRead); 566 cbMemAllWrite += cbAllRead; 567 } 568 if (fEOF) 569 ASMAtomicWriteBool(&pInt->fEOF, true); 570 /* Reset the thread status and signal the main thread that we 571 are finished. */ 572 ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WAIT, STATUS_READ); 573 rc = RTSemEventSignal(pInt->calcFinishedEvent); 574 break; 575 } 454 576 } 455 577 } … … 470 592 { 471 593 // RTPrintf(" wait\n"); 472 if (ASMAtomicReadU32(&pInt->u32Status) != STATUS_CALC) 594 if (!( ASMAtomicReadU32(&pInt->u32Status) == STATUS_CALC 595 || ASMAtomicReadU32(&pInt->u32Status) == STATUS_READ)) 473 596 break; 474 597 rc = RTSemEventWait(pInt->calcFinishedEvent, 100); … … 523 646 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER); 524 647 AssertPtrReturn(ppInt, VERR_INVALID_POINTER); 648 AssertReturn((fOpen & RTFILE_O_READWRITE) != RTFILE_O_READWRITE, VERR_INVALID_PARAMETER); /* No read/write allowed */ 525 649 526 650 PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser; … … 540 664 { 541 665 pInt->pfnCompleted = pfnCompleted; 542 /* For caching reasons and to be able to calculate the sha1 sum of the543 data we need a memory buffer. */544 pInt->cbBuf = _1M; 545 pInt->pcBuf = (char*)RTMemAlloc(pInt->cbBuf); 546 if ( !pInt->pcBuf)666 pInt->pSha1Storage = pSha1Storage; 667 pInt->fEOF = false; 668 669 670 if (fOpen & RTFILE_O_READ) 547 671 { 548 rc = VERR_NO_MEMORY; 549 break; 672 /* Circular buffer in the read case. */ 673 rc = RTCircBufCreate(&pInt->pCircBuf, _1M * 2); 674 if (RT_FAILURE(rc)) 675 break; 550 676 } 551 /* The zero buffer is used for appending empty parts at the end of the 552 * file (or our buffer) in setSize or when uOffset in writeSync is 553 * increased in steps bigger than a byte. */ 554 pInt->cbZeroBuf = _1K; 555 pInt->pvZeroBuf = RTMemAllocZ(pInt->cbZeroBuf); 556 if (!pInt->pvZeroBuf) 677 else if (fOpen & RTFILE_O_WRITE) 557 678 { 558 rc = VERR_NO_MEMORY; 559 break; 679 /* For caching reasons and to be able to calculate the sha1 sum of the 680 data we need a memory buffer. */ 681 pInt->cbBuf = _1M; 682 pInt->pcBuf = (char*)RTMemAlloc(pInt->cbBuf); 683 if (!pInt->pcBuf) 684 { 685 rc = VERR_NO_MEMORY; 686 break; 687 } 688 /* The zero buffer is used for appending empty parts at the end of the 689 * file (or our buffer) in setSize or when uOffset in writeSync is 690 * increased in steps bigger than a byte. */ 691 pInt->cbZeroBuf = _1K; 692 pInt->pvZeroBuf = RTMemAllocZ(pInt->cbZeroBuf); 693 if (!pInt->pvZeroBuf) 694 { 695 rc = VERR_NO_MEMORY; 696 break; 697 } 560 698 } 561 699 562 if (pSha1Storage->fCreateDigest) 700 if ( fOpen & RTFILE_O_READ 701 || pSha1Storage->fCreateDigest) 563 702 { 564 /* Create a sha1 context the sha1 worker thread will work with. */565 RTSha1Init(&pInt->ctx);566 703 /* Create an event semaphore to indicate a state change for the sha1 567 704 worker thread. */ … … 579 716 break; 580 717 } 718 719 if (pSha1Storage->fCreateDigest) 720 /* Create a sha1 context the sha1 worker thread will work with. */ 721 RTSha1Init(&pInt->ctx); 722 581 723 /* Open the file. */ 582 724 rc = pCallbacks->pfnOpen(pIO->pvUser, pszLocation, … … 588 730 if (RT_FAILURE(rc)) 589 731 { 590 if (p Sha1Storage->fCreateDigest)732 if (pInt->pMfThread) 591 733 { 592 if (pInt->pMfThread) 593 { 594 sha1SignalManifestThread(pInt, STATUS_END); 595 RTThreadWait(pInt->pMfThread, RT_INDEFINITE_WAIT, 0); 596 } 597 if (pInt->calcFinishedEvent) 598 RTSemEventDestroy(pInt->calcFinishedEvent); 599 if (pInt->newStatusEvent) 600 RTSemEventDestroy(pInt->newStatusEvent); 734 sha1SignalManifestThread(pInt, STATUS_END); 735 RTThreadWait(pInt->pMfThread, RT_INDEFINITE_WAIT, 0); 601 736 } 737 if (pInt->calcFinishedEvent) 738 RTSemEventDestroy(pInt->calcFinishedEvent); 739 if (pInt->newStatusEvent) 740 RTSemEventDestroy(pInt->newStatusEvent); 741 if (pInt->pCircBuf) 742 RTCircBufDestroy(pInt->pCircBuf); 602 743 if (pInt->pvZeroBuf) 603 744 RTMemFree(pInt->pvZeroBuf); … … 634 775 rc = sha1FlushCurBuf(pIO, pCallbacks, pInt, pSha1Storage->fCreateDigest); 635 776 636 if (pSha1Storage->fCreateDigest) 637 { 777 if (pInt->pMfThread) 638 778 /* Signal the worker thread to end himself */ 639 779 rc = sha1SignalManifestThread(pInt, STATUS_END); 780 781 if (pSha1Storage->fCreateDigest) 782 { 640 783 /* Finally calculate & format the SHA1 sum */ 641 784 unsigned char auchDig[RTSHA1_HASH_SIZE]; … … 650 793 RTStrFree(pszDigest); 651 794 } 795 } 796 797 if (pInt->pMfThread) 652 798 /* Worker thread stopped? */ 653 799 rc = RTThreadWait(pInt->pMfThread, RT_INDEFINITE_WAIT, 0); 654 } 800 655 801 /* Close the file */ 656 802 rc = pCallbacks->pfnClose(pIO->pvUser, pInt->pvStorage); 657 803 804 // RTPrintf("%lu %lu\n", pInt->calls, pInt->waits); 805 658 806 /* Cleanup */ 659 if (pSha1Storage->fCreateDigest) 660 { 661 if (pInt->calcFinishedEvent) 662 RTSemEventDestroy(pInt->calcFinishedEvent); 663 if (pInt->newStatusEvent) 664 RTSemEventDestroy(pInt->newStatusEvent); 665 } 807 if (pInt->calcFinishedEvent) 808 RTSemEventDestroy(pInt->calcFinishedEvent); 809 if (pInt->newStatusEvent) 810 RTSemEventDestroy(pInt->newStatusEvent); 811 if (pInt->pCircBuf) 812 RTCircBufDestroy(pInt->pCircBuf); 666 813 if (pInt->pvZeroBuf) 667 814 RTMemFree(pInt->pvZeroBuf); … … 760 907 761 908 *pcbSize = RT_MAX(pInt->cbCurAll, cbSize); 909 762 910 return VINF_SUCCESS; 763 911 } … … 870 1018 PSHA1STORAGEINTERNAL pInt = (PSHA1STORAGEINTERNAL)pvStorage; 871 1019 872 return pCallbacks->pfnReadSync(pIO->pvUser, pInt->pvStorage, uOffset, pvBuf, cbRead, pcbRead); 1020 int rc = VINF_SUCCESS; 1021 1022 // pInt->calls++; 1023 // RTPrintf("Read uOffset: %7lu cbRead: %7lu = %7lu\n", uOffset, cbRead, uOffset + cbRead); 1024 1025 /* Check if we jump forward in the file. If so we have to read the 1026 * remaining stuff in the gap anyway (SHA1; streaming). */ 1027 if (pInt->cbCurAll < uOffset) 1028 { 1029 rc = sha1ReadSyncCallback(pvUser, pvStorage, pInt->cbCurAll, 0, uOffset - pInt->cbCurAll, 0); 1030 if (RT_FAILURE(rc)) 1031 return rc; 1032 } 1033 1034 size_t cbAllRead = 0; 1035 for(;;) 1036 { 1037 /* Finished? */ 1038 if (cbAllRead == cbRead) 1039 break; 1040 size_t cbAvail = RTCircBufUsed(pInt->pCircBuf); 1041 if ( cbAvail == 0 1042 && pInt->fEOF) 1043 return VERR_EOF; 1044 /* If there isn't enough data make sure the worker thread is fetching 1045 * more. */ 1046 if ((cbRead - cbAllRead) > cbAvail) 1047 { 1048 rc = sha1SignalManifestThread(pInt, STATUS_READ); 1049 if(RT_FAILURE(rc)) 1050 break; 1051 /* If there is _no_ data available, we have to wait until it is. */ 1052 if (cbAvail == 0) 1053 { 1054 rc = sha1WaitForManifestThreadFinished(pInt); 1055 if (RT_FAILURE(rc)) 1056 break; 1057 cbAvail = RTCircBufUsed(pInt->pCircBuf); 1058 // RTPrintf("############## wait %lu %lu %lu \n", cbRead, cbAllRead, cbAvail); 1059 // pInt->waits++; 1060 } 1061 } 1062 size_t cbToRead = RT_MIN(cbRead - cbAllRead, cbAvail); 1063 char *pcBuf; 1064 size_t cbMemRead = 0; 1065 /* Acquire a block for reading from our circular buffer. */ 1066 RTCircBufAcquireReadBlock(pInt->pCircBuf, cbToRead, (void**)&pcBuf, &cbMemRead); 1067 if (pvBuf) /* Make it possible to blind read data (for skipping) */ 1068 memcpy(&((char*)pvBuf)[cbAllRead], pcBuf, cbMemRead); 1069 /* Mark the block as empty again. */ 1070 RTCircBufReleaseReadBlock(pInt->pCircBuf, cbMemRead); 1071 cbAllRead += cbMemRead; 1072 pInt->cbCurAll += cbMemRead; 1073 } 1074 1075 if (pcbRead) 1076 *pcbRead = cbAllRead; 1077 1078 if (rc == VERR_EOF) 1079 rc = VINF_SUCCESS; 1080 1081 /* Signal the thread to read more data in the mean time. */ 1082 if ( RT_SUCCESS(rc) 1083 && RTCircBufFree(pInt->pCircBuf) >= (RTCircBufSize(pInt->pCircBuf) / 2)) 1084 rc = sha1SignalManifestThread(pInt, STATUS_READ); 1085 1086 return rc; 873 1087 } 874 1088 … … 975 1189 } 976 1190 1191 int Sha1ReadBuf(const char *pcszFilename, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, void *pvUser) 1192 { 1193 /* Validate input. */ 1194 AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER); 1195 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); 1196 AssertPtrReturn(pCallbacks, VERR_INVALID_POINTER); 1197 1198 void *pvStorage; 1199 int rc = pCallbacks->pfnOpen(pvUser, pcszFilename, 1200 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, 0, 1201 &pvStorage); 1202 if (RT_FAILURE(rc)) 1203 return rc; 1204 1205 void *pvBuf = 0; 1206 uint64_t cbSize = 0; 1207 do 1208 { 1209 rc = pCallbacks->pfnGetSize(pvUser, pvStorage, &cbSize); 1210 if (RT_FAILURE(rc)) 1211 break; 1212 1213 pvBuf = RTMemAlloc(cbSize); 1214 if (!pvBuf) 1215 { 1216 rc = VERR_NO_MEMORY; 1217 break; 1218 } 1219 1220 size_t cbAllRead = 0; 1221 for(;;) 1222 { 1223 if (cbAllRead == cbSize) 1224 break; 1225 size_t cbToRead = cbSize - cbAllRead; 1226 size_t cbRead = 0; 1227 rc = pCallbacks->pfnReadSync(pvUser, pvStorage, cbAllRead, &((char*)pvBuf)[cbAllRead], cbToRead, &cbRead); 1228 if (RT_FAILURE(rc)) 1229 break; 1230 cbAllRead += cbRead; 1231 } 1232 }while(0); 1233 1234 pCallbacks->pfnClose(pvUser, pvStorage); 1235 1236 if (RT_SUCCESS(rc)) 1237 { 1238 *ppvBuf = pvBuf; 1239 *pcbSize = cbSize; 1240 }else 1241 { 1242 if (pvBuf) 1243 RTMemFree(pvBuf); 1244 } 1245 1246 return rc; 1247 } 1248 977 1249 int Sha1WriteBuf(const char *pcszFilename, void *pvBuf, size_t cbSize, PVDINTERFACEIO pCallbacks, void *pvUser) 978 1250 { 979 1251 /* Validate input. */ 980 AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER); 1252 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 1253 AssertReturn(cbSize, VERR_INVALID_PARAMETER); 1254 AssertPtrReturn(pCallbacks, VERR_INVALID_POINTER); 981 1255 982 1256 void *pvStorage; -
trunk/src/VBox/Main/ApplianceImplImport.cpp
r33238 r33289 26 26 #include <iprt/stream.h> 27 27 28 #include <VBox/VBoxHDD.h> 28 29 #include <VBox/com/array.h> 29 30 … … 33 34 #include "ProgressImpl.h" 34 35 #include "MachineImpl.h" 36 #include "MediumImpl.h" 37 #include "MediumFormatImpl.h" 38 #include "SystemPropertiesImpl.h" 35 39 36 40 #include "AutoCaller.h" … … 77 81 } 78 82 79 // see if we can handle this file; for now we insist it has an ".ovf"extension83 // see if we can handle this file; for now we insist it has an ovf/ova extension 80 84 Utf8Str strPath (path); 81 85 if (!( strPath.endsWith(".ovf", Utf8Str::CaseInsensitive) … … 180 184 nameVBox); 181 185 186 /* Based on the VM name, create a target machine path. */ 187 Bstr bstrMachineFilename; 188 rc = mVirtualBox->ComposeMachineFilename(Bstr(nameVBox).raw(), 189 NULL, 190 bstrMachineFilename.asOutParam()); 191 if (FAILED(rc)) throw rc; 192 /* Determine the machine folder from that */ 193 Utf8Str strMachineFolder = Utf8Str(bstrMachineFilename).stripFilename(); 194 182 195 /* VM Product */ 183 196 if (!vsysThis.strProduct.isEmpty()) … … 236 249 /* CPU count */ 237 250 ULONG cpuCountVBox = vsysThis.cCPUs; 238 /* Check for the constrain s */251 /* Check for the constraints */ 239 252 if (cpuCountVBox > SchemaDefs::MaxCPUCount) 240 253 { … … 496 509 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str()); 497 510 511 Utf8Str strTargetPath = Utf8Str(strMachineFolder) 512 .append(RTPATH_DELIMITER) 513 .append(di.strHref);; 514 searchUniqueDiskImageFilePath(strTargetPath); 515 498 516 /* find the description for the hard disk controller 499 517 * that has the same ID as hd.idController */ … … 512 530 hd.strDiskId, 513 531 di.strHref, 514 str Filename,532 strTargetPath, 515 533 di.ulSuggestedSizeMB, 516 534 strExtraConfig); … … 586 604 // 587 605 //////////////////////////////////////////////////////////////////////////////// 606 607 608 /******************************************************************************* 609 * Read stuff 610 ******************************************************************************/ 588 611 589 612 /** … … 655 678 * @return 656 679 */ 657 HRESULT Appliance::readFS(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress) 658 { 659 if (locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)) 660 return readFSOVF(locInfo, pProgress); 661 else 662 return readFSOVA(locInfo, pProgress); 663 } 664 665 HRESULT Appliance::readFSOVF(const LocationInfo &locInfo, ComObjPtr<Progress> & /* pProgress */) 680 HRESULT Appliance::readFS(TaskOVF *pTask) 666 681 { 667 682 LogFlowFuncEnter(); … … 675 690 HRESULT rc = S_OK; 676 691 692 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)) 693 rc = readFSOVF(pTask); 694 else 695 rc = readFSOVA(pTask); 696 697 LogFlowFunc(("rc=%Rhrc\n", rc)); 698 LogFlowFuncLeave(); 699 700 return rc; 701 } 702 703 HRESULT Appliance::readFSOVF(TaskOVF *pTask) 704 { 705 LogFlowFuncEnter(); 706 707 HRESULT rc = S_OK; 708 709 PVDINTERFACEIO pSha1Callbacks = 0; 710 PVDINTERFACEIO pRTFileCallbacks = 0; 711 do 712 { 713 pSha1Callbacks = Sha1CreateInterface(); 714 if (!pSha1Callbacks) 715 { 716 rc = E_OUTOFMEMORY; 717 break; 718 } 719 pRTFileCallbacks = RTFileCreateInterface(); 720 if (!pRTFileCallbacks) 721 { 722 rc = E_OUTOFMEMORY; 723 break; 724 } 725 VDINTERFACE VDInterfaceIO; 726 SHA1STORAGE storage; 727 RT_ZERO(storage); 728 int vrc = VDInterfaceAdd(&VDInterfaceIO, "Appliance::IORTFile", 729 VDINTERFACETYPE_IO, pRTFileCallbacks, 730 0, &storage.pVDImageIfaces); 731 if (RT_FAILURE(vrc)) 732 { 733 rc = E_FAIL; 734 break; 735 } 736 rc = readFSImpl(pTask, pSha1Callbacks, &storage); 737 }while(0); 738 739 /* Cleanup */ 740 if (pSha1Callbacks) 741 RTMemFree(pSha1Callbacks); 742 if (pRTFileCallbacks) 743 RTMemFree(pRTFileCallbacks); 744 745 LogFlowFunc(("rc=%Rhrc\n", rc)); 746 LogFlowFuncLeave(); 747 748 return rc; 749 } 750 751 HRESULT Appliance::readFSOVA(TaskOVF *pTask) 752 { 753 LogFlowFuncEnter(); 754 755 RTTAR tar; 756 int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true); 757 if (RT_FAILURE(vrc)) 758 return setError(VBOX_E_FILE_ERROR, 759 tr("Could not open OVA file '%s' (%Rrc)"), 760 pTask->locInfo.strPath.c_str(), vrc); 761 762 HRESULT rc = S_OK; 763 764 PVDINTERFACEIO pSha1Callbacks = 0; 765 PVDINTERFACEIO pRTTarCallbacks = 0; 766 do 767 { 768 pSha1Callbacks = Sha1CreateInterface(); 769 if (!pSha1Callbacks) 770 { 771 rc = E_OUTOFMEMORY; 772 break; 773 } 774 pRTTarCallbacks = RTTarCreateInterface(); 775 if (!pRTTarCallbacks) 776 { 777 rc = E_OUTOFMEMORY; 778 break; 779 } 780 VDINTERFACE VDInterfaceIO; 781 SHA1STORAGE storage; 782 RT_ZERO(storage); 783 vrc = VDInterfaceAdd(&VDInterfaceIO, "Appliance::IORTTar", 784 VDINTERFACETYPE_IO, pRTTarCallbacks, 785 tar, &storage.pVDImageIfaces); 786 if (RT_FAILURE(vrc)) 787 { 788 rc = E_FAIL; 789 break; 790 } 791 rc = readFSImpl(pTask, pSha1Callbacks, &storage); 792 }while(0); 793 794 RTTarClose(tar); 795 796 /* Cleanup */ 797 if (pSha1Callbacks) 798 RTMemFree(pSha1Callbacks); 799 if (pRTTarCallbacks) 800 RTMemFree(pRTTarCallbacks); 801 802 LogFlowFunc(("rc=%Rhrc\n", rc)); 803 LogFlowFuncLeave(); 804 805 return rc; 806 } 807 808 HRESULT Appliance::readFSImpl(TaskOVF *pTask, PVDINTERFACEIO pCallbacks, PSHA1STORAGE pStorage) 809 { 810 LogFlowFuncEnter(); 811 812 HRESULT rc = S_OK; 813 814 pStorage->fCreateDigest = true; 815 816 void *pvTmpBuf = 0; 677 817 try 678 818 { 679 /* Read & parse the XML structure of the OVF file */ 680 m->pReader = new ovf::OVFReader(locInfo.strPath); 681 /* Create the SHA1 sum of the OVF file for later validation */ 682 char *pszDigest; 683 int vrc = RTSha1DigestFromFile(locInfo.strPath.c_str(), &pszDigest, NULL, NULL); 819 Utf8Str strOvfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".ovf"); 820 /* Read the OVF into a memory buffer */ 821 size_t cbSize = 0; 822 int vrc = Sha1ReadBuf(strOvfFile.c_str(), &pvTmpBuf, &cbSize, pCallbacks, pStorage); 684 823 if (RT_FAILURE(vrc)) 685 824 throw setError(VBOX_E_FILE_ERROR, 686 tr("Couldn't calculate SHA1 digest for file '%s' (%Rrc)"), 687 RTPathFilename(locInfo.strPath.c_str()), vrc); 688 m->strOVFSHA1Digest = pszDigest; 689 RTStrFree(pszDigest); 825 tr("Could not read OVF file '%s' (%Rrc)"), 826 RTPathFilename(strOvfFile.c_str()), vrc); 827 /* Copy the SHA1 sum of the OVF file for later validation */ 828 m->strOVFSHA1Digest = pStorage->strDigest; 829 /* Read & parse the XML structure of the OVF file */ 830 m->pReader = new ovf::OVFReader(pvTmpBuf, cbSize, pTask->locInfo.strPath); 690 831 } 691 832 catch (iprt::Error &x) // includes all XML exceptions … … 699 840 } 700 841 701 LogFlowFunc(("rc=%Rhrc\n", rc)); 702 LogFlowFuncLeave(); 703 704 return rc; 705 } 706 707 HRESULT Appliance::readFSOVA(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress) 708 { 709 LogFlowFuncEnter(); 710 LogFlowFunc(("Appliance %p\n", this)); 711 712 AutoCaller autoCaller(this); 713 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 714 715 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS); 716 HRESULT rc = S_OK; 717 void *pvBuf = 0; 718 719 try 720 { 721 Utf8Str tmpPath = locInfo.strPath; 722 /* Remove the ova extension */ 723 tmpPath.stripExt(); 724 /* add the ovf extension. */ 725 tmpPath += ".ovf"; 726 char* pcszOVFName = RTPathFilename(tmpPath.c_str()); 727 728 /* Read the OVF into a memory buffer */ 729 size_t cbSize; 730 int vrc = RTTarExtractFileToBuf(locInfo.strPath.c_str(), &pvBuf, &cbSize, pcszOVFName, 0, 0); 731 if (RT_FAILURE(vrc)) 732 { 733 if (vrc == VERR_FILE_NOT_FOUND) 734 throw setError(VBOX_E_IPRT_ERROR, 735 tr("Can't find ovf file '%s' in archive '%s' (%Rrc)"), pcszOVFName, locInfo.strPath.c_str(), vrc); 736 else 737 throw setError(VBOX_E_IPRT_ERROR, 738 tr("Can't unpack the archive file '%s' (%Rrc)"), locInfo.strPath.c_str(), vrc); 739 } 740 741 /* Read & parse the XML structure of the OVF file */ 742 m->pReader = new ovf::OVFReader(pvBuf, cbSize, locInfo.strPath); 743 /* Create the SHA1 sum of the OVF file for later validation */ 744 char *pszDigest; 745 vrc = RTSha1Digest(pvBuf, cbSize, &pszDigest, 0, 0); 746 if (RT_FAILURE(vrc)) 747 throw setError(VBOX_E_FILE_ERROR, 748 tr("Couldn't calculate SHA1 digest for file '%s' (%Rrc)"), 749 RTPathFilename(locInfo.strPath.c_str()), vrc); 750 m->strOVFSHA1Digest = pszDigest; 751 RTStrFree(pszDigest); 752 753 } 754 catch (iprt::Error &x) // includes all XML exceptions 755 { 756 rc = setError(VBOX_E_FILE_ERROR, 757 x.what()); 758 } 759 catch (HRESULT aRC) 760 { 761 rc = aRC; 762 } 763 764 /* Cleanup the OVF memory buffer */ 765 if (pvBuf) 766 RTMemFree(pvBuf); 842 /* Cleanup */ 843 if (pvTmpBuf) 844 RTMemFree(pvTmpBuf); 767 845 768 846 LogFlowFunc(("rc=%Rhrc\n", rc)); … … 899 977 return rc; 900 978 } 979 980 /******************************************************************************* 981 * Import stuff 982 ******************************************************************************/ 983 984 /** 985 * Implementation for importing OVF data into VirtualBox. This starts a new thread which will call 986 * Appliance::taskThreadImportOrExport(). 987 * 988 * This creates one or more new machines according to the VirtualSystemScription instances created by 989 * Appliance::Interpret(). 990 * 991 * This is in a separate private method because it is used from two locations: 992 * 993 * 1) from the public Appliance::ImportMachines(). 994 * 2) from Appliance::importS3(), which got called from a previous instance of Appliance::taskThreadImportOrExport(). 995 * 996 * @param aLocInfo 997 * @param aProgress 998 * @return 999 */ 1000 HRESULT Appliance::importImpl(const LocationInfo &locInfo, 1001 ComObjPtr<Progress> &progress) 1002 { 1003 HRESULT rc = S_OK; 1004 1005 SetUpProgressMode mode; 1006 if (locInfo.storageType == VFSType_File) 1007 mode = ImportFile; 1008 else 1009 mode = ImportS3; 1010 1011 rc = setUpProgress(progress, 1012 BstrFmt(tr("Importing appliance '%s'"), locInfo.strPath.c_str()), 1013 mode); 1014 if (FAILED(rc)) throw rc; 1015 1016 /* Initialize our worker task */ 1017 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Import, locInfo, progress)); 1018 1019 rc = task->startThread(); 1020 if (FAILED(rc)) throw rc; 1021 1022 /* Don't destruct on success */ 1023 task.release(); 1024 1025 return rc; 1026 } 1027 1028 /** 1029 * Actual worker code for importing OVF data into VirtualBox. This is called from Appliance::taskThreadImportOrExport() 1030 * and therefore runs on the OVF import worker thread. This creates one or more new machines according to the 1031 * VirtualSystemScription instances created by Appliance::Interpret(). 1032 * 1033 * This runs in three contexts: 1034 * 1035 * 1) in a first worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(); 1036 * 1037 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which 1038 * called Appliance::importFSOVA(), which called Appliance::importImpl(), which then called this again. 1039 * 1040 * 3) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which 1041 * called Appliance::importS3(), which called Appliance::importImpl(), which then called this again. 1042 * 1043 * @param pTask 1044 * @return 1045 */ 1046 HRESULT Appliance::importFS(TaskOVF *pTask) 1047 { 1048 1049 LogFlowFuncEnter(); 1050 LogFlowFunc(("Appliance %p\n", this)); 1051 1052 AutoCaller autoCaller(this); 1053 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1054 1055 /* Change the appliance state so we can safely leave the lock while doing 1056 * time-consuming disk imports; also the below method calls do all kinds of 1057 * locking which conflicts with the appliance object lock. */ 1058 AutoWriteLock writeLock(this COMMA_LOCKVAL_SRC_POS); 1059 /* Check if the appliance is currently busy. */ 1060 if (!isApplianceIdle()) 1061 return E_ACCESSDENIED; 1062 /* Set the internal state to importing. */ 1063 m->state = Data::ApplianceImporting; 1064 1065 HRESULT rc = S_OK; 1066 1067 /* Clear the list of imported machines, if any */ 1068 m->llGuidsMachinesCreated.clear(); 1069 1070 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)) 1071 rc = importFSOVF(pTask, writeLock); 1072 else 1073 rc = importFSOVA(pTask, writeLock); 1074 1075 if (FAILED(rc)) 1076 { 1077 /* With _whatever_ error we've had, do a complete roll-back of 1078 * machines and disks we've created */ 1079 writeLock.release(); 1080 for (list<Guid>::iterator itID = m->llGuidsMachinesCreated.begin(); 1081 itID != m->llGuidsMachinesCreated.end(); 1082 ++itID) 1083 { 1084 Guid guid = *itID; 1085 Bstr bstrGuid = guid.toUtf16(); 1086 ComPtr<IMachine> failedMachine; 1087 HRESULT rc2 = mVirtualBox->GetMachine(bstrGuid.raw(), failedMachine.asOutParam()); 1088 if (SUCCEEDED(rc2)) 1089 { 1090 SafeIfaceArray<IMedium> aMedia; 1091 rc2 = failedMachine->Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(aMedia)); 1092 ComPtr<IProgress> pProgress2; 1093 rc2 = failedMachine->Delete(ComSafeArrayAsInParam(aMedia), pProgress2.asOutParam()); 1094 pProgress2->WaitForCompletion(-1); 1095 } 1096 } 1097 writeLock.acquire(); 1098 } 1099 1100 /* Reset the state so others can call methods again */ 1101 m->state = Data::ApplianceIdle; 1102 1103 LogFlowFunc(("rc=%Rhrc\n", rc)); 1104 LogFlowFuncLeave(); 1105 1106 return rc; 1107 } 1108 1109 HRESULT Appliance::importFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock) 1110 { 1111 LogFlowFuncEnter(); 1112 1113 HRESULT rc = S_OK; 1114 1115 PVDINTERFACEIO pSha1Callbacks = 0; 1116 PVDINTERFACEIO pRTFileCallbacks = 0; 1117 void *pvMfBuf = 0; 1118 writeLock.release(); 1119 try 1120 { 1121 /* Create the necessary file access interfaces. */ 1122 pSha1Callbacks = Sha1CreateInterface(); 1123 if (!pSha1Callbacks) 1124 throw E_OUTOFMEMORY; 1125 pRTFileCallbacks = RTFileCreateInterface(); 1126 if (!pRTFileCallbacks) 1127 throw E_OUTOFMEMORY; 1128 1129 VDINTERFACE VDInterfaceIO; 1130 SHA1STORAGE storage; 1131 RT_ZERO(storage); 1132 storage.fCreateDigest = true; 1133 int vrc = VDInterfaceAdd(&VDInterfaceIO, "Appliance::IORTFile", 1134 VDINTERFACETYPE_IO, pRTFileCallbacks, 1135 0, &storage.pVDImageIfaces); 1136 if (RT_FAILURE(vrc)) 1137 throw E_FAIL; 1138 1139 size_t cbMfSize = 0; 1140 Utf8Str strMfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf"); 1141 /* Create the import stack for the rollback on errors. */ 1142 ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress); 1143 /* Do we need the digest information? */ 1144 storage.fCreateDigest = RTFileExists(strMfFile.c_str()); 1145 /* Now import the appliance. */ 1146 importMachines(stack, pSha1Callbacks, &storage); 1147 /* Read & verify the manifest file, if there is one. */ 1148 if (storage.fCreateDigest) 1149 { 1150 /* Add the ovf file to the digest list. */ 1151 stack.llSrcDisksDigest.push_front(STRPAIR(pTask->locInfo.strPath, m->strOVFSHA1Digest)); 1152 rc = readManifestFile(strMfFile, &pvMfBuf, &cbMfSize, pSha1Callbacks, &storage); 1153 if (FAILED(rc)) throw rc; 1154 rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize); 1155 if (FAILED(rc)) throw rc; 1156 } 1157 } 1158 catch (HRESULT rc2) 1159 { 1160 rc = rc2; 1161 } 1162 writeLock.acquire(); 1163 1164 /* Cleanup */ 1165 if (pvMfBuf) 1166 RTMemFree(pvMfBuf); 1167 if (pSha1Callbacks) 1168 RTMemFree(pSha1Callbacks); 1169 if (pRTFileCallbacks) 1170 RTMemFree(pRTFileCallbacks); 1171 1172 LogFlowFunc(("rc=%Rhrc\n", rc)); 1173 LogFlowFuncLeave(); 1174 1175 return rc; 1176 } 1177 1178 HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) 1179 { 1180 LogFlowFuncEnter(); 1181 1182 RTTAR tar; 1183 int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true); 1184 if (RT_FAILURE(vrc)) 1185 return setError(VBOX_E_FILE_ERROR, 1186 tr("Could not open OVA file '%s' (%Rrc)"), 1187 pTask->locInfo.strPath.c_str(), vrc); 1188 1189 HRESULT rc = S_OK; 1190 1191 PVDINTERFACEIO pSha1Callbacks = 0; 1192 PVDINTERFACEIO pRTTarCallbacks = 0; 1193 void *pvMfBuf = 0; 1194 writeLock.release(); 1195 try 1196 { 1197 /* Create the necessary file access interfaces. */ 1198 pSha1Callbacks = Sha1CreateInterface(); 1199 if (!pSha1Callbacks) 1200 throw E_OUTOFMEMORY; 1201 pRTTarCallbacks = RTTarCreateInterface(); 1202 if (!pRTTarCallbacks) 1203 throw E_OUTOFMEMORY; 1204 1205 VDINTERFACE VDInterfaceIO; 1206 SHA1STORAGE storage; 1207 RT_ZERO(storage); 1208 vrc = VDInterfaceAdd(&VDInterfaceIO, "Appliance::IORTTar", 1209 VDINTERFACETYPE_IO, pRTTarCallbacks, 1210 tar, &storage.pVDImageIfaces); 1211 if (RT_FAILURE(vrc)) 1212 throw E_FAIL; 1213 1214 /* Skip the OVF file, cause this was read in IAppliance::Read already. */ 1215 vrc = RTTarSeekNextFile(tar); 1216 if (RT_FAILURE(vrc)) 1217 /* Better error .... no unusual error */ 1218 throw E_FAIL; 1219 1220 PVDINTERFACEIO pCallbacks = pSha1Callbacks; 1221 PSHA1STORAGE pStorage = &storage; 1222 1223 /* We always need to create the digest, cause we didn't know if there 1224 * is a manifest file in the stream. */ 1225 pStorage->fCreateDigest = true; 1226 1227 size_t cbMfSize = 0; 1228 Utf8Str strMfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf"); 1229 /* Create the import stack for the rollback on errors. */ 1230 ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress); 1231 /* 1232 * Try to read the manifest file. First try. 1233 * 1234 * Note: This isn't fatal if the file is not found. The standard 1235 * defines 3 cases. 1236 * 1. no manifest file 1237 * 2. manifest file after the OVF file 1238 * 3. manifest file after all disk files 1239 * If we want streaming capabilities, we can't check if it is there by 1240 * searching for it. We have to try to open it on all possible places. 1241 * If it fails here, we will try it again after all disks where read. 1242 */ 1243 rc = readTarManifestFile(tar, strMfFile, &pvMfBuf, &cbMfSize, pCallbacks, pStorage); 1244 if (FAILED(rc)) throw rc; 1245 /* Now import the appliance. */ 1246 importMachines(stack, pCallbacks, pStorage); 1247 /* Try to read the manifest file. Second try. */ 1248 if (!pvMfBuf) 1249 { 1250 rc = readTarManifestFile(tar, strMfFile, &pvMfBuf, &cbMfSize, pCallbacks, pStorage); 1251 if (FAILED(rc)) throw rc; 1252 } 1253 /* If we were able to read a manifest file we can check it now. */ 1254 if (pvMfBuf) 1255 { 1256 /* Add the ovf file to the digest list. */ 1257 stack.llSrcDisksDigest.push_front(STRPAIR(Utf8Str(pTask->locInfo.strPath).stripExt().append(".ovf"), m->strOVFSHA1Digest)); 1258 rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize); 1259 if (FAILED(rc)) throw rc; 1260 } 1261 } 1262 catch (HRESULT rc2) 1263 { 1264 rc = rc2; 1265 } 1266 writeLock.acquire(); 1267 1268 RTTarClose(tar); 1269 1270 /* Cleanup */ 1271 if (pvMfBuf) 1272 RTMemFree(pvMfBuf); 1273 if (pSha1Callbacks) 1274 RTMemFree(pSha1Callbacks); 1275 if (pRTTarCallbacks) 1276 RTMemFree(pRTTarCallbacks); 1277 1278 LogFlowFunc(("rc=%Rhrc\n", rc)); 1279 LogFlowFuncLeave(); 1280 1281 return rc; 1282 } 1283 1284 /** 1285 * Worker code for importing OVF from the cloud. This is called from Appliance::taskThreadImportOrExport() 1286 * in S3 mode and therefore runs on the OVF import worker thread. This then starts a second worker 1287 * thread to import from temporary files (see Appliance::importFS()). 1288 * @param pTask 1289 * @return 1290 */ 1291 HRESULT Appliance::importS3(TaskOVF *pTask) 1292 { 1293 LogFlowFuncEnter(); 1294 LogFlowFunc(("Appliance %p\n", this)); 1295 1296 AutoCaller autoCaller(this); 1297 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1298 1299 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS); 1300 1301 int vrc = VINF_SUCCESS; 1302 RTS3 hS3 = NIL_RTS3; 1303 char szOSTmpDir[RTPATH_MAX]; 1304 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir)); 1305 /* The template for the temporary directory created below */ 1306 char *pszTmpDir; 1307 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir); 1308 list< pair<Utf8Str, ULONG> > filesList; 1309 1310 HRESULT rc = S_OK; 1311 try 1312 { 1313 /* Extract the bucket */ 1314 Utf8Str tmpPath = pTask->locInfo.strPath; 1315 Utf8Str bucket; 1316 parseBucket(tmpPath, bucket); 1317 1318 /* We need a temporary directory which we can put the all disk images 1319 * in */ 1320 vrc = RTDirCreateTemp(pszTmpDir); 1321 if (RT_FAILURE(vrc)) 1322 throw setError(VBOX_E_FILE_ERROR, 1323 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 1324 1325 /* Add every disks of every virtual system to an internal list */ 1326 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; 1327 for (it = m->virtualSystemDescriptions.begin(); 1328 it != m->virtualSystemDescriptions.end(); 1329 ++it) 1330 { 1331 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it); 1332 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 1333 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH; 1334 for (itH = avsdeHDs.begin(); 1335 itH != avsdeHDs.end(); 1336 ++itH) 1337 { 1338 const Utf8Str &strTargetFile = (*itH)->strOvf; 1339 if (!strTargetFile.isEmpty()) 1340 { 1341 /* The temporary name of the target disk file */ 1342 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str())); 1343 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB)); 1344 } 1345 } 1346 } 1347 1348 /* Next we have to download the disk images */ 1349 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING); 1350 if (RT_FAILURE(vrc)) 1351 throw setError(VBOX_E_IPRT_ERROR, 1352 tr("Cannot create S3 service handler")); 1353 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask); 1354 1355 /* Download all files */ 1356 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1) 1357 { 1358 const pair<Utf8Str, ULONG> &s = (*it1); 1359 const Utf8Str &strSrcFile = s.first; 1360 /* Construct the source file name */ 1361 char *pszFilename = RTPathFilename(strSrcFile.c_str()); 1362 /* Advance to the next operation */ 1363 if (!pTask->pProgress.isNull()) 1364 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename).raw(), s.second); 1365 1366 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str()); 1367 if (RT_FAILURE(vrc)) 1368 { 1369 if (vrc == VERR_S3_CANCELED) 1370 throw S_OK; /* todo: !!!!!!!!!!!!! */ 1371 else if (vrc == VERR_S3_ACCESS_DENIED) 1372 throw setError(E_ACCESSDENIED, 1373 tr("Cannot download file '%s' from S3 storage server (Access denied). " 1374 "Make sure that your credentials are right. Also check that your host clock is properly synced"), 1375 pszFilename); 1376 else if (vrc == VERR_S3_NOT_FOUND) 1377 throw setError(VBOX_E_FILE_ERROR, 1378 tr("Cannot download file '%s' from S3 storage server (File not found)"), 1379 pszFilename); 1380 else 1381 throw setError(VBOX_E_IPRT_ERROR, 1382 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), 1383 pszFilename, vrc); 1384 } 1385 } 1386 1387 /* Provide a OVF file (haven't to exist) so the import routine can 1388 * figure out where the disk images/manifest file are located. */ 1389 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str())); 1390 /* Now check if there is an manifest file. This is optional. */ 1391 Utf8Str strManifestFile; //= queryManifestFileName(strTmpOvf); 1392 // Utf8Str strManifestFile = queryManifestFileName(strTmpOvf); 1393 char *pszFilename = RTPathFilename(strManifestFile.c_str()); 1394 if (!pTask->pProgress.isNull()) 1395 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename).raw(), 1); 1396 1397 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */ 1398 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str()); 1399 if (RT_SUCCESS(vrc)) 1400 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0)); 1401 else if (RT_FAILURE(vrc)) 1402 { 1403 if (vrc == VERR_S3_CANCELED) 1404 throw S_OK; /* todo: !!!!!!!!!!!!! */ 1405 else if (vrc == VERR_S3_NOT_FOUND) 1406 vrc = VINF_SUCCESS; /* Not found is ok */ 1407 else if (vrc == VERR_S3_ACCESS_DENIED) 1408 throw setError(E_ACCESSDENIED, 1409 tr("Cannot download file '%s' from S3 storage server (Access denied)." 1410 "Make sure that your credentials are right. Also check that your host clock is properly synced"), 1411 pszFilename); 1412 else 1413 throw setError(VBOX_E_IPRT_ERROR, 1414 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), 1415 pszFilename, vrc); 1416 } 1417 1418 /* Close the connection early */ 1419 RTS3Destroy(hS3); 1420 hS3 = NIL_RTS3; 1421 1422 pTask->pProgress->SetNextOperation(BstrFmt(tr("Importing appliance")).raw(), m->ulWeightForXmlOperation); 1423 1424 ComObjPtr<Progress> progress; 1425 /* Import the whole temporary OVF & the disk images */ 1426 LocationInfo li; 1427 li.strPath = strTmpOvf; 1428 rc = importImpl(li, progress); 1429 if (FAILED(rc)) throw rc; 1430 1431 /* Unlock the appliance for the fs import thread */ 1432 appLock.release(); 1433 /* Wait until the import is done, but report the progress back to the 1434 caller */ 1435 ComPtr<IProgress> progressInt(progress); 1436 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */ 1437 1438 /* Again lock the appliance for the next steps */ 1439 appLock.acquire(); 1440 } 1441 catch(HRESULT aRC) 1442 { 1443 rc = aRC; 1444 } 1445 /* Cleanup */ 1446 RTS3Destroy(hS3); 1447 /* Delete all files which where temporary created */ 1448 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1) 1449 { 1450 const char *pszFilePath = (*it1).first.c_str(); 1451 if (RTPathExists(pszFilePath)) 1452 { 1453 vrc = RTFileDelete(pszFilePath); 1454 if (RT_FAILURE(vrc)) 1455 rc = setError(VBOX_E_FILE_ERROR, 1456 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc); 1457 } 1458 } 1459 /* Delete the temporary directory */ 1460 if (RTPathExists(pszTmpDir)) 1461 { 1462 vrc = RTDirRemove(pszTmpDir); 1463 if (RT_FAILURE(vrc)) 1464 rc = setError(VBOX_E_FILE_ERROR, 1465 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 1466 } 1467 if (pszTmpDir) 1468 RTStrFree(pszTmpDir); 1469 1470 LogFlowFunc(("rc=%Rhrc\n", rc)); 1471 LogFlowFuncLeave(); 1472 1473 return rc; 1474 } 1475 1476 HRESULT Appliance::readManifestFile(const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PSHA1STORAGE pStorage) 1477 { 1478 HRESULT rc = S_OK; 1479 1480 bool fOldDigest = pStorage->fCreateDigest; 1481 pStorage->fCreateDigest = false; /* No digest for the manifest file */ 1482 int vrc = Sha1ReadBuf(strFile.c_str(), ppvBuf, pcbSize, pCallbacks, pStorage); 1483 if ( RT_FAILURE(vrc) 1484 && vrc != VERR_FILE_NOT_FOUND) 1485 rc = setError(VBOX_E_FILE_ERROR, 1486 tr("Could not read manifest file '%s' (%Rrc)"), 1487 RTPathFilename(strFile.c_str()), vrc); 1488 pStorage->fCreateDigest = fOldDigest; /* Restore the old digest creation behavior again. */ 1489 1490 return rc; 1491 } 1492 1493 HRESULT Appliance::readTarManifestFile(RTTAR tar, const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PSHA1STORAGE pStorage) 1494 { 1495 HRESULT rc = S_OK; 1496 1497 char *pszCurFile; 1498 int vrc = RTTarCurrentFile(tar, &pszCurFile); 1499 if (RT_SUCCESS(vrc)) 1500 { 1501 if (!strcmp(pszCurFile, RTPathFilename(strFile.c_str()))) 1502 rc = readManifestFile(strFile, ppvBuf, pcbSize, pCallbacks, pStorage); 1503 RTStrFree(pszCurFile); 1504 } 1505 else if (vrc != VERR_TAR_END_OF_FILE) 1506 rc = E_FAIL; 1507 1508 return rc; 1509 } 1510 1511 HRESULT Appliance::verifyManifestFile(const Utf8Str &strFile, ImportStack &stack, void *pvBuf, size_t cbSize) 1512 { 1513 HRESULT rc = S_OK; 1514 1515 PRTMANIFESTTEST paTests = (PRTMANIFESTTEST)RTMemAlloc(sizeof(RTMANIFESTTEST) * stack.llSrcDisksDigest.size()); 1516 if (!paTests) 1517 return E_OUTOFMEMORY; 1518 1519 size_t i = 0; 1520 list<STRPAIR>::const_iterator it1; 1521 for (it1 = stack.llSrcDisksDigest.begin(); 1522 it1 != stack.llSrcDisksDigest.end(); 1523 ++it1, ++i) 1524 { 1525 paTests[i].pszTestFile = (*it1).first.c_str(); 1526 paTests[i].pszTestDigest = (*it1).second.c_str(); 1527 } 1528 size_t iFailed; 1529 int vrc = RTManifestVerifyFilesBuf(pvBuf, cbSize, paTests, stack.llSrcDisksDigest.size(), &iFailed); 1530 if (RT_UNLIKELY(vrc == VERR_MANIFEST_DIGEST_MISMATCH)) 1531 rc = setError(VBOX_E_FILE_ERROR, 1532 tr("The SHA1 digest of '%s' does not match the one in '%s' (%Rrc)"), 1533 RTPathFilename(paTests[iFailed].pszTestFile), RTPathFilename(strFile.c_str()), vrc); 1534 else if (RT_FAILURE(vrc)) 1535 rc = setError(VBOX_E_FILE_ERROR, 1536 tr("Could not verify the content of '%s' against the available files (%Rrc)"), 1537 RTPathFilename(strFile.c_str()), vrc); 1538 1539 RTMemFree(paTests); 1540 1541 return rc; 1542 } 1543 901 1544 902 1545 /** … … 996 1639 997 1640 /** 998 * Implementation for importing OVF data into VirtualBox. This starts a new thread which will call999 * Appliance::taskThreadImportOrExport().1000 *1001 * This creates one or more new machines according to the VirtualSystemScription instances created by1002 * Appliance::Interpret().1003 *1004 * This is in a separate private method because it is used from two locations:1005 *1006 * 1) from the public Appliance::ImportMachines().1007 * 2) from Appliance::importS3(), which got called from a previous instance of Appliance::taskThreadImportOrExport().1008 *1009 * @param aLocInfo1010 * @param aProgress1011 * @return1012 */1013 HRESULT Appliance::importImpl(const LocationInfo &locInfo,1014 ComObjPtr<Progress> &progress)1015 {1016 HRESULT rc = S_OK;1017 1018 SetUpProgressMode mode;1019 if (locInfo.storageType == VFSType_File)1020 {1021 mode = ImportFileNoManifest;1022 Utf8Str strMfFile = queryManifestFileName(locInfo.strPath);1023 if (!strMfFile.isEmpty())1024 mode = ImportFileWithManifest;1025 }1026 else1027 mode = ImportS3;1028 1029 rc = setUpProgress(locInfo,1030 progress,1031 BstrFmt(tr("Importing appliance '%s'"), locInfo.strPath.c_str()),1032 mode);1033 if (FAILED(rc)) throw rc;1034 1035 /* Initialize our worker task */1036 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Import, locInfo, progress));1037 1038 rc = task->startThread();1039 if (FAILED(rc)) throw rc;1040 1041 /* Don't destruct on success */1042 task.release();1043 1044 return rc;1045 }1046 1047 Utf8Str Appliance::queryManifestFileName(const Utf8Str& aPath) const1048 {1049 Utf8Str strMfFile = manifestFileName(aPath);1050 if (!aPath.endsWith(".ova", Utf8Str::CaseInsensitive))1051 {1052 if (RTPathExists(strMfFile.c_str()))1053 return strMfFile;1054 1055 }1056 else1057 {1058 if (RTTarFileExists(aPath.c_str(), RTPathFilename(strMfFile.c_str())) == VINF_SUCCESS)1059 return strMfFile;1060 }1061 return Utf8Str();1062 }1063 1064 /**1065 * Checks if a manifest file exists in the given location and, if so, verifies1066 * that the relevant files (the OVF XML and the disks referenced by it, as1067 * represented by the VirtualSystemDescription instances contained in this appliance)1068 * match it. Requires a previous read() and interpret().1069 *1070 * @param locInfo1071 * @param reader1072 * @return1073 */1074 HRESULT Appliance::manifestVerify(const LocationInfo &locInfo,1075 const ovf::OVFReader &reader,1076 ComObjPtr<Progress> &pProgress)1077 {1078 HRESULT rc = S_OK;1079 1080 Utf8Str strManifestFile = queryManifestFileName(locInfo.strPath);1081 if (!strManifestFile.isEmpty())1082 {1083 const char *pcszManifestFileOnly = RTPathFilename(strManifestFile.c_str());1084 pProgress->SetNextOperation(BstrFmt(tr("Verifying manifest file '%s'"), pcszManifestFileOnly).raw(),1085 m->ulWeightForManifestOperation); // operation's weight, as set up with the IProgress originally1086 1087 list<Utf8Str> filesList;1088 Utf8Str strSrcDir(locInfo.strPath);1089 strSrcDir.stripFilename();1090 // add every disks of every virtual system to an internal list1091 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;1092 for (it = m->virtualSystemDescriptions.begin();1093 it != m->virtualSystemDescriptions.end();1094 ++it)1095 {1096 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);1097 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);1098 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;1099 for (itH = avsdeHDs.begin();1100 itH != avsdeHDs.end();1101 ++itH)1102 {1103 VirtualSystemDescriptionEntry *vsdeHD = *itH;1104 // find the disk from the OVF's disk list1105 ovf::DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);1106 const ovf::DiskImage &di = itDiskImage->second;1107 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());1108 filesList.push_back(strSrcFilePath);1109 }1110 }1111 1112 // create the test list1113 PRTMANIFESTTEST pTestList = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST) * (filesList.size() + 1));1114 pTestList[0].pszTestFile = (char*)locInfo.strPath.c_str();1115 pTestList[0].pszTestDigest = (char*)m->strOVFSHA1Digest.c_str();1116 int vrc = VINF_SUCCESS;1117 size_t i = 1;1118 list<Utf8Str>::const_iterator it1;1119 for (it1 = filesList.begin();1120 it1 != filesList.end();1121 ++it1, ++i)1122 {1123 char* pszDigest;1124 vrc = RTSha1DigestFromFile((*it1).c_str(), &pszDigest, NULL, NULL);1125 pTestList[i].pszTestFile = (char*)(*it1).c_str();1126 pTestList[i].pszTestDigest = pszDigest;1127 }1128 1129 // this call can take a very long time1130 size_t cIndexOnError;1131 vrc = RTManifestVerify(strManifestFile.c_str(),1132 pTestList,1133 filesList.size() + 1,1134 &cIndexOnError);1135 1136 if (vrc == VERR_MANIFEST_DIGEST_MISMATCH)1137 rc = setError(VBOX_E_FILE_ERROR,1138 tr("The SHA1 digest of '%s' does not match the one in '%s'"),1139 RTPathFilename(pTestList[cIndexOnError].pszTestFile),1140 pcszManifestFileOnly);1141 else if (RT_FAILURE(vrc))1142 rc = setError(VBOX_E_FILE_ERROR,1143 tr("Could not verify the content of '%s' against the available files (%Rrc)"),1144 pcszManifestFileOnly,1145 vrc);1146 1147 // clean up1148 for (size_t j = 1;1149 j < filesList.size();1150 ++j)1151 RTStrFree(pTestList[j].pszTestDigest);1152 RTMemFree(pTestList);1153 }1154 1155 return rc;1156 }1157 1158 /**1159 * Actual worker code for importing OVF data into VirtualBox. This is called from Appliance::taskThreadImportOrExport()1160 * and therefore runs on the OVF import worker thread. This creates one or more new machines according to the1161 * VirtualSystemScription instances created by Appliance::Interpret().1162 *1163 * This runs in three contexts:1164 *1165 * 1) in a first worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl();1166 *1167 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which1168 * called Appliance::importFSOVA(), which called Appliance::importImpl(), which then called this again.1169 *1170 * 3) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which1171 * called Appliance::importS3(), which called Appliance::importImpl(), which then called this again.1172 *1173 * @param pTask1174 * @return1175 */1176 HRESULT Appliance::importFS(TaskOVF *pTask)1177 {1178 if (!Utf8Str(RTPathExt(pTask->locInfo.strPath.c_str())).compare(".ovf", Utf8Str::CaseInsensitive))1179 return importFSOVF(pTask);1180 else1181 return importFSOVA(pTask);1182 }1183 1184 HRESULT Appliance::importFSOVF(TaskOVF *pTask)1185 {1186 LogFlowFuncEnter();1187 LogFlowFunc(("Appliance %p\n", this));1188 1189 AutoCaller autoCaller(this);1190 if (FAILED(autoCaller.rc())) return autoCaller.rc();1191 1192 Assert(!pTask->pProgress.isNull());1193 1194 // Change the appliance state so we can safely leave the lock while doing time-consuming1195 // disk imports; also the below method calls do all kinds of locking which conflicts with1196 // the appliance object lock1197 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);1198 if (!isApplianceIdle())1199 return E_ACCESSDENIED;1200 m->state = Data::ApplianceImporting;1201 appLock.release();1202 1203 HRESULT rc = S_OK;1204 1205 const ovf::OVFReader &reader = *m->pReader;1206 // this is safe to access because this thread only gets started1207 // if pReader != NULL1208 1209 // rollback for errors:1210 ImportStack stack(pTask->locInfo, reader.m_mapDisks, pTask->pProgress);1211 1212 // clear the list of imported machines, if any1213 m->llGuidsMachinesCreated.clear();1214 1215 try1216 {1217 // if a manifest file exists, verify the content; we then need all files which are referenced by the OVF & the OVF itself1218 rc = manifestVerify(pTask->locInfo, reader, pTask->pProgress);1219 if (FAILED(rc)) throw rc;1220 1221 // create a session for the machine + disks we manipulate below1222 rc = stack.pSession.createInprocObject(CLSID_Session);1223 if (FAILED(rc)) throw rc;1224 1225 list<ovf::VirtualSystem>::const_iterator it;1226 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;1227 /* Iterate through all virtual systems of that appliance */1228 size_t i = 0;1229 for (it = reader.m_llVirtualSystems.begin(),1230 it1 = m->virtualSystemDescriptions.begin();1231 it != reader.m_llVirtualSystems.end();1232 ++it, ++it1, ++i)1233 {1234 const ovf::VirtualSystem &vsysThis = *it;1235 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);1236 1237 ComPtr<IMachine> pNewMachine;1238 1239 // there are two ways in which we can create a vbox machine from OVF:1240 // -- either this OVF was written by vbox 3.2 or later, in which case there is a <vbox:Machine> element1241 // in the <VirtualSystem>; then the VirtualSystemDescription::Data has a settings::MachineConfigFile1242 // with all the machine config pretty-parsed;1243 // -- or this is an OVF from an older vbox or an external source, and then we need to translate the1244 // VirtualSystemDescriptionEntry and do import work1245 1246 // Even for the vbox:Machine case, there are a number of configuration items that will be taken from1247 // the OVF because otherwise the "override import parameters" mechanism in the GUI won't work.1248 1249 // VM name1250 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);1251 if (vsdeName.size() < 1)1252 throw setError(VBOX_E_FILE_ERROR,1253 tr("Missing VM name"));1254 stack.strNameVBox = vsdeName.front()->strVboxCurrent;1255 1256 // have VirtualBox suggest where the filename would be placed so we can1257 // put the disk images in the same directory1258 Bstr bstrMachineFilename;1259 rc = mVirtualBox->ComposeMachineFilename(Bstr(stack.strNameVBox).raw(),1260 NULL,1261 bstrMachineFilename.asOutParam());1262 if (FAILED(rc)) throw rc;1263 // and determine the machine folder from that1264 stack.strMachineFolder = bstrMachineFilename;1265 stack.strMachineFolder.stripFilename();1266 1267 // guest OS type1268 std::list<VirtualSystemDescriptionEntry*> vsdeOS;1269 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);1270 if (vsdeOS.size() < 1)1271 throw setError(VBOX_E_FILE_ERROR,1272 tr("Missing guest OS type"));1273 stack.strOsTypeVBox = vsdeOS.front()->strVboxCurrent;1274 1275 // CPU count1276 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU);1277 if (vsdeCPU.size() != 1)1278 throw setError(VBOX_E_FILE_ERROR, tr("CPU count missing"));1279 1280 const Utf8Str &cpuVBox = vsdeCPU.front()->strVboxCurrent;1281 stack.cCPUs = (uint32_t)RTStrToUInt64(cpuVBox.c_str());1282 // We need HWVirt & IO-APIC if more than one CPU is requested1283 if (stack.cCPUs > 1)1284 {1285 stack.fForceHWVirt = true;1286 stack.fForceIOAPIC = true;1287 }1288 1289 // RAM1290 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);1291 if (vsdeRAM.size() != 1)1292 throw setError(VBOX_E_FILE_ERROR, tr("RAM size missing"));1293 const Utf8Str &memoryVBox = vsdeRAM.front()->strVboxCurrent;1294 stack.ulMemorySizeMB = (uint32_t)RTStrToUInt64(memoryVBox.c_str());1295 1296 #ifdef VBOX_WITH_USB1297 // USB controller1298 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);1299 // USB support is enabled if there's at least one such entry; to disable USB support,1300 // the type of the USB item would have been changed to "ignore"1301 stack.fUSBEnabled = vsdeUSBController.size() > 0;1302 #endif1303 // audio adapter1304 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);1305 /* @todo: we support one audio adapter only */1306 if (vsdeAudioAdapter.size() > 0)1307 stack.strAudioAdapter = vsdeAudioAdapter.front()->strVboxCurrent;1308 1309 // for the description of the new machine, always use the OVF entry, the user may have changed it in the import config1310 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);1311 if (vsdeDescription.size())1312 stack.strDescription = vsdeDescription.front()->strVboxCurrent;1313 1314 // import vbox:machine or OVF now1315 if (vsdescThis->m->pConfig)1316 // vbox:Machine config1317 importVBoxMachine(vsdescThis, pNewMachine, stack);1318 else1319 // generic OVF config1320 importMachineGeneric(vsysThis, vsdescThis, pNewMachine, stack);1321 1322 } // for (it = pAppliance->m->llVirtualSystems.begin() ...1323 }1324 catch (HRESULT rc2)1325 {1326 rc = rc2;1327 }1328 1329 if (FAILED(rc))1330 {1331 // with _whatever_ error we've had, do a complete roll-back of1332 // machines and disks we've created1333 1334 for (list<Guid>::iterator itID = m->llGuidsMachinesCreated.begin();1335 itID != m->llGuidsMachinesCreated.end();1336 ++itID)1337 {1338 Guid guid = *itID;1339 Bstr bstrGuid = guid.toUtf16();1340 ComPtr<IMachine> failedMachine;1341 HRESULT rc2 = mVirtualBox->GetMachine(bstrGuid.raw(), failedMachine.asOutParam());1342 if (SUCCEEDED(rc2))1343 {1344 SafeIfaceArray<IMedium> aMedia;1345 rc2 = failedMachine->Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(aMedia));1346 ComPtr<IProgress> pProgress2;1347 rc2 = failedMachine->Delete(ComSafeArrayAsInParam(aMedia), pProgress2.asOutParam());1348 pProgress2->WaitForCompletion(-1);1349 }1350 }1351 }1352 1353 // restore the appliance state1354 appLock.acquire();1355 m->state = Data::ApplianceIdle;1356 appLock.release();1357 1358 LogFlowFunc(("rc=%Rhrc\n", rc));1359 LogFlowFuncLeave();1360 1361 return rc;1362 }1363 1364 HRESULT Appliance::importFSOVA(TaskOVF *pTask)1365 {1366 LogFlowFuncEnter();1367 LogFlowFunc(("Appliance %p\n", this));1368 1369 AutoCaller autoCaller(this);1370 if (FAILED(autoCaller.rc())) return autoCaller.rc();1371 1372 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);1373 1374 int vrc = VINF_SUCCESS;1375 char szOSTmpDir[RTPATH_MAX];1376 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));1377 /* The template for the temporary directory created below */1378 char *pszTmpDir;1379 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);1380 list< pair<Utf8Str, ULONG> > filesList;1381 const char** paFiles = 0;1382 1383 HRESULT rc = S_OK;1384 try1385 {1386 /* Extract the path */1387 Utf8Str tmpPath = pTask->locInfo.strPath;1388 /* Remove the ova extension */1389 tmpPath.stripExt();1390 tmpPath += ".ovf";1391 1392 /* We need a temporary directory which we can put the all disk images1393 * in */1394 vrc = RTDirCreateTemp(pszTmpDir);1395 if (RT_FAILURE(vrc))1396 throw setError(VBOX_E_FILE_ERROR,1397 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);1398 1399 /* Provide a OVF file (haven't to exist) so the import routine can1400 * figure out where the disk images/manifest file are located. */1401 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));1402 /* Add the manifest file to the list of files to extract, but only if1403 one is in the archive. */1404 Utf8Str strManifestFile = queryManifestFileName(strTmpOvf);1405 if (!strManifestFile.isEmpty())1406 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile.c_str(), 1));1407 1408 ULONG ulWeight = m->ulWeightForXmlOperation;1409 /* Add every disks of every virtual system to an internal list */1410 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;1411 for (it = m->virtualSystemDescriptions.begin();1412 it != m->virtualSystemDescriptions.end();1413 ++it)1414 {1415 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);1416 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);1417 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;1418 for (itH = avsdeHDs.begin();1419 itH != avsdeHDs.end();1420 ++itH)1421 {1422 const Utf8Str &strTargetFile = (*itH)->strOvf;1423 if (!strTargetFile.isEmpty())1424 {1425 /* The temporary name of the target disk file */1426 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str()));1427 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB));1428 ulWeight += (*itH)->ulSizeMB;1429 }1430 }1431 }1432 1433 /* Download all files */1434 paFiles = (const char**)RTMemAlloc(sizeof(char*) * filesList.size());1435 int i = 0;1436 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1, ++i)1437 paFiles[i] = RTPathFilename((*it1).first.c_str());1438 if (!pTask->pProgress.isNull())1439 pTask->pProgress->SetNextOperation(BstrFmt(tr("Unpacking file '%s'"), RTPathFilename(pTask->locInfo.strPath.c_str())).raw(), ulWeight);1440 vrc = RTTarExtractFiles(pTask->locInfo.strPath.c_str(), pszTmpDir, paFiles, filesList.size(), pTask->updateProgress, &pTask);1441 if (RT_FAILURE(vrc))1442 throw setError(VBOX_E_FILE_ERROR,1443 tr("Cannot unpack archive file '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);1444 1445 // if (!pTask->pProgress.isNull())1446 // pTask->pProgress->SetNextOperation(BstrFmt(tr("Importing appliance")), m->ulWeightForXmlOperation);1447 1448 ComObjPtr<Progress> progress;1449 /* Import the whole temporary OVF & the disk images */1450 LocationInfo li;1451 li.strPath = strTmpOvf;1452 rc = importImpl(li, progress);1453 if (FAILED(rc)) throw rc;1454 1455 /* Unlock the appliance for the fs import thread */1456 appLock.release();1457 /* Wait until the import is done, but report the progress back to the1458 caller */1459 ComPtr<IProgress> progressInt(progress);1460 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */1461 1462 /* Again lock the appliance for the next steps */1463 appLock.acquire();1464 }1465 catch(HRESULT aRC)1466 {1467 rc = aRC;1468 }1469 /* Delete the temporary files list */1470 if (paFiles)1471 RTMemFree(paFiles);1472 /* Delete all files which where temporary created */1473 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)1474 {1475 const char *pszFilePath = (*it1).first.c_str();1476 if (RTPathExists(pszFilePath))1477 {1478 vrc = RTFileDelete(pszFilePath);1479 if (RT_FAILURE(vrc))1480 rc = setError(VBOX_E_FILE_ERROR,1481 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);1482 }1483 }1484 /* Delete the temporary directory */1485 if (RTPathExists(pszTmpDir))1486 {1487 vrc = RTDirRemove(pszTmpDir);1488 if (RT_FAILURE(vrc))1489 rc = setError(VBOX_E_FILE_ERROR,1490 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);1491 }1492 if (pszTmpDir)1493 RTStrFree(pszTmpDir);1494 1495 LogFlowFunc(("rc=%Rhrc\n", rc));1496 LogFlowFuncLeave();1497 1498 return rc;1499 }1500 1501 /**1502 1641 * Imports one disk image. This is common code shared between 1503 1642 * -- importMachineGeneric() for the OVF case; in that case the information comes from … … 1523 1662 void Appliance::importOneDiskImage(const ovf::DiskImage &di, 1524 1663 const Utf8Str &strTargetPath, 1525 ComPtr<IMedium> &pTargetHD, 1526 ImportStack &stack) 1664 ComObjPtr<Medium> &pTargetHD, 1665 ImportStack &stack, 1666 PVDINTERFACEIO pCallbacks, 1667 PSHA1STORAGE pStorage) 1527 1668 { 1528 ComPtr<IMedium> pSourceHD; 1529 bool fSourceHdNeedsClosing = false; 1530 1531 try 1532 { 1533 // destination file must not exist 1534 if ( strTargetPath.isEmpty() 1535 || RTPathExists(strTargetPath.c_str()) 1669 // destination file must not exist 1670 if ( strTargetPath.isEmpty() 1671 || RTPathExists(strTargetPath.c_str()) 1672 ) 1673 throw setError(VBOX_E_FILE_ERROR, 1674 tr("Destination file '%s' exists"), 1675 strTargetPath.c_str()); 1676 1677 const Utf8Str &strSourceOVF = di.strHref; 1678 1679 // construct source file path 1680 Utf8StrFmt strSrcFilePath("%s%c%s", stack.strSourceDir.c_str(), RTPATH_DELIMITER, strSourceOVF.c_str()); 1681 1682 // subprogress object for hard disk 1683 ComPtr<IProgress> pProgress2; 1684 1685 /**************** Check */ 1686 ComObjPtr<Progress> pProgress3; 1687 pProgress3.createObject(); 1688 HRESULT rc = pProgress3->init(mVirtualBox, static_cast<IAppliance*>(this), BstrFmt(tr("Creating medium")).raw(), TRUE); 1689 // rc = pProgress3->init(mVirtualBox, static_cast<IAppliance*>(this), BstrFmt(tr("Creating medium '%s'"), strTargetFilePath.c_str()).raw(), TRUE); 1690 if (FAILED(rc)) throw rc; 1691 /**************** Check */ 1692 1693 pTargetHD.createObject(); 1694 bool fNeedsGlobalSaveSettings; 1695 // create an empty hard disk 1696 rc = pTargetHD->init(mVirtualBox, 1697 Utf8Str("VMDK"), 1698 strTargetPath, 1699 Guid::Empty, // media registry 1700 &fNeedsGlobalSaveSettings); 1701 if (FAILED(rc)) throw rc; 1702 1703 /* If strHref is empty we have to create a new file */ 1704 if (strSourceOVF.isEmpty()) 1705 { 1706 // which format to use? 1707 Bstr srcFormat = L"VDI"; 1708 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive) 1709 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized", Utf8Str::CaseInsensitive) 1710 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive) 1711 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive) 1536 1712 ) 1537 throw setError(VBOX_E_FILE_ERROR, 1538 tr("Destination file '%s' exists"), 1539 strTargetPath.c_str()); 1540 1541 const Utf8Str &strSourceOVF = di.strHref; 1542 1543 // Make sure target directory exists 1544 HRESULT rc = VirtualBox::ensureFilePathExists(strTargetPath.c_str()); 1713 srcFormat = L"VMDK"; 1714 rc = mVirtualBox->CreateHardDisk(srcFormat.raw(), 1715 Bstr(strTargetPath).raw(), 1716 ComPtr<IMedium>(pTargetHD).asOutParam()); 1545 1717 if (FAILED(rc)) throw rc; 1546 1718 1547 // subprogress object for hard disk1548 ComPtr<IProgress> pProgress2;1549 1550 /* If strHref is empty we have to create a new file */ 1551 if (strSourceOVF.isEmpty())1552 {1553 // which format to use?1554 Bstr srcFormat = L"VDI";1555 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)1556 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized", Utf8Str::CaseInsensitive)1557 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)1558 || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)1559 )1560 srcFormat = L"VMDK";1561 // create an empty hard disk1562 rc = mVirtualBox->CreateHardDisk(srcFormat.raw(), 1563 Bstr(strTargetPath).raw(),1564 pTargetHD.asOutParam());1565 if (FAILED(rc)) throw rc;1566 1567 // create a dynamic growing disk image with the given capacity1568 rc = pTargetHD->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam());1569 if (FAILED(rc)) throw rc;1570 1571 // advance to the next operation1572 stack.pProgress->SetNextOperation(BstrFmt(tr("Creating disk image '%s'"), strTargetPath.c_str()).raw(),1573 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally1719 // create a dynamic growing disk image with the given capacity 1720 rc = pTargetHD->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam()); 1721 if (FAILED(rc)) throw rc; 1722 1723 // advance to the next operation 1724 stack.pProgress->SetNextOperation(BstrFmt(tr("Creating disk image '%s'"), strTargetPath.c_str()).raw(), 1725 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally 1726 } 1727 else 1728 { 1729 /* Create a new hard disk interface for the destination disk image */ 1730 rc = mVirtualBox->CreateHardDisk(NULL, 1731 Bstr(strTargetPath).raw(), 1732 ComPtr<IMedium>(pTargetHD).asOutParam()); 1733 if (FAILED(rc)) throw rc; 1734 1735 // We need a proper format description 1736 ComObjPtr<MediumFormat> format; 1737 // Scope for the AutoReadLock 1738 { 1739 SystemProperties *pSysProps = mVirtualBox->getSystemProperties(); 1740 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS); 1741 // We are always exporting to VMDK stream optimized for now 1742 format = pSysProps->mediumFormat("VMDK"); 1743 if (format.isNull()) 1744 throw setError(VBOX_E_NOT_SUPPORTED, 1745 tr("Invalid medium storage format")); 1574 1746 } 1575 else 1576 { 1577 // construct source file path 1578 Utf8StrFmt strSrcFilePath("%s%c%s", stack.strSourceDir.c_str(), RTPATH_DELIMITER, strSourceOVF.c_str()); 1579 1580 // Do NOT check here whether the file exists. The clone operation 1581 // will figure that out, and filesystem-based tests are simply 1582 // wrong in the general case (think of iSCSI). 1583 1584 // Clone the disk image (this is necessary cause the id has 1585 // to be recreated for the case the same hard disk is 1586 // attached already from a previous import) 1587 1588 // First open the existing disk image 1589 rc = mVirtualBox->OpenMedium(Bstr(strSrcFilePath).raw(), 1590 DeviceType_HardDisk, 1591 AccessMode_ReadOnly, 1592 pSourceHD.asOutParam()); 1593 if (FAILED(rc)) throw rc; 1594 fSourceHdNeedsClosing = true; 1595 1596 /* We need the format description of the source disk image */ 1597 Bstr srcFormat; 1598 rc = pSourceHD->COMGETTER(Format)(srcFormat.asOutParam()); 1599 if (FAILED(rc)) throw rc; 1600 /* Create a new hard disk interface for the destination disk image */ 1601 rc = mVirtualBox->CreateHardDisk(srcFormat.raw(), 1602 Bstr(strTargetPath).raw(), 1603 pTargetHD.asOutParam()); 1604 if (FAILED(rc)) throw rc; 1605 /* Clone the source disk image */ 1606 rc = pSourceHD->CloneTo(pTargetHD, MediumVariant_Standard, NULL, pProgress2.asOutParam()); 1607 if (FAILED(rc)) throw rc; 1608 1609 /* Advance to the next operation */ 1610 stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), RTPathFilename(strSrcFilePath.c_str())).raw(), 1611 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally); 1612 } 1613 1614 // now wait for the background disk operation to complete; this throws HRESULTs on error 1615 waitForAsyncProgress(stack.pProgress, pProgress2); 1616 1617 if (fSourceHdNeedsClosing) 1618 { 1619 rc = pSourceHD->Close(); 1620 if (FAILED(rc)) throw rc; 1621 fSourceHdNeedsClosing = false; 1622 } 1623 1624 stack.llHardDisksCreated.push_back(pTargetHD); 1625 } 1626 catch (...) 1627 { 1628 if (fSourceHdNeedsClosing) 1629 pSourceHD->Close(); 1630 1631 throw; 1632 } 1747 1748 /* Clone the source disk image */ 1749 ComObjPtr<Medium> nullParent; 1750 rc = pTargetHD->importFile(strSrcFilePath.c_str(), 1751 format, 1752 MediumVariant_Standard, 1753 pCallbacks, pStorage, 1754 nullParent, 1755 pProgress3); 1756 1757 /* Advance to the next operation */ 1758 stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), RTPathFilename(strSrcFilePath.c_str())).raw(), 1759 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally); 1760 } 1761 1762 // now wait for the background disk operation to complete; this throws HRESULTs on error 1763 ComPtr<IProgress> pp(pProgress3); 1764 waitForAsyncProgress(stack.pProgress, pp); 1765 1766 stack.llSrcDisksDigest.push_back(STRPAIR(strSrcFilePath, pStorage->strDigest)); 1633 1767 } 1634 1768 … … 1649 1783 ComObjPtr<VirtualSystemDescription> &vsdescThis, 1650 1784 ComPtr<IMachine> &pNewMachine, 1651 ImportStack &stack) 1785 ImportStack &stack, 1786 PVDINTERFACEIO pCallbacks, 1787 PSHA1STORAGE pStorage) 1652 1788 { 1653 1789 HRESULT rc; … … 2112 2248 const ovf::VirtualDisk &ovfVdisk = itVirtualDisk->second; 2113 2249 2114 Com Ptr<IMedium> pTargetHD;2250 ComObjPtr<Medium> pTargetHD; 2115 2251 importOneDiskImage(ovfDiskImage, 2116 2252 vsdeHD->strVboxCurrent, 2117 2253 pTargetHD, 2118 stack); 2254 stack, 2255 pCallbacks, 2256 pStorage); 2119 2257 2120 2258 // now use the new uuid to attach the disk image to our new machine … … 2197 2335 void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThis, 2198 2336 ComPtr<IMachine> &pReturnNewMachine, 2199 ImportStack &stack) 2337 ImportStack &stack, 2338 PVDINTERFACEIO pCallbacks, 2339 PSHA1STORAGE pStorage) 2200 2340 { 2201 2341 Assert(vsdescThis->m->pConfig); … … 2304 2444 * 2305 2445 */ 2306 Com Ptr<IMedium> pTargetHD;2446 ComObjPtr<Medium> pTargetHD; 2307 2447 importOneDiskImage(di, 2308 2448 strTargetPath, 2309 2449 pTargetHD, 2310 stack); 2450 stack, 2451 pCallbacks, 2452 pStorage); 2311 2453 2312 2454 // ... and replace the old UUID in the machine config with the one of … … 2365 2507 } 2366 2508 2367 /** 2368 * Worker code for importing OVF from the cloud. This is called from Appliance::taskThreadImportOrExport() 2369 * in S3 mode and therefore runs on the OVF import worker thread. This then starts a second worker 2370 * thread to import from temporary files (see Appliance::importFS()). 2371 * @param pTask 2372 * @return 2373 */ 2374 HRESULT Appliance::importS3(TaskOVF *pTask) 2509 void Appliance::importMachines(ImportStack &stack, 2510 PVDINTERFACEIO pCallbacks, 2511 PSHA1STORAGE pStorage) 2375 2512 { 2376 LogFlowFuncEnter();2377 LogFlowFunc(("Appliance %p\n", this));2378 2379 AutoCaller autoCaller(this);2380 if (FAILED(autoCaller.rc())) return autoCaller.rc();2381 2382 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);2383 2384 int vrc = VINF_SUCCESS;2385 RTS3 hS3 = NIL_RTS3;2386 char szOSTmpDir[RTPATH_MAX];2387 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));2388 /* The template for the temporary directory created below */2389 char *pszTmpDir;2390 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);2391 list< pair<Utf8Str, ULONG> > filesList;2392 2393 2513 HRESULT rc = S_OK; 2394 try 2395 { 2396 /* Extract the bucket */ 2397 Utf8Str tmpPath = pTask->locInfo.strPath; 2398 Utf8Str bucket; 2399 parseBucket(tmpPath, bucket); 2400 2401 /* We need a temporary directory which we can put the all disk images 2402 * in */ 2403 vrc = RTDirCreateTemp(pszTmpDir); 2404 if (RT_FAILURE(vrc)) 2514 2515 // this is safe to access because this thread only gets started 2516 // if pReader != NULL 2517 const ovf::OVFReader &reader = *m->pReader; 2518 2519 // create a session for the machine + disks we manipulate below 2520 rc = stack.pSession.createInprocObject(CLSID_Session); 2521 if (FAILED(rc)) throw rc; 2522 2523 list<ovf::VirtualSystem>::const_iterator it; 2524 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1; 2525 /* Iterate through all virtual systems of that appliance */ 2526 size_t i = 0; 2527 for (it = reader.m_llVirtualSystems.begin(), 2528 it1 = m->virtualSystemDescriptions.begin(); 2529 it != reader.m_llVirtualSystems.end(); 2530 ++it, ++it1, ++i) 2531 { 2532 const ovf::VirtualSystem &vsysThis = *it; 2533 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1); 2534 2535 ComPtr<IMachine> pNewMachine; 2536 2537 // there are two ways in which we can create a vbox machine from OVF: 2538 // -- either this OVF was written by vbox 3.2 or later, in which case there is a <vbox:Machine> element 2539 // in the <VirtualSystem>; then the VirtualSystemDescription::Data has a settings::MachineConfigFile 2540 // with all the machine config pretty-parsed; 2541 // -- or this is an OVF from an older vbox or an external source, and then we need to translate the 2542 // VirtualSystemDescriptionEntry and do import work 2543 2544 // Even for the vbox:Machine case, there are a number of configuration items that will be taken from 2545 // the OVF because otherwise the "override import parameters" mechanism in the GUI won't work. 2546 2547 // VM name 2548 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name); 2549 if (vsdeName.size() < 1) 2405 2550 throw setError(VBOX_E_FILE_ERROR, 2406 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 2407 2408 /* Add every disks of every virtual system to an internal list */ 2409 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; 2410 for (it = m->virtualSystemDescriptions.begin(); 2411 it != m->virtualSystemDescriptions.end(); 2412 ++it) 2413 { 2414 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it); 2415 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 2416 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH; 2417 for (itH = avsdeHDs.begin(); 2418 itH != avsdeHDs.end(); 2419 ++itH) 2420 { 2421 const Utf8Str &strTargetFile = (*itH)->strOvf; 2422 if (!strTargetFile.isEmpty()) 2423 { 2424 /* The temporary name of the target disk file */ 2425 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str())); 2426 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB)); 2427 } 2428 } 2551 tr("Missing VM name")); 2552 stack.strNameVBox = vsdeName.front()->strVboxCurrent; 2553 2554 // have VirtualBox suggest where the filename would be placed so we can 2555 // put the disk images in the same directory 2556 Bstr bstrMachineFilename; 2557 rc = mVirtualBox->ComposeMachineFilename(Bstr(stack.strNameVBox).raw(), 2558 NULL, 2559 bstrMachineFilename.asOutParam()); 2560 if (FAILED(rc)) throw rc; 2561 // and determine the machine folder from that 2562 stack.strMachineFolder = bstrMachineFilename; 2563 stack.strMachineFolder.stripFilename(); 2564 2565 // guest OS type 2566 std::list<VirtualSystemDescriptionEntry*> vsdeOS; 2567 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS); 2568 if (vsdeOS.size() < 1) 2569 throw setError(VBOX_E_FILE_ERROR, 2570 tr("Missing guest OS type")); 2571 stack.strOsTypeVBox = vsdeOS.front()->strVboxCurrent; 2572 2573 // CPU count 2574 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU); 2575 if (vsdeCPU.size() != 1) 2576 throw setError(VBOX_E_FILE_ERROR, tr("CPU count missing")); 2577 2578 const Utf8Str &cpuVBox = vsdeCPU.front()->strVboxCurrent; 2579 stack.cCPUs = (uint32_t)RTStrToUInt64(cpuVBox.c_str()); 2580 // We need HWVirt & IO-APIC if more than one CPU is requested 2581 if (stack.cCPUs > 1) 2582 { 2583 stack.fForceHWVirt = true; 2584 stack.fForceIOAPIC = true; 2429 2585 } 2430 2586 2431 /* Next we have to download the disk images */ 2432 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING); 2433 if (RT_FAILURE(vrc)) 2434 throw setError(VBOX_E_IPRT_ERROR, 2435 tr("Cannot create S3 service handler")); 2436 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask); 2437 2438 /* Download all files */ 2439 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1) 2440 { 2441 const pair<Utf8Str, ULONG> &s = (*it1); 2442 const Utf8Str &strSrcFile = s.first; 2443 /* Construct the source file name */ 2444 char *pszFilename = RTPathFilename(strSrcFile.c_str()); 2445 /* Advance to the next operation */ 2446 if (!pTask->pProgress.isNull()) 2447 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename).raw(), s.second); 2448 2449 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str()); 2450 if (RT_FAILURE(vrc)) 2451 { 2452 if (vrc == VERR_S3_CANCELED) 2453 throw S_OK; /* todo: !!!!!!!!!!!!! */ 2454 else if (vrc == VERR_S3_ACCESS_DENIED) 2455 throw setError(E_ACCESSDENIED, 2456 tr("Cannot download file '%s' from S3 storage server (Access denied). " 2457 "Make sure that your credentials are right. Also check that your host clock is properly synced"), 2458 pszFilename); 2459 else if (vrc == VERR_S3_NOT_FOUND) 2460 throw setError(VBOX_E_FILE_ERROR, 2461 tr("Cannot download file '%s' from S3 storage server (File not found)"), 2462 pszFilename); 2463 else 2464 throw setError(VBOX_E_IPRT_ERROR, 2465 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), 2466 pszFilename, vrc); 2467 } 2468 } 2469 2470 /* Provide a OVF file (haven't to exist) so the import routine can 2471 * figure out where the disk images/manifest file are located. */ 2472 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str())); 2473 /* Now check if there is an manifest file. This is optional. */ 2474 Utf8Str strManifestFile = queryManifestFileName(strTmpOvf); 2475 char *pszFilename = RTPathFilename(strManifestFile.c_str()); 2476 if (!pTask->pProgress.isNull()) 2477 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename).raw(), 1); 2478 2479 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */ 2480 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str()); 2481 if (RT_SUCCESS(vrc)) 2482 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0)); 2483 else if (RT_FAILURE(vrc)) 2484 { 2485 if (vrc == VERR_S3_CANCELED) 2486 throw S_OK; /* todo: !!!!!!!!!!!!! */ 2487 else if (vrc == VERR_S3_NOT_FOUND) 2488 vrc = VINF_SUCCESS; /* Not found is ok */ 2489 else if (vrc == VERR_S3_ACCESS_DENIED) 2490 throw setError(E_ACCESSDENIED, 2491 tr("Cannot download file '%s' from S3 storage server (Access denied)." 2492 "Make sure that your credentials are right. Also check that your host clock is properly synced"), 2493 pszFilename); 2494 else 2495 throw setError(VBOX_E_IPRT_ERROR, 2496 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), 2497 pszFilename, vrc); 2498 } 2499 2500 /* Close the connection early */ 2501 RTS3Destroy(hS3); 2502 hS3 = NIL_RTS3; 2503 2504 pTask->pProgress->SetNextOperation(BstrFmt(tr("Importing appliance")).raw(), m->ulWeightForXmlOperation); 2505 2506 ComObjPtr<Progress> progress; 2507 /* Import the whole temporary OVF & the disk images */ 2508 LocationInfo li; 2509 li.strPath = strTmpOvf; 2510 rc = importImpl(li, progress); 2511 if (FAILED(rc)) throw rc; 2512 2513 /* Unlock the appliance for the fs import thread */ 2514 appLock.release(); 2515 /* Wait until the import is done, but report the progress back to the 2516 caller */ 2517 ComPtr<IProgress> progressInt(progress); 2518 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */ 2519 2520 /* Again lock the appliance for the next steps */ 2521 appLock.acquire(); 2522 } 2523 catch(HRESULT aRC) 2524 { 2525 rc = aRC; 2526 } 2527 /* Cleanup */ 2528 RTS3Destroy(hS3); 2529 /* Delete all files which where temporary created */ 2530 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1) 2531 { 2532 const char *pszFilePath = (*it1).first.c_str(); 2533 if (RTPathExists(pszFilePath)) 2534 { 2535 vrc = RTFileDelete(pszFilePath); 2536 if (RT_FAILURE(vrc)) 2537 rc = setError(VBOX_E_FILE_ERROR, 2538 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc); 2539 } 2540 } 2541 /* Delete the temporary directory */ 2542 if (RTPathExists(pszTmpDir)) 2543 { 2544 vrc = RTDirRemove(pszTmpDir); 2545 if (RT_FAILURE(vrc)) 2546 rc = setError(VBOX_E_FILE_ERROR, 2547 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 2548 } 2549 if (pszTmpDir) 2550 RTStrFree(pszTmpDir); 2551 2552 LogFlowFunc(("rc=%Rhrc\n", rc)); 2553 LogFlowFuncLeave(); 2554 2555 return rc; 2587 // RAM 2588 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory); 2589 if (vsdeRAM.size() != 1) 2590 throw setError(VBOX_E_FILE_ERROR, tr("RAM size missing")); 2591 const Utf8Str &memoryVBox = vsdeRAM.front()->strVboxCurrent; 2592 stack.ulMemorySizeMB = (uint32_t)RTStrToUInt64(memoryVBox.c_str()); 2593 2594 #ifdef VBOX_WITH_USB 2595 // USB controller 2596 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController); 2597 // USB support is enabled if there's at least one such entry; to disable USB support, 2598 // the type of the USB item would have been changed to "ignore" 2599 stack.fUSBEnabled = vsdeUSBController.size() > 0; 2600 #endif 2601 // audio adapter 2602 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard); 2603 /* @todo: we support one audio adapter only */ 2604 if (vsdeAudioAdapter.size() > 0) 2605 stack.strAudioAdapter = vsdeAudioAdapter.front()->strVboxCurrent; 2606 2607 // for the description of the new machine, always use the OVF entry, the user may have changed it in the import config 2608 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description); 2609 if (vsdeDescription.size()) 2610 stack.strDescription = vsdeDescription.front()->strVboxCurrent; 2611 2612 // import vbox:machine or OVF now 2613 if (vsdescThis->m->pConfig) 2614 // vbox:Machine config 2615 importVBoxMachine(vsdescThis, pNewMachine, stack, pCallbacks, pStorage); 2616 else 2617 // generic OVF config 2618 importMachineGeneric(vsysThis, vsdescThis, pNewMachine, stack, pCallbacks, pStorage); 2619 2620 } // for (it = pAppliance->m->llVirtualSystems.begin() ... 2556 2621 } 2557 2622 -
trunk/src/VBox/Main/include/ApplianceImpl.h
r33238 r33289 24 24 #include "VirtualBoxBase.h" 25 25 26 /* Todo: This file needs massive cleanup. Split IAppliance in a public and 27 * private classes. */ 28 #include <iprt/tar.h> 29 26 30 /* VBox forward declarations */ 27 31 class Progress; … … 114 118 VirtualBox* const mVirtualBox; 115 119 120 struct ImportStack; 121 struct TaskOVF; 122 struct LocationInfo; 116 123 struct Data; // opaque, defined in ApplianceImpl.cpp 117 124 Data *m; 118 125 126 enum SetUpProgressMode { ImportFile, ImportS3, WriteFile, WriteS3 }; 127 128 /******************************************************************************* 129 * General stuff 130 ******************************************************************************/ 131 119 132 bool isApplianceIdle(); 120 121 133 HRESULT searchUniqueVMName(Utf8Str& aName) const; 122 134 HRESULT searchUniqueDiskImageFilePath(Utf8Str& aName) const; 135 HRESULT setUpProgress(ComObjPtr<Progress> &pProgress, 136 const Bstr &bstrDescription, 137 SetUpProgressMode mode); 123 138 void waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis, ComPtr<IProgress> &pProgressAsync); 124 139 void addWarning(const char* aWarning, ...); 125 126 140 void disksWeight(); 127 struct LocationInfo;128 enum SetUpProgressMode { ImportFileWithManifest, ImportFileNoManifest, ImportS3, WriteFile, WriteS3 };129 HRESULT setUpProgress(const LocationInfo &locInfo,130 ComObjPtr<Progress> &pProgress,131 const Bstr &bstrDescription,132 SetUpProgressMode mode);133 134 141 void parseURI(Utf8Str strUri, LocationInfo &locInfo) const; 135 142 void parseBucket(Utf8Str &aPath, Utf8Str &aBucket); 136 Utf8Str manifestFileName(const Utf8Str& aPath) const; 137 Utf8Str queryManifestFileName(const Utf8Str& aPath) const; 143 144 static DECLCALLBACK(int) taskThreadImportOrExport(RTTHREAD aThread, void *pvUser); 145 146 /******************************************************************************* 147 * Read stuff 148 ******************************************************************************/ 138 149 139 150 HRESULT readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress); 140 151 141 struct TaskOVF; 142 static DECLCALLBACK(int) taskThreadImportOrExport(RTTHREAD aThread, void *pvUser); 143 144 HRESULT readFS(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress); 145 HRESULT readFSOVF(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress); 146 HRESULT readFSOVA(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress); 152 HRESULT readFS(TaskOVF *pTask); 153 HRESULT readFSOVF(TaskOVF *pTask); 154 HRESULT readFSOVA(TaskOVF *pTask); 155 HRESULT readFSImpl(TaskOVF *pTask, PVDINTERFACEIO pCallbacks, PSHA1STORAGE pStorage); 147 156 HRESULT readS3(TaskOVF *pTask); 157 158 /******************************************************************************* 159 * Import stuff 160 ******************************************************************************/ 161 162 HRESULT importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress); 163 164 HRESULT importFS(TaskOVF *pTask); 165 HRESULT importFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock); 166 HRESULT importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock); 167 HRESULT importS3(TaskOVF *pTask); 168 169 HRESULT readManifestFile(const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PSHA1STORAGE pStorage); 170 HRESULT readTarManifestFile(RTTAR tar, const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PSHA1STORAGE pStorage); 171 HRESULT verifyManifestFile(const Utf8Str &strFile, ImportStack &stack, void *pvBuf, size_t cbSize); 148 172 149 173 void convertDiskAttachmentValues(const ovf::HardDiskController &hdc, … … 153 177 int32_t &lDevice); 154 178 155 HRESULT importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress);156 HRESULT manifestVerify(const LocationInfo &locInfo, const ovf::OVFReader &reader, ComObjPtr<Progress> &pProgress);157 158 HRESULT importFS(TaskOVF *pTask);159 HRESULT importFSOVF(TaskOVF *pTask);160 HRESULT importFSOVA(TaskOVF *pTask);161 162 struct ImportStack;163 179 void importOneDiskImage(const ovf::DiskImage &di, 164 180 const Utf8Str &strTargetPath, 165 ComPtr<IMedium> &pTargetHD, 166 ImportStack &stack); 181 ComObjPtr<Medium> &pTargetHD, 182 ImportStack &stack, 183 PVDINTERFACEIO pCallbacks, 184 PSHA1STORAGE pStorage); 167 185 void importMachineGeneric(const ovf::VirtualSystem &vsysThis, 168 186 ComObjPtr<VirtualSystemDescription> &vsdescThis, 169 187 ComPtr<IMachine> &pNewMachine, 170 ImportStack &stack); 188 ImportStack &stack, 189 PVDINTERFACEIO pCallbacks, 190 PSHA1STORAGE pStorage); 171 191 void importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThis, 172 192 ComPtr<IMachine> &pNewMachine, 173 ImportStack &stack); 174 175 HRESULT importS3(TaskOVF *pTask); 193 ImportStack &stack, 194 PVDINTERFACEIO pCallbacks, 195 PSHA1STORAGE pStorage); 196 void importMachines(ImportStack &stack, 197 PVDINTERFACEIO pCallbacks, 198 PSHA1STORAGE pStorage); 199 200 /******************************************************************************* 201 * Write stuff 202 ******************************************************************************/ 176 203 177 204 HRESULT writeImpl(OVFFormat aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress); 205 206 HRESULT writeFS(TaskOVF *pTask); 207 HRESULT writeFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock); 208 HRESULT writeFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock); 209 HRESULT writeFSImpl(TaskOVF *pTask, AutoWriteLockBase& writeLock, PVDINTERFACEIO pCallbacks, PSHA1STORAGE pStorage); 210 HRESULT writeS3(TaskOVF *pTask); 178 211 179 212 struct XMLStack; … … 186 219 XMLStack &stack); 187 220 188 HRESULT writeFS(TaskOVF *pTask);189 HRESULT writeFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock);190 HRESULT writeFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock);191 HRESULT writeFSImpl(TaskOVF *pTask, AutoWriteLockBase& writeLock, PVDINTERFACEIO pCallbacks, PSHA1STORAGE pStorage);192 HRESULT writeS3(TaskOVF *pTask);193 221 194 222 friend class Machine; -
trunk/src/VBox/Main/include/ApplianceImplPrivate.h
r33238 r33289 29 29 //////////////////////////////////////////////////////////////////////////////// 30 30 31 typedef std::pair<Utf8Str, Utf8Str> STRPAIR; 32 31 33 /* Describe a location for the import/export. The location could be a file on a 32 34 * local hard disk or a remote target based on the supported inet protocols. */ … … 153 155 bool fForceHWVirt; // if true, we force enabling hardware virtualization 154 156 bool fForceIOAPIC; // if true, we force enabling the IOAPIC 155 uint32_t ulMemorySizeMB; // virtual machi enRAM in megabytes157 uint32_t ulMemorySizeMB; // virtual machine RAM in megabytes 156 158 #ifdef VBOX_WITH_USB 157 159 bool fUSBEnabled; … … 167 169 // and will be cleaned up on errors 168 170 std::list<MyHardDiskAttachment> llHardDiskAttachments; // disks that were attached 169 std::list< ComPtr<IMedium> > llHardDisksCreated; // media that were created171 std::list<STRPAIR> llSrcDisksDigest; // Digests of the source disks 170 172 171 173 ImportStack(const LocationInfo &aLocInfo, … … 225 227 PVDINTERFACEIO RTFileCreateInterface(); 226 228 PVDINTERFACEIO RTTarCreateInterface(); 229 int Sha1ReadBuf(const char *pcszFilename, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, void *pvUser); 227 230 int Sha1WriteBuf(const char *pcszFilename, void *pvBuf, size_t cbSize, PVDINTERFACEIO pCallbacks, void *pvUser); 228 231 -
trunk/src/VBox/Runtime/common/checksum/manifest.cpp
r33057 r33289 67 67 typedef RTMANIFESTCALLBACKDATA* PRTMANIFESTCALLBACKDATA; 68 68 69 /******************************************************************************* 70 * Private functions 71 *******************************************************************************/ 72 73 DECLINLINE(char *) rtManifestPosOfCharInBuf(char const *pv, size_t cb, char c) 74 { 75 char *pb = (char *)pv; 76 for (; cb; --cb, ++pb) 77 if (RT_UNLIKELY(*pb == c)) 78 return pb; 79 return NULL; 80 } 81 82 DECLINLINE(size_t) rtManifestIndexOfCharInBuf(char const *pv, size_t cb, char c) 83 { 84 char const *pb = (char const *)pv; 85 for (size_t i=0; i < cb; ++i, ++pb) 86 if (RT_UNLIKELY(*pb == c)) 87 return i; 88 return cb; 89 } 69 90 70 91 int rtSHAProgressCallback(unsigned uPercent, void *pvUser) … … 76 97 } 77 98 99 /******************************************************************************* 100 * Public functions 101 *******************************************************************************/ 102 78 103 RTR3DECL(int) RTManifestVerify(const char *pszManifestFile, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed) 79 104 { 80 105 /* Validate input */ 81 106 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER); 107 108 /* Open the manifest file */ 109 RTFILE file; 110 int rc = RTFileOpen(&file, pszManifestFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); 111 if (RT_FAILURE(rc)) 112 return rc; 113 114 void *pvBuf = 0; 115 do 116 { 117 uint64_t cbSize; 118 rc = RTFileGetSize(file, &cbSize); 119 if (RT_FAILURE(rc)) 120 break; 121 122 /* Cast down for the case size_t < uint64_t. This isn't really correct, 123 but we consider manifest files bigger than size_t as not supported 124 by now. */ 125 size_t cbToRead = (size_t)cbSize; 126 pvBuf = RTMemAlloc(cbToRead); 127 if (!pvBuf) 128 { 129 rc = VERR_NO_MEMORY; 130 break; 131 } 132 133 size_t cbRead = 0; 134 rc = RTFileRead(file, pvBuf, cbToRead, &cbRead); 135 if (RT_FAILURE(rc)) 136 break; 137 138 rc = RTManifestVerifyFilesBuf(pvBuf, cbRead, paTests, cTests, piFailed); 139 }while (0); 140 141 /* Cleanup */ 142 if (pvBuf) 143 RTMemFree(pvBuf); 144 145 RTFileClose(file); 146 147 return rc; 148 } 149 150 RTR3DECL(int) RTManifestVerifyFiles(const char *pszManifestFile, const char * const *papszFiles, size_t cFiles, size_t *piFailed, 151 PFNRTPROGRESS pfnProgressCallback, void *pvUser) 152 { 153 /* Validate input */ 154 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER); 155 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER); 156 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER); 157 158 int rc = VINF_SUCCESS; 159 160 /* Create our compare list */ 161 PRTMANIFESTTEST paFiles = (PRTMANIFESTTEST)RTMemTmpAllocZ(sizeof(RTMANIFESTTEST) * cFiles); 162 if (!paFiles) 163 return VERR_NO_MEMORY; 164 165 RTMANIFESTCALLBACKDATA callback = { pfnProgressCallback, pvUser, cFiles, 0 }; 166 /* Fill our compare list */ 167 for (size_t i = 0; i < cFiles; ++i) 168 { 169 char *pszDigest; 170 if (pfnProgressCallback) 171 { 172 callback.cCurrentFile = i; 173 rc = RTSha1DigestFromFile(papszFiles[i], &pszDigest, rtSHAProgressCallback, &callback); 174 } 175 else 176 rc = RTSha1DigestFromFile(papszFiles[i], &pszDigest, NULL, NULL); 177 if (RT_FAILURE(rc)) 178 break; 179 paFiles[i].pszTestFile = (char*)papszFiles[i]; 180 paFiles[i].pszTestDigest = pszDigest; 181 } 182 183 /* Do the verification */ 184 if (RT_SUCCESS(rc)) 185 rc = RTManifestVerify(pszManifestFile, paFiles, cFiles, piFailed); 186 187 /* Cleanup */ 188 for (size_t i = 0; i < cFiles; ++i) 189 { 190 if (paFiles[i].pszTestDigest) 191 RTStrFree((char*)paFiles[i].pszTestDigest); 192 } 193 RTMemTmpFree(paFiles); 194 195 return rc; 196 } 197 198 RTR3DECL(int) RTManifestWriteFiles(const char *pszManifestFile, const char * const *papszFiles, size_t cFiles, 199 PFNRTPROGRESS pfnProgressCallback, void *pvUser) 200 { 201 /* Validate input */ 202 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER); 203 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER); 204 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_POINTER); 205 206 RTFILE file; 207 int rc = RTFileOpen(&file, pszManifestFile, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL); 208 if (RT_FAILURE(rc)) 209 return rc; 210 211 PRTMANIFESTTEST paFiles = 0; 212 void *pvBuf = 0; 213 do 214 { 215 paFiles = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST) * cFiles); 216 if (!paFiles) 217 { 218 rc = VERR_NO_MEMORY; 219 break; 220 } 221 222 RTMANIFESTCALLBACKDATA callback = { pfnProgressCallback, pvUser, cFiles, 0 }; 223 for (size_t i = 0; i < cFiles; ++i) 224 { 225 paFiles[i].pszTestFile = papszFiles[i]; 226 /* Calculate the SHA1 digest of every file */ 227 if (pfnProgressCallback) 228 { 229 callback.cCurrentFile = i; 230 rc = RTSha1DigestFromFile(paFiles[i].pszTestFile, (char**)&paFiles[i].pszTestDigest, rtSHAProgressCallback, &callback); 231 } 232 else 233 rc = RTSha1DigestFromFile(paFiles[i].pszTestFile, (char**)&paFiles[i].pszTestDigest, NULL, NULL); 234 if (RT_FAILURE(rc)) 235 break; 236 } 237 238 if (RT_SUCCESS(rc)) 239 { 240 size_t cbSize = 0; 241 rc = RTManifestWriteFilesBuf(&pvBuf, &cbSize, paFiles, cFiles); 242 if (RT_FAILURE(rc)) 243 break; 244 245 rc = RTFileWrite(file, pvBuf, cbSize, 0); 246 } 247 }while (0); 248 249 RTFileClose(file); 250 251 /* Cleanup */ 252 if (pvBuf) 253 RTMemFree(pvBuf); 254 for (size_t i = 0; i < cFiles; ++i) 255 if (paFiles[i].pszTestDigest) 256 RTStrFree((char*)paFiles[i].pszTestDigest); 257 RTMemFree(paFiles); 258 259 /* Delete the manifest file on failure */ 260 if (RT_FAILURE(rc)) 261 RTFileDelete(pszManifestFile); 262 263 return rc; 264 } 265 266 RTR3DECL(int) RTManifestVerifyFilesBuf(void *pvBuf, size_t cbSize, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed) 267 { 268 /* Validate input */ 269 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 270 AssertReturn(cbSize > 0, VERR_INVALID_PARAMETER); 82 271 AssertPtrReturn(paTests, VERR_INVALID_POINTER); 83 272 AssertReturn(cTests > 0, VERR_INVALID_PARAMETER); 84 85 /* Open the manifest file */ 86 PRTSTREAM pStream; 87 int rc = RTStrmOpen(pszManifestFile, "r", &pStream); 88 if (RT_FAILURE(rc)) 89 return rc; 273 AssertPtrNullReturn(piFailed, VERR_INVALID_POINTER); 274 275 int rc = VINF_SUCCESS; 90 276 91 277 PRTMANIFESTFILEENTRY paFiles = (PRTMANIFESTFILEENTRY)RTMemTmpAllocZ(sizeof(RTMANIFESTFILEENTRY) * cTests); 92 278 if (!paFiles) 93 {94 RTStrmClose(pStream);95 279 return VERR_NO_MEMORY; 96 }97 280 98 281 /* Fill our compare list */ … … 100 283 paFiles[i].pTestPattern = &paTests[i]; 101 284 285 char *pcBuf = (char*)pvBuf; 286 size_t cbRead = 0; 102 287 /* Parse the manifest file line by line */ 103 char szLine[1024];104 288 for (;;) 105 289 { 106 rc = RTStrmGetLine(pStream, szLine, sizeof(szLine)); 107 if (RT_FAILURE(rc)) 108 break; 109 size_t cch = strlen(szLine); 110 111 /* Skip empty lines */ 112 if (cch == 0) 290 if (cbRead >= cbSize) 291 break; 292 293 size_t cch = rtManifestIndexOfCharInBuf(pcBuf, cbSize - cbRead, '\n') + 1; 294 295 /* Skip empty lines (UNIX/DOS format) */ 296 if ( ( cch == 1 297 && pcBuf[0] == '\n') 298 || ( cch == 2 299 && pcBuf[0] == '\r' 300 && pcBuf[1] == '\n')) 301 { 302 pcBuf += cch; 303 cbRead += cch; 113 304 continue; 305 } 114 306 115 307 /** @todo r=bird: 308 * -# Better deal with this EOF line platform dependency 116 309 * -# The SHA1 test should probably include a blank space check. 117 310 * -# If there is a specific order to the elements in the string, it would be … … 122 315 /* Check for the digest algorithm */ 123 316 if ( cch < 4 124 || !( szLine[0] == 'S'125 && szLine[1] == 'H'126 && szLine[2] == 'A'127 && szLine[3] == '1'))317 || !( pcBuf[0] == 'S' 318 && pcBuf[1] == 'H' 319 && pcBuf[2] == 'A' 320 && pcBuf[3] == '1')) 128 321 { 129 322 /* Digest unsupported */ … … 133 326 134 327 /* Try to find the filename */ 135 char *pszNameStart = strchr(szLine, '(');328 char *pszNameStart = rtManifestPosOfCharInBuf(pcBuf, cch, '('); 136 329 if (!pszNameStart) 137 330 { … … 139 332 break; 140 333 } 141 char *pszNameEnd = strchr(szLine, ')');334 char *pszNameEnd = rtManifestPosOfCharInBuf(pcBuf, cch, ')'); 142 335 if (!pszNameEnd) 143 336 { … … 158 351 159 352 /* Try to find the digest sum */ 160 char *pszDigestStart = strchr(szLine, '=');353 char *pszDigestStart = rtManifestPosOfCharInBuf(pcBuf, cch, '=') + 1; 161 354 if (!pszDigestStart) 162 355 { … … 165 358 break; 166 359 } 167 char *pszDigest = ++pszDigestStart; 360 char *pszDigestEnd = rtManifestPosOfCharInBuf(pcBuf, cch, '\r'); 361 if (!pszDigestEnd) 362 pszDigestEnd = rtManifestPosOfCharInBuf(pcBuf, cch, '\n'); 363 if (!pszDigestEnd) 364 { 365 rc = VERR_MANIFEST_WRONG_FILE_FORMAT; 366 break; 367 } 368 /* Copy the digest part */ 369 size_t cchDigest = pszDigestEnd - pszDigestStart - 1; 370 char *pszDigest = (char *)RTMemTmpAlloc(cchDigest + 1); 371 if (!pszDigest) 372 { 373 rc = VERR_NO_MEMORY; 374 break; 375 } 376 memcpy(pszDigest, pszDigestStart + 1, cchDigest); 377 pszDigest[cchDigest] = '\0'; 168 378 169 379 /* Check our file list against the extracted data */ … … 181 391 } 182 392 RTMemTmpFree(pszName); 393 RTMemTmpFree(pszDigest); 183 394 if (!fFound) 184 395 { … … 187 398 break; 188 399 } 189 } 190 RTStrmClose(pStream); 400 401 pcBuf += cch; 402 cbRead += cch; 403 } 191 404 192 405 if ( rc == VINF_SUCCESS … … 229 442 } 230 443 231 232 RTR3DECL(int) RTManifestVerifyFiles(const char *pszManifestFile, const char * const *papszFiles, size_t cFiles, size_t *piFailed, 233 PFNRTPROGRESS pfnProgressCallback, void *pvUser) 234 { 235 /* Validate input */ 236 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER); 237 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER); 238 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_PARAMETER); 239 240 int rc = VINF_SUCCESS; 241 242 /* Create our compare list */ 243 PRTMANIFESTTEST paFiles = (PRTMANIFESTTEST)RTMemTmpAllocZ(sizeof(RTMANIFESTTEST) * cFiles); 244 if (!paFiles) 245 return VERR_NO_MEMORY; 246 247 RTMANIFESTCALLBACKDATA callback = { pfnProgressCallback, pvUser, cFiles, 0 }; 248 /* Fill our compare list */ 249 for (size_t i = 0; i < cFiles; ++i) 250 { 251 char *pszDigest; 252 if (pfnProgressCallback) 253 { 254 callback.cCurrentFile = i; 255 rc = RTSha1DigestFromFile(papszFiles[i], &pszDigest, rtSHAProgressCallback, &callback); 256 } 257 else 258 rc = RTSha1DigestFromFile(papszFiles[i], &pszDigest, NULL, NULL); 259 if (RT_FAILURE(rc)) 260 break; 261 paFiles[i].pszTestFile = (char*)papszFiles[i]; 262 paFiles[i].pszTestDigest = pszDigest; 263 } 264 265 /* Do the verification */ 266 if (RT_SUCCESS(rc)) 267 rc = RTManifestVerify(pszManifestFile, paFiles, cFiles, piFailed); 268 269 /* Cleanup */ 270 for (size_t i = 0; i < cFiles; ++i) 271 { 272 if (paFiles[i].pszTestDigest) 273 RTStrFree(paFiles[i].pszTestDigest); 274 } 275 RTMemTmpFree(paFiles); 276 277 return rc; 278 } 279 280 281 RTR3DECL(int) RTManifestWriteFiles(const char *pszManifestFile, const char * const *papszFiles, size_t cFiles, 282 PFNRTPROGRESS pfnProgressCallback, void *pvUser) 283 { 284 /* Validate input */ 285 AssertPtrReturn(pszManifestFile, VERR_INVALID_POINTER); 286 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER); 287 AssertPtrNullReturn(pfnProgressCallback, VERR_INVALID_PARAMETER); 288 289 /* Open a file to stream in */ 290 PRTSTREAM pStream; 291 int rc = RTStrmOpen(pszManifestFile, "w", &pStream); 292 if (RT_FAILURE(rc)) 293 return rc; 294 295 RTMANIFESTCALLBACKDATA callback = { pfnProgressCallback, pvUser, cFiles, 0 }; 296 for (size_t i = 0; i < cFiles; ++i) 297 { 298 /* Calculate the SHA1 digest of every file */ 299 char *pszDigest; 300 if (pfnProgressCallback) 301 { 302 callback.cCurrentFile = i; 303 rc = RTSha1DigestFromFile(papszFiles[i], &pszDigest, rtSHAProgressCallback, &callback); 304 } 305 else 306 rc = RTSha1DigestFromFile(papszFiles[i], &pszDigest, NULL, NULL); 307 if (RT_FAILURE(rc)) 308 break; 309 310 /* Add the entry to the manifest file */ 311 int cch = RTStrmPrintf(pStream, "SHA1 (%s)= %s\n", RTPathFilename(papszFiles[i]), pszDigest); 312 RTStrFree(pszDigest); 313 if (RT_UNLIKELY(cch < 0)) 314 { 315 rc = VERR_INTERNAL_ERROR; 316 break; 317 } 318 } 319 int rc2 = RTStrmClose(pStream); 320 if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) 321 rc2 = rc; 322 323 /* Delete the manifest file on failure */ 324 if (RT_FAILURE(rc)) 325 RTFileDelete(pszManifestFile); 326 327 return rc; 328 } 329 330 RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, const char * const *papszFileNames, const char * const *papszFileDigests, size_t cFiles) 444 RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, PRTMANIFESTTEST paFiles, size_t cFiles) 331 445 { 332 446 /* Validate input */ 333 447 AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER); 334 448 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); 335 AssertPtrReturn(papszFileNames, VERR_INVALID_POINTER); 336 AssertPtrReturn(papszFileDigests, VERR_INVALID_POINTER); 449 AssertPtrReturn(paFiles, VERR_INVALID_POINTER); 337 450 AssertReturn(cFiles > 0, VERR_INVALID_PARAMETER); 338 451 … … 342 455 for (size_t i = 0; i < cFiles; ++i) 343 456 { 344 size_t cbTmp = strlen(RTPathFilename(pa pszFileNames[i])) + strlen(papszFileDigests[i]) + 10;457 size_t cbTmp = strlen(RTPathFilename(paFiles[i].pszTestFile)) + strlen(paFiles[i].pszTestDigest) + 10; 345 458 cbMaxSize = RT_MAX(cbMaxSize, cbTmp); 346 459 cbSize += cbTmp; … … 357 470 for (size_t i = 0; i < cFiles; ++i) 358 471 { 359 size_t cch = RTStrPrintf(pszTmp, cbMaxSize + 1, "SHA1 (%s)= %s\n", RTPathFilename(pa pszFileNames[i]), papszFileDigests[i]);472 size_t cch = RTStrPrintf(pszTmp, cbMaxSize + 1, "SHA1 (%s)= %s\n", RTPathFilename(paFiles[i].pszTestFile), paFiles[i].pszTestDigest); 360 473 memcpy(&((char*)pvBuf)[cbPos], pszTmp, cch); 361 474 cbPos += cch; -
trunk/src/VBox/Runtime/common/misc/tar.cpp
r33058 r33289 92 92 #endif 93 93 94 typedef struct RTTARFILEINTERNAL* PRTTARFILEINTERNAL; 94 95 typedef struct RTTARINTERNAL 95 96 { … … 98 99 bool fFileOpenForWrite; 99 100 uint32_t fOpenMode; 101 bool fStreamMode; 102 PRTTARFILEINTERNAL pFileCache; 100 103 } RTTARINTERNAL; 101 104 typedef RTTARINTERNAL* PRTTARINTERNAL; … … 275 278 276 279 return rc; 280 } 281 282 DECLINLINE(PRTTARFILEINTERNAL) rtCreateTarFileInternal(PRTTARINTERNAL pInt, const char *pszFilename, uint32_t fOpen) 283 { 284 PRTTARFILEINTERNAL pFileInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL)); 285 if (!pFileInt) 286 return NULL; 287 288 pFileInt->u32Magic = RTTARFILE_MAGIC; 289 pFileInt->pTar = pInt; 290 pFileInt->pszFilename = RTStrDup(pszFilename); 291 pFileInt->fOpenMode = fOpen; 292 293 return pFileInt; 294 } 295 296 DECLINLINE(PRTTARFILEINTERNAL) rtCopyTarFileInternal(PRTTARFILEINTERNAL pInt) 297 { 298 PRTTARFILEINTERNAL pNewInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL)); 299 if (!pNewInt) 300 return NULL; 301 302 memcpy(pNewInt, pInt, sizeof(RTTARFILEINTERNAL)); 303 pNewInt->pszFilename = RTStrDup(pInt->pszFilename); 304 305 return pNewInt; 306 } 307 308 DECLINLINE(void) rtDeleteTarFileInternal(PRTTARFILEINTERNAL pInt) 309 { 310 if (pInt) 311 { 312 if (pInt->pszFilename) 313 RTStrFree(pInt->pszFilename); 314 pInt->u32Magic = RTTARFILE_MAGIC_DEAD; 315 RTMemFree(pInt); 316 } 277 317 } 278 318 … … 563 603 ******************************************************************************/ 564 604 565 RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char* pszTarname, uint32_t fMode )605 RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char* pszTarname, uint32_t fMode, bool fStream) 566 606 { 567 607 PRTTARINTERNAL pInt = (PRTTARINTERNAL)RTMemAllocZ(sizeof(RTTARINTERNAL)); … … 571 611 pInt->u32Magic = RTTAR_MAGIC; 572 612 pInt->fOpenMode = fMode; 613 pInt->fStreamMode = fStream && (fMode & RTFILE_O_READ); 573 614 574 615 int rc = VINF_SUCCESS; … … 618 659 rc = RTFileClose(pInt->hTarFile); 619 660 661 /* Delete any remaining cached file headers. */ 662 if (pInt->pFileCache) 663 { 664 rtDeleteTarFileInternal(pInt->pFileCache); 665 pInt->pFileCache = 0; 666 } 667 620 668 pInt->u32Magic = RTTAR_MAGIC_DEAD; 621 669 … … 634 682 if (!pInt->hTarFile) 635 683 return VERR_INVALID_HANDLE; 684 685 if (pInt->fStreamMode) 686 return VERR_INVALID_STATE; 636 687 637 688 if (fOpen & RTFILE_O_WRITE) … … 643 694 } 644 695 645 PRTTARFILEINTERNAL pFileInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL));696 PRTTARFILEINTERNAL pFileInt = rtCreateTarFileInternal(pInt, pszFilename, fOpen); 646 697 if (!pFileInt) 647 698 return VERR_NO_MEMORY; 648 699 649 pFileInt->u32Magic = RTTARFILE_MAGIC;650 651 700 int rc = VINF_SUCCESS; 652 701 do 653 702 { 654 pFileInt->pTar = pInt;655 pFileInt->pszFilename = RTStrDup(pszFilename);656 pFileInt->uStart = 0;657 pFileInt->cbSize = 0;658 pFileInt->cbSetSize = 0;659 pFileInt->fOpenMode = fOpen;660 pFileInt->uCurrentPos = 0;661 662 703 if (pFileInt->fOpenMode & RTFILE_O_WRITE) 663 704 { … … 718 759 719 760 /* In write mode: */ 720 if (pFileInt->fOpenMode & RTFILE_O_WRITE) 761 if (pFileInt->fOpenMode & RTFILE_O_READ) 762 { 763 /* In read mode, we want to make sure to stay at the aligned end of this 764 * file, so the next file could be read immediately. */ 765 uint64_t uCurPos = RTFileTell(pFileInt->pTar->hTarFile); 766 /* Check that the file pointer is somewhere within the last open file. 767 * If we are at the beginning (nothing read yet) nothing will be done. 768 * A user could open/close a file more than once, without reading 769 * something. */ 770 if (pFileInt->uStart + sizeof(RTTARRECORD) < uCurPos && uCurPos < RT_ALIGN(pFileInt->uStart + sizeof(RTTARRECORD) + pFileInt->cbSize, sizeof(RTTARRECORD))) 771 { 772 /* Seek to the next file header. */ 773 uint64_t uNextPos = RT_ALIGN(pFileInt->uStart + sizeof(RTTARRECORD) + pFileInt->cbSize, sizeof(RTTARRECORD)); 774 rc = RTFileSeek(pFileInt->pTar->hTarFile, uNextPos - uCurPos, RTFILE_SEEK_CURRENT, NULL); 775 } 776 } 777 else if (pFileInt->fOpenMode & RTFILE_O_WRITE) 721 778 { 722 779 pFileInt->pTar->fFileOpenForWrite = false; … … 756 813 while(0); 757 814 } 758 /* Nothing special in readmode. */759 815 760 816 /* Now cleanup and delete the handle */ 761 RTStrFree(pFileInt->pszFilename); 762 pFileInt->u32Magic = RTTARFILE_MAGIC_DEAD; 763 RTMemFree(pFileInt); 817 rtDeleteTarFileInternal(pFileInt); 764 818 765 819 return rc; … … 770 824 PRTTARFILEINTERNAL pFileInt = hFile; 771 825 RTTARFILE_VALID_RETURN(pFileInt); 826 827 if (pFileInt->pTar->fStreamMode) 828 return VERR_INVALID_STATE; 772 829 773 830 switch (uMethod) … … 822 879 RTTARFILE_VALID_RETURN(pFileInt); 823 880 881 /* Check that we not read behind the end of file. If so return immediately. */ 882 if (uOffset > pFileInt->cbSize) 883 { 884 if (pcbRead) 885 *pcbRead = 0; 886 return VINF_SUCCESS; /* ??? VERR_EOF */ 887 } 888 889 size_t cbToCopy = RT_MIN(pFileInt->cbSize - uOffset, cbToRead); 824 890 size_t cbTmpRead = 0; 825 int rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->uStart + 512 + uOffset, pvBuf, cbTo Read, &cbTmpRead);891 int rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->uStart + 512 + uOffset, pvBuf, cbToCopy, &cbTmpRead); 826 892 pFileInt->uCurrentPos = uOffset + cbTmpRead; 827 893 if (pcbRead) … … 1021 1087 /* Open the tar file */ 1022 1088 RTTAR hTar; 1023 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE );1089 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, false); 1024 1090 if (RT_FAILURE(rc)) 1025 1091 return rc; … … 1045 1111 /* Open the tar file */ 1046 1112 RTTAR hTar; 1047 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE );1113 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, false); 1048 1114 if (RT_FAILURE(rc)) 1049 1115 return rc; … … 1152 1218 do 1153 1219 { 1154 rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE );1220 rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, false); 1155 1221 if (RT_FAILURE(rc)) 1156 1222 break; … … 1215 1281 /* Open the tar file */ 1216 1282 RTTAR hTar; 1217 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE );1283 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, false); 1218 1284 if (RT_FAILURE(rc)) 1219 1285 return rc; … … 1286 1352 1287 1353 RTTAR hTar; 1288 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_CREATE | RTFILE_O_READWRITE | RTFILE_O_DENY_NONE );1354 int rc = RTTarOpen(&hTar, pszTarFile, RTFILE_O_CREATE | RTFILE_O_READWRITE | RTFILE_O_DENY_NONE, false); 1289 1355 if (RT_FAILURE(rc)) 1290 1356 return rc; … … 1316 1382 } 1317 1383 1384 /****************************************************************************** 1385 * Streaming Functions * 1386 ******************************************************************************/ 1387 1388 RTR3DECL(int) RTTarCurrentFile(RTTAR hTar, char **ppszFilename) 1389 { 1390 /* Validate input. */ 1391 AssertPtrNullReturn(ppszFilename, VERR_INVALID_POINTER); 1392 1393 PRTTARINTERNAL pInt = hTar; 1394 RTTAR_VALID_RETURN(pInt); 1395 1396 /* Open and close the file on the current position. This makes sure the 1397 * cache is filled in case we never read something before. On success it 1398 * will return the current filename. */ 1399 RTTARFILE hFile; 1400 int rc = RTTarFileOpenCurrentFile(hTar, &hFile, ppszFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); 1401 if (RT_SUCCESS(rc)) 1402 RTTarFileClose(hFile); 1403 1404 return rc; 1405 } 1406 1407 RTR3DECL(int) RTTarSeekNextFile(RTTAR hTar) 1408 { 1409 PRTTARINTERNAL pInt = hTar; 1410 RTTAR_VALID_RETURN(pInt); 1411 1412 int rc = VINF_SUCCESS; 1413 1414 if (!pInt->fStreamMode) 1415 return VERR_INVALID_STATE; 1416 1417 /* If there is nothing in the cache, it means we never read something. Just 1418 * ask for the current filename to fill the cache. */ 1419 if (!pInt->pFileCache) 1420 { 1421 rc = RTTarCurrentFile(hTar, 0); 1422 if (RT_FAILURE(rc)) 1423 return rc; 1424 } 1425 1426 /* Check that the file pointer is somewhere within the last open file. 1427 * If not we are somehow busted. */ 1428 uint64_t uCurPos = RTFileTell(pInt->hTarFile); 1429 if (!(pInt->pFileCache->uStart <= uCurPos && uCurPos < pInt->pFileCache->uStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize)) 1430 return VERR_INVALID_STATE; 1431 1432 /* Seek to the next file header. */ 1433 uint64_t uNextPos = RT_ALIGN(pInt->pFileCache->uStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize, sizeof(RTTARRECORD)); 1434 rc = RTFileSeek(pInt->hTarFile, uNextPos - uCurPos, RTFILE_SEEK_CURRENT, NULL); 1435 if (RT_FAILURE(rc)) 1436 return rc; 1437 1438 /* Again check the current filename to fill the cache with the new value. */ 1439 return RTTarCurrentFile(hTar, 0); 1440 } 1441 1442 RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **ppszFilename, uint32_t fOpen) 1443 { 1444 /* Validate input. */ 1445 AssertPtrReturn(phFile, VERR_INVALID_POINTER); 1446 AssertPtrNullReturn(ppszFilename, VERR_INVALID_POINTER); 1447 AssertReturn((fOpen & RTFILE_O_READ), VERR_INVALID_PARAMETER); /* Only valid in read mode. */ 1448 1449 PRTTARINTERNAL pInt = hTar; 1450 RTTAR_VALID_RETURN(pInt); 1451 1452 if (!pInt->fStreamMode) 1453 return VERR_INVALID_STATE; 1454 1455 int rc = VINF_SUCCESS; 1456 1457 /* Is there some cached entry? */ 1458 if (pInt->pFileCache) 1459 { 1460 /* Are we still direct behind that header? */ 1461 if (pInt->pFileCache->uStart + sizeof(RTTARRECORD) == RTFileTell(pInt->hTarFile)) 1462 { 1463 /* Yes, so the streaming can start. Just return the cached file 1464 * structure to the caller. */ 1465 *phFile = rtCopyTarFileInternal(pInt->pFileCache); 1466 if (ppszFilename) 1467 *ppszFilename = RTStrDup(pInt->pFileCache->pszFilename); 1468 return VINF_SUCCESS; 1469 }else 1470 { 1471 /* Else delete the last open file cache. Might be recreated below. */ 1472 rtDeleteTarFileInternal(pInt->pFileCache); 1473 pInt->pFileCache = 0; 1474 } 1475 } 1476 1477 PRTTARFILEINTERNAL pFileInt = 0; 1478 do 1479 { 1480 /* Try to read a header entry from the current position. If we aren't 1481 * on a header record, the header checksum will show and an error will 1482 * be returned. */ 1483 RTTARRECORD record; 1484 /* Read & verify a header record */ 1485 rc = rtTarReadHeaderRecord(pInt->hTarFile, &record); 1486 /* Check for error or EOF. */ 1487 if (RT_FAILURE(rc)) 1488 break; 1489 /* We support normal files only */ 1490 if ( record.h.linkflag == LF_OLDNORMAL 1491 || record.h.linkflag == LF_NORMAL) 1492 { 1493 pFileInt = rtCreateTarFileInternal(pInt, record.h.name, fOpen); 1494 if (!pFileInt) 1495 { 1496 rc = VERR_NO_MEMORY; 1497 break; 1498 } 1499 /* Get the file size */ 1500 rc = RTStrToUInt64Full(record.h.size, 8, &pFileInt->cbSize); 1501 if (RT_FAILURE(rc)) 1502 break; 1503 /* The start is -512 from here. */ 1504 pFileInt->uStart = RTFileTell(pInt->hTarFile) - sizeof(RTTARRECORD); 1505 /* Copy the new file structure to our cache. */ 1506 pInt->pFileCache = rtCopyTarFileInternal(pFileInt); 1507 if (ppszFilename) 1508 *ppszFilename = RTStrDup(pFileInt->pszFilename); 1509 } 1510 }while (0); 1511 1512 if (RT_FAILURE(rc)) 1513 { 1514 if (pFileInt) 1515 rtDeleteTarFileInternal(pFileInt); 1516 } 1517 else 1518 *phFile = pFileInt; 1519 1520 return rc; 1521 } 1522
Note:
See TracChangeset
for help on using the changeset viewer.