Changeset 31676 in vbox for trunk/src/VBox/Main
- Timestamp:
- Aug 13, 2010 6:40:53 PM (14 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/ApplianceImpl.cpp
r31562 r31676 667 667 BOOL fCanceled; 668 668 ULONG currentPercent; 669 ULONG cOp = 0; 669 670 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted)))) 670 671 { … … 676 677 break; 677 678 } 678 679 rc = pProgressAsync->COMGETTER(Percent(¤tPercent)); 679 /* Check if the current operation have changed. It is also possible 680 that in the meantime more than one async operation was finished. So 681 we have to loop as long as we reached the same operation count. */ 682 ULONG curOp; 683 for(;;) 684 { 685 rc = pProgressAsync->COMGETTER(Operation(&curOp)); 686 if (FAILED(rc)) throw rc; 687 if (cOp != curOp) 688 { 689 Bstr bstr; 690 ULONG currentWeight; 691 rc = pProgressAsync->COMGETTER(OperationDescription(bstr.asOutParam())); 692 if (FAILED(rc)) throw rc; 693 rc = pProgressAsync->COMGETTER(OperationWeight(¤tWeight)); 694 if (FAILED(rc)) throw rc; 695 rc = pProgressThis->SetNextOperation(bstr, currentWeight); 696 if (FAILED(rc)) throw rc; 697 ++cOp; 698 }else 699 break; 700 } 701 702 rc = pProgressAsync->COMGETTER(OperationPercent(¤tPercent)); 680 703 if (FAILED(rc)) throw rc; 681 if (!pProgressThis.isNull()) 682 pProgressThis->SetCurrentOperationProgress(currentPercent); 704 pProgressThis->SetCurrentOperationProgress(currentPercent); 683 705 if (fCompleted) 684 706 break; … … 745 767 } 746 768 769 #include <iprt/tar.h> 747 770 /** 748 771 * Called from Appliance::importImpl() and Appliance::writeImpl() to set up a … … 754 777 * @return 755 778 */ 756 HRESULT Appliance::setUpProgress(ComObjPtr<Progress> &pProgress, 779 HRESULT Appliance::setUpProgress(const LocationInfo &locInfo, 780 ComObjPtr<Progress> &pProgress, 757 781 const Bstr &bstrDescription, 758 782 SetUpProgressMode mode) … … 785 809 } 786 810 811 bool fOVA = locInfo.strPath.endsWith(".ova", Utf8Str::CaseInsensitive); 787 812 switch (mode) 788 813 { 789 814 case ImportFileNoManifest: 790 break; 791 815 { 816 if (fOVA) 817 { 818 // Another operation for packing 819 ++cOperations; 820 821 // assume that packing the files into the archive has the same weight than creating all files in the ovf exporting step 822 ulTotalOperationsWeight += m->ulTotalDisksMB; 823 } 824 break; 825 } 792 826 case ImportFileWithManifest: 793 827 case WriteFile: 828 { 794 829 ++cOperations; // another one for creating the manifest 795 830 796 // assume that c hecking or creating the manifest will take 10% of the time it takes to export the disks831 // assume that creating the manifest will take 10% of the time it takes to export the disks 797 832 m->ulWeightForManifestOperation = m->ulTotalDisksMB / 10; 798 833 ulTotalOperationsWeight += m->ulWeightForManifestOperation; 799 break; 800 834 if (fOVA) 835 { 836 // Another operation for packing 837 ++cOperations; 838 839 // assume that packing the files into the archive has the same weight than creating all files in the ovf exporting step 840 ulTotalOperationsWeight += m->ulTotalDisksMB; 841 } 842 break; 843 } 801 844 case ImportS3: 802 845 { … … 814 857 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init 815 858 ulTotalOperationsWeight += ulInitWeight; 859 break; 816 860 } 817 break;818 819 861 case WriteS3: 820 862 { … … 834 876 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */ 835 877 ulTotalOperationsWeight += ulOVFCreationWeight; 878 break; 836 879 } 837 break;838 880 } 839 881 … … 916 958 } 917 959 918 Utf8Str Appliance::manifestFileName(Utf8Str aPath) const 919 { 960 Utf8Str Appliance::manifestFileName(const Utf8Str& aPath) const 961 { 962 Utf8Str strTmpPath = aPath; 920 963 /* Get the name part */ 921 char *pszMfName = RTStrDup(RTPathFilename( aPath.c_str()));964 char *pszMfName = RTStrDup(RTPathFilename(strTmpPath.c_str())); 922 965 /* Strip any extensions */ 923 966 RTPathStripExt(pszMfName); 924 967 /* Path without the filename */ 925 aPath.stripFilename();968 strTmpPath.stripFilename(); 926 969 /* Format the manifest path */ 927 Utf8StrFmt strMfFile("%s/%s.mf", aPath.c_str(), pszMfName);970 Utf8StrFmt strMfFile("%s/%s.mf", strTmpPath.c_str(), pszMfName); 928 971 RTStrFree(pszMfName); 929 972 return strMfFile; … … 973 1016 case TaskOVF::Read: 974 1017 if (task->locInfo.storageType == VFSType_File) 975 taskrc = pAppliance->readFS(task->locInfo );1018 taskrc = pAppliance->readFS(task->locInfo, task->pProgress); 976 1019 else if (task->locInfo.storageType == VFSType_S3) 977 1020 taskrc = pAppliance->readS3(task.get()); … … 980 1023 case TaskOVF::Import: 981 1024 if (task->locInfo.storageType == VFSType_File) 982 taskrc = pAppliance->importFS(task ->locInfo, task->pProgress);1025 taskrc = pAppliance->importFS(task.get()); 983 1026 else if (task->locInfo.storageType == VFSType_S3) 984 1027 taskrc = pAppliance->importS3(task.get()); … … 987 1030 case TaskOVF::Write: 988 1031 if (task->locInfo.storageType == VFSType_File) 989 taskrc = pAppliance->writeFS(task ->locInfo, task->enFormat, task->pProgress);1032 taskrc = pAppliance->writeFS(task.get()); 990 1033 else if (task->locInfo.storageType == VFSType_S3) 991 1034 taskrc = pAppliance->writeS3(task.get()); -
trunk/src/VBox/Main/ApplianceImplExport.cpp
r31615 r31676 22 22 #include <iprt/s3.h> 23 23 #include <iprt/manifest.h> 24 #include <iprt/tar.h> 25 #include <iprt/stream.h> 24 26 25 27 #include <VBox/version.h> … … 566 568 // see if we can handle this file; for now we insist it has an ".ovf" extension 567 569 Utf8Str strPath = path; 568 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)) 570 if (!( strPath.endsWith(".ovf", Utf8Str::CaseInsensitive) 571 || strPath.endsWith(".ova", Utf8Str::CaseInsensitive))) 569 572 return setError(VBOX_E_FILE_ERROR, 570 tr("Appliance file must have .ovf extension"));573 tr("Appliance file must have .ovf or .ova extension")); 571 574 572 575 Utf8Str strFormat(format); … … 613 616 * 614 617 * 1) from the public Appliance::Write(). 615 * 2) from Appliance::writeS3(), which got called from a previous instance of Appliance::taskThreadWriteOVF(). 618 * 619 * 2) in a second worker thread; in that case, Appliance::Write() called Appliance::writeImpl(), which 620 * called Appliance::writeFSOVA(), which called Appliance::writeImpl(), which then called this again. 621 * 622 * 3) from Appliance::writeS3(), which got called from a previous instance of Appliance::taskThreadWriteOVF(). 616 623 * 617 624 * @param aFormat … … 625 632 try 626 633 { 627 rc = setUpProgress(aProgress, 634 rc = setUpProgress(aLocInfo, 635 aProgress, 628 636 BstrFmt(tr("Export appliance '%s'"), aLocInfo.strPath.c_str()), 629 637 (aLocInfo.storageType == VFSType_File) ? WriteFile : WriteS3); … … 1310 1318 1311 1319 /** 1312 * Actual worker code for writing out OVF to disk. This is called from Appliance::taskThreadWriteOVF()1313 * and therefore runs on the OVF write worker thread. This runs in two contexts:1320 * Actual worker code for writing out OVF/OVA to disk. This is called from Appliance::taskThreadWriteOVF() 1321 * and therefore runs on the OVF/OVA write worker thread. This runs in two contexts: 1314 1322 * 1315 1323 * 1) in a first worker thread; in that case, Appliance::Write() called Appliance::writeImpl(); … … 1323 1331 * @return 1324 1332 */ 1325 HRESULT Appliance::writeFS( const LocationInfo &locInfo, const OVFFormat enFormat, ComObjPtr<Progress> &pProgress)1333 HRESULT Appliance::writeFS(TaskOVF *pTask) 1326 1334 { 1335 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)) 1336 return writeFSOVF(pTask); 1337 else 1338 return writeFSOVA(pTask); 1339 } 1340 1341 HRESULT Appliance::writeFSOVF(TaskOVF *pTask) 1342 { 1343 LogFlowFuncEnter(); 1327 1344 LogFlowFunc(("ENTER appliance %p\n", this)); 1328 1345 … … 1339 1356 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope"); 1340 1357 1341 pelmRoot->setAttribute("ovf:version", ( enFormat == OVF_1_0) ? "1.0" : "0.9");1358 pelmRoot->setAttribute("ovf:version", (pTask->enFormat == OVF_1_0) ? "1.0" : "0.9"); 1342 1359 pelmRoot->setAttribute("xml:lang", "en-US"); 1343 1360 1344 Utf8Str strNamespace = ( enFormat == OVF_0_9)1361 Utf8Str strNamespace = (pTask->enFormat == OVF_0_9) 1345 1362 ? "http://www.vmware.com/schema/ovf/1/envelope" // 0.9 1346 1363 : "http://schemas.dmtf.org/ovf/envelope/1"; // 1.0 … … 1364 1381 </DiskSection> */ 1365 1382 xml::ElementNode *pelmDiskSection; 1366 if ( enFormat == OVF_0_9)1383 if (pTask->enFormat == OVF_0_9) 1367 1384 { 1368 1385 // <Section xsi:type="ovf:DiskSection_Type"> … … 1389 1406 </NetworkSection> */ 1390 1407 xml::ElementNode *pelmNetworkSection; 1391 if ( enFormat == OVF_0_9)1408 if (pTask->enFormat == OVF_0_9) 1392 1409 { 1393 1410 // <Section xsi:type="ovf:NetworkSection_Type"> … … 1415 1432 if (m->virtualSystemDescriptions.size() > 1) 1416 1433 { 1417 if ( enFormat == OVF_0_9)1434 if (pTask->enFormat == OVF_0_9) 1418 1435 throw setError(VBOX_E_FILE_ERROR, 1419 1436 tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0")); … … 1439 1456 &llElementsWithUuidAttributes, 1440 1457 vsdescThis, 1441 enFormat,1458 pTask->enFormat, 1442 1459 stack); // disks and networks stack 1443 1460 } … … 1494 1511 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf; 1495 1512 // target path needs to be composed from where the output OVF is 1496 Utf8Str strTargetFilePath( locInfo.strPath);1513 Utf8Str strTargetFilePath(pTask->locInfo.strPath); 1497 1514 strTargetFilePath.stripFilename(); 1498 1515 strTargetFilePath.append("/"); … … 1518 1535 1519 1536 // advance to the next operation 1520 p Progress->SetNextOperation(BstrFmt(tr("Exporting to disk image '%s'"), strTargetFilePath.c_str()),1521 pDiskEntry->ulSizeMB); // operation's weight, as set up with the IProgress originally);1537 pTask->pProgress->SetNextOperation(BstrFmt(tr("Exporting to disk image '%s'"), RTPathFilename(strTargetFilePath.c_str())), 1538 pDiskEntry->ulSizeMB); // operation's weight, as set up with the IProgress originally); 1522 1539 1523 1540 // now wait for the background disk operation to complete; this throws HRESULTs on error 1524 waitForAsyncProgress(p Progress, pProgress2);1541 waitForAsyncProgress(pTask->pProgress, pProgress2); 1525 1542 } 1526 1543 catch (HRESULT rc3) … … 1568 1585 pelmDisk->setAttribute("ovf:fileRef", strFileRef); 1569 1586 pelmDisk->setAttribute("ovf:format", 1570 ( enFormat == OVF_0_9)1587 (pTask->enFormat == OVF_0_9) 1571 1588 ? "http://www.vmware.com/specifications/vmdk.html#sparse" // must be sparse or ovftool chokes 1572 1589 : "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" … … 1593 1610 // now go write the XML 1594 1611 xml::XmlFileWriter writer(doc); 1595 writer.write( locInfo.strPath.c_str(), false /*fSafe*/);1612 writer.write(pTask->locInfo.strPath.c_str(), false /*fSafe*/); 1596 1613 1597 1614 // Create & write the manifest file 1598 Utf8Str strMfFile = manifestFileName( locInfo.strPath.c_str());1615 Utf8Str strMfFile = manifestFileName(pTask->locInfo.strPath.c_str()); 1599 1616 const char *pcszManifestFileOnly = RTPathFilename(strMfFile.c_str()); 1600 p Progress->SetNextOperation(BstrFmt(tr("Creating manifest file '%s'"), pcszManifestFileOnly),1601 m->ulWeightForManifestOperation); // operation's weight, as set up with the IProgress originally);1617 pTask->pProgress->SetNextOperation(BstrFmt(tr("Creating manifest file '%s'"), pcszManifestFileOnly), 1618 m->ulWeightForManifestOperation); // operation's weight, as set up with the IProgress originally); 1602 1619 1603 1620 const char** ppManifestFiles = (const char**)RTMemAlloc(sizeof(char*)*diskList.size() + 1); 1604 ppManifestFiles[0] = locInfo.strPath.c_str();1621 ppManifestFiles[0] = pTask->locInfo.strPath.c_str(); 1605 1622 list<Utf8Str>::const_iterator it1; 1606 1623 size_t i = 1; … … 1636 1653 } 1637 1654 1638 /** 1639 * Worker code for writing out OVF to the cloud. This is called from Appliance::taskThreadWriteOVF() 1640 * in S3 mode and therefore runs on the OVF write worker thread. This then starts a second worker 1641 * thread to create temporary files (see Appliance::writeFS()). 1642 * 1643 * @param pTask 1644 * @return 1645 */ 1646 HRESULT Appliance::writeS3(TaskOVF *pTask) 1655 HRESULT Appliance::writeFSOVA(TaskOVF *pTask) 1647 1656 { 1648 1657 LogFlowFuncEnter(); … … 1657 1666 1658 1667 int vrc = VINF_SUCCESS; 1659 RTS3 hS3 = NIL_RTS3; 1668 1660 1669 char szOSTmpDir[RTPATH_MAX]; 1661 1670 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir)); … … 1663 1672 char *pszTmpDir; 1664 1673 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir); 1665 list< pair<Utf8Str, ULONG> > filesList; 1666 1667 // todo: 1668 // - usable error codes 1669 // - seems snapshot filenames are problematic {uuid}.vdi 1674 list<Utf8Str> filesList; 1675 const char** paFiles = 0; 1676 1670 1677 try 1671 1678 { 1672 /* Extract the bucket*/1679 /* Extract the path */ 1673 1680 Utf8Str tmpPath = pTask->locInfo.strPath; 1674 Utf8Str bucket; 1675 parseBucket(tmpPath, bucket); 1681 /* Remove the ova extension */ 1682 tmpPath.stripExt(); 1683 tmpPath += ".ovf"; 1676 1684 1677 1685 /* We need a temporary directory which we can put the OVF file & all … … 1680 1688 if (RT_FAILURE(vrc)) 1681 1689 throw setError(VBOX_E_FILE_ERROR, 1682 tr("Cannot create temporary directory '%s' "), pszTmpDir);1690 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 1683 1691 1684 1692 /* The temporary name of the target OVF file */ … … 1706 1714 if (RT_FAILURE(vrc)) 1707 1715 throw setError(VBOX_E_FILE_ERROR, 1708 tr("Cannot find source file '%s' "), strTmpOvf.c_str());1716 tr("Cannot find source file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc); 1709 1717 /* Add the OVF file */ 1710 filesList.push_back( pair<Utf8Str, ULONG>(strTmpOvf, m->ulWeightForXmlOperation)); /* Use 1% of the total for the OVF file upload */1718 filesList.push_back(strTmpOvf); /* Use 1% of the total for the OVF file upload */ 1711 1719 Utf8Str strMfFile = manifestFileName(strTmpOvf); 1712 filesList.push_back(pair<Utf8Str, ULONG>(strMfFile , m->ulWeightForXmlOperation)); /* Use 1% of the total for the manifest file upload */ 1713 1720 filesList.push_back(strMfFile); /* Use 1% of the total for the manifest file upload */ 1721 1722 ULONG ulWeight = 2 * m->ulWeightForXmlOperation; 1714 1723 /* Now add every disks of every virtual system */ 1715 1724 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; … … 1734 1743 if (RT_FAILURE(vrc)) 1735 1744 throw setError(VBOX_E_FILE_ERROR, 1736 tr("Cannot find source file '%s'"), strTargetFilePath.c_str()); 1745 tr("Cannot find source file '%s' (%Rrc)"), strTargetFilePath.c_str(), vrc); 1746 filesList.push_back(strTargetFilePath); 1747 ulWeight += (*itH)->ulSizeMB; 1748 } 1749 } 1750 paFiles = (const char**)RTMemAlloc(sizeof(char*) * filesList.size()); 1751 int i = 0; 1752 for (list<Utf8Str>::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1, ++i) 1753 paFiles[i] = (*it1).c_str(); 1754 pTask->pProgress->SetNextOperation(BstrFmt(tr("Packing into '%s'"), RTPathFilename(pTask->locInfo.strPath.c_str())), ulWeight); 1755 /* Create the tar file out of our file list. */ 1756 vrc = RTTarCreate(pTask->locInfo.strPath.c_str(), paFiles, filesList.size(), pTask->updateProgress, &pTask); 1757 if (RT_FAILURE(vrc)) 1758 throw setError(VBOX_E_FILE_ERROR, 1759 tr("Cannot create OVA file '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc); 1760 } 1761 catch(HRESULT aRC) 1762 { 1763 rc = aRC; 1764 } 1765 1766 /* Delete the temporary files list */ 1767 if (paFiles) 1768 RTMemFree(paFiles); 1769 /* Delete all files which where temporary created */ 1770 for (list<Utf8Str>::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1) 1771 { 1772 const char *pszFilePath = (*it1).c_str(); 1773 if (RTPathExists(pszFilePath)) 1774 { 1775 vrc = RTFileDelete(pszFilePath); 1776 if (RT_FAILURE(vrc)) 1777 rc = setError(VBOX_E_FILE_ERROR, 1778 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc); 1779 } 1780 } 1781 /* Delete the temporary directory */ 1782 if (RTPathExists(pszTmpDir)) 1783 { 1784 vrc = RTDirRemove(pszTmpDir); 1785 if (RT_FAILURE(vrc)) 1786 rc = setError(VBOX_E_FILE_ERROR, 1787 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 1788 } 1789 if (pszTmpDir) 1790 RTStrFree(pszTmpDir); 1791 1792 LogFlowFunc(("rc=%Rhrc\n", rc)); 1793 LogFlowFuncLeave(); 1794 1795 return rc; 1796 } 1797 1798 /** 1799 * Worker code for writing out OVF to the cloud. This is called from Appliance::taskThreadWriteOVF() 1800 * in S3 mode and therefore runs on the OVF write worker thread. This then starts a second worker 1801 * thread to create temporary files (see Appliance::writeFS()). 1802 * 1803 * @param pTask 1804 * @return 1805 */ 1806 HRESULT Appliance::writeS3(TaskOVF *pTask) 1807 { 1808 LogFlowFuncEnter(); 1809 LogFlowFunc(("Appliance %p\n", this)); 1810 1811 AutoCaller autoCaller(this); 1812 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1813 1814 HRESULT rc = S_OK; 1815 1816 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS); 1817 1818 int vrc = VINF_SUCCESS; 1819 RTS3 hS3 = NIL_RTS3; 1820 char szOSTmpDir[RTPATH_MAX]; 1821 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir)); 1822 /* The template for the temporary directory created below */ 1823 char *pszTmpDir; 1824 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir); 1825 list< pair<Utf8Str, ULONG> > filesList; 1826 1827 // todo: 1828 // - usable error codes 1829 // - seems snapshot filenames are problematic {uuid}.vdi 1830 try 1831 { 1832 /* Extract the bucket */ 1833 Utf8Str tmpPath = pTask->locInfo.strPath; 1834 Utf8Str bucket; 1835 parseBucket(tmpPath, bucket); 1836 1837 /* We need a temporary directory which we can put the OVF file & all 1838 * disk images in */ 1839 vrc = RTDirCreateTemp(pszTmpDir); 1840 if (RT_FAILURE(vrc)) 1841 throw setError(VBOX_E_FILE_ERROR, 1842 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 1843 1844 /* The temporary name of the target OVF file */ 1845 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str())); 1846 1847 /* Prepare the temporary writing of the OVF */ 1848 ComObjPtr<Progress> progress; 1849 /* Create a temporary file based location info for the sub task */ 1850 LocationInfo li; 1851 li.strPath = strTmpOvf; 1852 rc = writeImpl(pTask->enFormat, li, progress); 1853 if (FAILED(rc)) throw rc; 1854 1855 /* Unlock the appliance for the writing thread */ 1856 appLock.release(); 1857 /* Wait until the writing is done, but report the progress back to the 1858 caller */ 1859 ComPtr<IProgress> progressInt(progress); 1860 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */ 1861 1862 /* Again lock the appliance for the next steps */ 1863 appLock.acquire(); 1864 1865 vrc = RTPathExists(strTmpOvf.c_str()); /* Paranoid check */ 1866 if (RT_FAILURE(vrc)) 1867 throw setError(VBOX_E_FILE_ERROR, 1868 tr("Cannot find source file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc); 1869 /* Add the OVF file */ 1870 filesList.push_back(pair<Utf8Str, ULONG>(strTmpOvf, m->ulWeightForXmlOperation)); /* Use 1% of the total for the OVF file upload */ 1871 Utf8Str strMfFile = manifestFileName(strTmpOvf); 1872 filesList.push_back(pair<Utf8Str, ULONG>(strMfFile , m->ulWeightForXmlOperation)); /* Use 1% of the total for the manifest file upload */ 1873 1874 /* Now add every disks of every virtual system */ 1875 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; 1876 for (it = m->virtualSystemDescriptions.begin(); 1877 it != m->virtualSystemDescriptions.end(); 1878 ++it) 1879 { 1880 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it); 1881 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 1882 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH; 1883 for (itH = avsdeHDs.begin(); 1884 itH != avsdeHDs.end(); 1885 ++itH) 1886 { 1887 const Utf8Str &strTargetFileNameOnly = (*itH)->strOvf; 1888 /* Target path needs to be composed from where the output OVF is */ 1889 Utf8Str strTargetFilePath(strTmpOvf); 1890 strTargetFilePath.stripFilename(); 1891 strTargetFilePath.append("/"); 1892 strTargetFilePath.append(strTargetFileNameOnly); 1893 vrc = RTPathExists(strTargetFilePath.c_str()); /* Paranoid check */ 1894 if (RT_FAILURE(vrc)) 1895 throw setError(VBOX_E_FILE_ERROR, 1896 tr("Cannot find source file '%s' (%Rrc)"), strTargetFilePath.c_str(), vrc); 1737 1897 filesList.push_back(pair<Utf8Str, ULONG>(strTargetFilePath, (*itH)->ulSizeMB)); 1738 1898 } -
trunk/src/VBox/Main/ApplianceImplImport.cpp
r31615 r31676 23 23 #include <iprt/sha.h> 24 24 #include <iprt/manifest.h> 25 #include <iprt/tar.h> 26 #include <iprt/stream.h> 25 27 26 28 #include <VBox/com/array.h> … … 77 79 // see if we can handle this file; for now we insist it has an ".ovf" extension 78 80 Utf8Str strPath (path); 79 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)) 81 if (!( strPath.endsWith(".ovf", Utf8Str::CaseInsensitive) 82 || strPath.endsWith(".ova", Utf8Str::CaseInsensitive))) 80 83 return setError(VBOX_E_FILE_ERROR, 81 84 tr("Appliance file must have .ovf extension")); … … 599 602 * This will then open the OVF with ovfreader.cpp. 600 603 * 601 * This is in a separate private method because it is used from t wolocations:604 * This is in a separate private method because it is used from three locations: 602 605 * 603 606 * 1) from the public Appliance::Read(). 604 * 2) from Appliance::readS3(), which got called from a previous instance of Appliance::taskThreadImportOrExport(). 607 * 608 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which 609 * called Appliance::readFSOVA(), which called Appliance::importImpl(), which then called this again. 610 * 611 * 3) from Appliance::readS3(), which got called from a previous instance of Appliance::taskThreadImportOrExport(). 605 612 * 606 613 * @param aLocInfo … … 658 665 * @return 659 666 */ 660 HRESULT Appliance::readFS(const LocationInfo &locInfo) 667 HRESULT Appliance::readFS(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress) 668 { 669 if (locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)) 670 return readFSOVF(locInfo, pProgress); 671 else 672 return readFSOVA(locInfo, pProgress); 673 } 674 675 HRESULT Appliance::readFSOVF(const LocationInfo &locInfo, ComObjPtr<Progress> & /* pProgress */) 661 676 { 662 677 LogFlowFuncEnter(); … … 693 708 rc = aRC; 694 709 } 710 711 LogFlowFunc(("rc=%Rhrc\n", rc)); 712 LogFlowFuncLeave(); 713 714 return rc; 715 } 716 717 HRESULT Appliance::readFSOVA(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress) 718 { 719 LogFlowFuncEnter(); 720 LogFlowFunc(("Appliance %p\n", this)); 721 722 AutoCaller autoCaller(this); 723 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 724 725 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS); 726 727 HRESULT rc = S_OK; 728 int vrc = VINF_SUCCESS; 729 char szOSTmpDir[RTPATH_MAX]; 730 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir)); 731 /* The template for the temporary directory created below */ 732 char *pszTmpDir; 733 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir); 734 list< pair<Utf8Str, ULONG> > filesList; 735 Utf8Str strTmpOvf; 736 737 try 738 { 739 /* Extract the path */ 740 Utf8Str tmpPath = locInfo.strPath; 741 /* Remove the ova extension */ 742 tmpPath.stripExt(); 743 tmpPath += ".ovf"; 744 745 /* We need a temporary directory which we can put the OVF file & all 746 * disk images in */ 747 vrc = RTDirCreateTemp(pszTmpDir); 748 if (RT_FAILURE(vrc)) 749 DebugBreakThrow(setError(VBOX_E_FILE_ERROR, 750 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc)); 751 752 /* The temporary name of the target OVF file */ 753 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str())); 754 755 /* Next we have to download the OVF */ 756 char *papszFile = RTPathFilename(strTmpOvf.c_str()); 757 vrc = RTTarExtractFiles(locInfo.strPath.c_str(), pszTmpDir, &papszFile, 1, 0, 0); 758 if (RT_FAILURE(vrc)) 759 { 760 if (vrc == VERR_FILE_NOT_FOUND) 761 DebugBreakThrow(setError(VBOX_E_IPRT_ERROR, 762 tr("Can't find ovf file '%s' in archive '%s' (%Rrc)"), papszFile, locInfo.strPath.c_str(), vrc)); 763 else 764 DebugBreakThrow(setError(VBOX_E_IPRT_ERROR, 765 tr("Can't unpack the archive file '%s' (%Rrc)"), locInfo.strPath.c_str(), vrc)); 766 } 767 768 // todo: check this out 769 // pTask->pProgress->SetNextOperation(Bstr(tr("Reading")), 1); 770 771 /* Prepare the temporary reading of the OVF */ 772 ComObjPtr<Progress> progress; 773 LocationInfo li; 774 li.strPath = strTmpOvf; 775 /* Start the reading from the fs */ 776 rc = readImpl(li, progress); 777 if (FAILED(rc)) DebugBreakThrow(rc); 778 779 /* Unlock the appliance for the reading thread */ 780 appLock.release(); 781 /* Wait until the reading is done, but report the progress back to the 782 caller */ 783 ComPtr<IProgress> progressInt(progress); 784 waitForAsyncProgress(pProgress, progressInt); /* Any errors will be thrown */ 785 786 /* Again lock the appliance for the next steps */ 787 appLock.acquire(); 788 } 789 catch(HRESULT aRC) 790 { 791 rc = aRC; 792 } 793 /* Delete all files which where temporary created */ 794 if (RTPathExists(strTmpOvf.c_str())) 795 { 796 vrc = RTFileDelete(strTmpOvf.c_str()); 797 if (RT_FAILURE(vrc)) 798 rc = setError(VBOX_E_FILE_ERROR, 799 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc); 800 } 801 /* Delete the temporary directory */ 802 if (RTPathExists(pszTmpDir)) 803 { 804 vrc = RTDirRemove(pszTmpDir); 805 if (RT_FAILURE(vrc)) 806 rc = setError(VBOX_E_FILE_ERROR, 807 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 808 } 809 if (pszTmpDir) 810 RTStrFree(pszTmpDir); 695 811 696 812 LogFlowFunc(("rc=%Rhrc\n", rc)); … … 939 1055 * @return 940 1056 */ 941 HRESULT Appliance::importImpl(const LocationInfo & aLocInfo,942 ComObjPtr<Progress> & aProgress)1057 HRESULT Appliance::importImpl(const LocationInfo &locInfo, 1058 ComObjPtr<Progress> &progress) 943 1059 { 944 1060 HRESULT rc = S_OK; 945 1061 946 1062 SetUpProgressMode mode; 947 m->strManifestFile.setNull();948 if (aLocInfo.storageType == VFSType_File)949 {950 Utf8Str strMfFile = manifestFileName( aLocInfo.strPath);951 if ( RTPathExists(strMfFile.c_str()))952 { 953 m->strManifestFile = strMfFile;954 mode = ImportFileWithManifest;1063 if (locInfo.storageType == VFSType_File) 1064 { 1065 mode = ImportFileNoManifest; 1066 Utf8Str strMfFile = manifestFileName(locInfo.strPath); 1067 if (!locInfo.strPath.endsWith(".ova", Utf8Str::CaseInsensitive)) 1068 { 1069 if (RTPathExists(strMfFile.c_str())) 1070 mode = ImportFileWithManifest; 955 1071 } 956 1072 else 957 mode = ImportFileNoManifest; 1073 { 1074 if (RTTarQueryFileExists(locInfo.strPath.c_str(), RTPathFilename(strMfFile.c_str())) == VINF_SUCCESS) 1075 mode = ImportFileWithManifest; 1076 } 958 1077 } 959 1078 else 960 1079 mode = ImportS3; 961 1080 962 rc = setUpProgress(aProgress, 963 BstrFmt(tr("Importing appliance '%s'"), aLocInfo.strPath.c_str()), 1081 rc = setUpProgress(locInfo, 1082 progress, 1083 BstrFmt(tr("Importing appliance '%s'"), locInfo.strPath.c_str()), 964 1084 mode); 965 1085 if (FAILED(rc)) DebugBreakThrow(rc); 966 1086 967 1087 /* Initialize our worker task */ 968 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Import, aLocInfo, aProgress));1088 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Import, locInfo, progress)); 969 1089 970 1090 rc = task->startThread(); … … 993 1113 HRESULT rc = S_OK; 994 1114 995 if (!m->strManifestFile.isEmpty()) 996 { 997 const char *pcszManifestFileOnly = RTPathFilename(m->strManifestFile.c_str()); 1115 Utf8Str strManifestFile = manifestFileName(locInfo.strPath); 1116 if (!strManifestFile.isEmpty()) 1117 { 1118 const char *pcszManifestFileOnly = RTPathFilename(strManifestFile.c_str()); 998 1119 pProgress->SetNextOperation(BstrFmt(tr("Verifying manifest file '%s'"), pcszManifestFileOnly), 999 1120 m->ulWeightForManifestOperation); // operation's weight, as set up with the IProgress originally … … 1043 1164 // this call can take a very long time 1044 1165 size_t cIndexOnError; 1045 vrc = RTManifestVerify( m->strManifestFile.c_str(),1166 vrc = RTManifestVerify(strManifestFile.c_str(), 1046 1167 pTestList, 1047 1168 filesList.size() + 1, … … 1075 1196 * VirtualSystemScription instances created by Appliance::Interpret(). 1076 1197 * 1077 * This runs in t wocontexts:1198 * This runs in three contexts: 1078 1199 * 1079 1200 * 1) in a first worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(); 1080 1201 * 1081 1202 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which 1082 * called Appliance::importS3(), which called Appliance::importImpl(), which then called this. 1203 * called Appliance::importFSOVA(), which called Appliance::importImpl(), which then called this again. 1204 * 1205 * 3) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which 1206 * called Appliance::importS3(), which called Appliance::importImpl(), which then called this again. 1083 1207 * 1084 1208 * @param pTask 1085 1209 * @return 1086 1210 */ 1087 HRESULT Appliance::importFS(const LocationInfo &locInfo, 1088 ComObjPtr<Progress> &pProgress) 1211 HRESULT Appliance::importFS(TaskOVF *pTask) 1212 { 1213 if (!Utf8Str(RTPathExt(pTask->locInfo.strPath.c_str())).compare(".ovf", Utf8Str::CaseInsensitive)) 1214 return importFSOVF(pTask); 1215 else 1216 return importFSOVA(pTask); 1217 } 1218 1219 HRESULT Appliance::importFSOVF(TaskOVF *pTask) 1089 1220 { 1090 1221 LogFlowFuncEnter(); … … 1094 1225 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1095 1226 1096 Assert(!p Progress.isNull());1227 Assert(!pTask->pProgress.isNull()); 1097 1228 1098 1229 // Change the appliance state so we can safely leave the lock while doing time-consuming … … 1112 1243 1113 1244 // rollback for errors: 1114 ImportStack stack( locInfo, reader.m_mapDisks,pProgress);1245 ImportStack stack(pTask->locInfo, reader.m_mapDisks, pTask->pProgress); 1115 1246 1116 1247 // clear the list of imported machines, if any … … 1120 1251 { 1121 1252 // if a manifest file exists, verify the content; we then need all files which are referenced by the OVF & the OVF itself 1122 rc = manifestVerify( locInfo, reader,pProgress);1253 rc = manifestVerify(pTask->locInfo, reader, pTask->pProgress); 1123 1254 if (FAILED(rc)) DebugBreakThrow(rc); 1124 1255 … … 1255 1386 } 1256 1387 1388 HRESULT Appliance::importFSOVA(TaskOVF *pTask) 1389 { 1390 LogFlowFuncEnter(); 1391 LogFlowFunc(("Appliance %p\n", this)); 1392 1393 AutoCaller autoCaller(this); 1394 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1395 1396 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS); 1397 1398 int vrc = VINF_SUCCESS; 1399 char szOSTmpDir[RTPATH_MAX]; 1400 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir)); 1401 /* The template for the temporary directory created below */ 1402 char *pszTmpDir; 1403 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir); 1404 list< pair<Utf8Str, ULONG> > filesList; 1405 const char** paFiles = 0; 1406 1407 HRESULT rc = S_OK; 1408 try 1409 { 1410 /* Extract the path */ 1411 Utf8Str tmpPath = pTask->locInfo.strPath; 1412 /* Remove the ova extension */ 1413 tmpPath.stripExt(); 1414 tmpPath += ".ovf"; 1415 1416 /* We need a temporary directory which we can put the all disk images 1417 * in */ 1418 vrc = RTDirCreateTemp(pszTmpDir); 1419 if (RT_FAILURE(vrc)) 1420 DebugBreakThrow(setError(VBOX_E_FILE_ERROR, 1421 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc)); 1422 1423 /* Provide a OVF file (haven't to exist) so the import routine can 1424 * figure out where the disk images/manifest file are located. */ 1425 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str())); 1426 /* Add the manifest file to the list of files to extract, but only if 1427 one is in the archive. */ 1428 Utf8Str strManifestFile = manifestFileName(strTmpOvf); 1429 vrc = RTTarQueryFileExists(pTask->locInfo.strPath.c_str(), RTPathFilename(strManifestFile.c_str())); 1430 if (RT_SUCCESS(vrc)) 1431 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile.c_str(), 1)); 1432 1433 ULONG ulWeight = m->ulWeightForXmlOperation; 1434 /* Add every disks of every virtual system to an internal list */ 1435 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; 1436 for (it = m->virtualSystemDescriptions.begin(); 1437 it != m->virtualSystemDescriptions.end(); 1438 ++it) 1439 { 1440 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it); 1441 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 1442 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH; 1443 for (itH = avsdeHDs.begin(); 1444 itH != avsdeHDs.end(); 1445 ++itH) 1446 { 1447 const Utf8Str &strTargetFile = (*itH)->strOvf; 1448 if (!strTargetFile.isEmpty()) 1449 { 1450 /* The temporary name of the target disk file */ 1451 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str())); 1452 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB)); 1453 ulWeight += (*itH)->ulSizeMB; 1454 } 1455 } 1456 } 1457 1458 /* Download all files */ 1459 paFiles = (const char**)RTMemAlloc(sizeof(char*) * filesList.size()); 1460 int i = 0; 1461 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1, ++i) 1462 paFiles[i] = RTPathFilename((*it1).first.c_str()); 1463 if (!pTask->pProgress.isNull()) 1464 pTask->pProgress->SetNextOperation(BstrFmt(tr("Unpacking file '%s'"), RTPathFilename(pTask->locInfo.strPath.c_str())), ulWeight); 1465 vrc = RTTarExtractFiles(pTask->locInfo.strPath.c_str(), pszTmpDir, paFiles, filesList.size(), pTask->updateProgress, &pTask); 1466 if (RT_FAILURE(vrc)) 1467 throw setError(VBOX_E_FILE_ERROR, 1468 tr("Cannot unpack archive file '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc); 1469 1470 // if (!pTask->pProgress.isNull()) 1471 // pTask->pProgress->SetNextOperation(BstrFmt(tr("Importing appliance")), m->ulWeightForXmlOperation); 1472 1473 ComObjPtr<Progress> progress; 1474 /* Import the whole temporary OVF & the disk images */ 1475 LocationInfo li; 1476 li.strPath = strTmpOvf; 1477 rc = importImpl(li, progress); 1478 if (FAILED(rc)) DebugBreakThrow(rc); 1479 1480 /* Unlock the appliance for the fs import thread */ 1481 appLock.release(); 1482 /* Wait until the import is done, but report the progress back to the 1483 caller */ 1484 ComPtr<IProgress> progressInt(progress); 1485 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */ 1486 1487 /* Again lock the appliance for the next steps */ 1488 appLock.acquire(); 1489 } 1490 catch(HRESULT aRC) 1491 { 1492 rc = aRC; 1493 } 1494 /* Delete the temporary files list */ 1495 if (paFiles) 1496 RTMemFree(paFiles); 1497 /* Delete all files which where temporary created */ 1498 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1) 1499 { 1500 const char *pszFilePath = (*it1).first.c_str(); 1501 if (RTPathExists(pszFilePath)) 1502 { 1503 vrc = RTFileDelete(pszFilePath); 1504 if (RT_FAILURE(vrc)) 1505 rc = setError(VBOX_E_FILE_ERROR, 1506 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc); 1507 } 1508 } 1509 /* Delete the temporary directory */ 1510 if (RTPathExists(pszTmpDir)) 1511 { 1512 vrc = RTDirRemove(pszTmpDir); 1513 if (RT_FAILURE(vrc)) 1514 rc = setError(VBOX_E_FILE_ERROR, 1515 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc); 1516 } 1517 if (pszTmpDir) 1518 RTStrFree(pszTmpDir); 1519 1520 LogFlowFunc(("rc=%Rhrc\n", rc)); 1521 LogFlowFuncLeave(); 1522 1523 return rc; 1524 } 1525 1257 1526 /** 1258 1527 * Imports one disk image. This is common code shared between … … 1365 1634 1366 1635 /* Advance to the next operation */ 1367 stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),1636 stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), RTPathFilename(strSrcFilePath.c_str())), 1368 1637 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally); 1369 1638 } … … 2163 2432 if (RT_FAILURE(vrc)) 2164 2433 DebugBreakThrow(setError(VBOX_E_FILE_ERROR, 2165 tr("Cannot create temporary directory '%s' "), pszTmpDir));2434 tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc)); 2166 2435 2167 2436 /* Add every disks of every virtual system to an internal list */ -
trunk/src/VBox/Main/include/ApplianceImpl.h
r30881 r31676 122 122 123 123 void disksWeight(); 124 struct LocationInfo; 124 125 enum SetUpProgressMode { ImportFileWithManifest, ImportFileNoManifest, ImportS3, WriteFile, WriteS3 }; 125 HRESULT setUpProgress(ComObjPtr<Progress> &pProgress, 126 HRESULT setUpProgress(const LocationInfo &locInfo, 127 ComObjPtr<Progress> &pProgress, 126 128 const Bstr &bstrDescription, 127 129 SetUpProgressMode mode); 128 130 129 struct LocationInfo;130 131 void parseURI(Utf8Str strUri, LocationInfo &locInfo) const; 131 132 void parseBucket(Utf8Str &aPath, Utf8Str &aBucket); 132 Utf8Str manifestFileName( Utf8StraPath) const;133 Utf8Str manifestFileName(const Utf8Str& aPath) const; 133 134 134 135 HRESULT readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress); … … 137 138 static DECLCALLBACK(int) taskThreadImportOrExport(RTTHREAD aThread, void *pvUser); 138 139 139 HRESULT readFS(const LocationInfo &locInfo); 140 HRESULT readFS(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress); 141 HRESULT readFSOVF(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress); 142 HRESULT readFSOVA(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress); 140 143 HRESULT readS3(TaskOVF *pTask); 141 144 … … 149 152 HRESULT manifestVerify(const LocationInfo &locInfo, const ovf::OVFReader &reader, ComObjPtr<Progress> &pProgress); 150 153 151 HRESULT importFS(const LocationInfo &locInfo, ComObjPtr<Progress> &aProgress); 154 HRESULT importFS(TaskOVF *pTask); 155 HRESULT importFSOVF(TaskOVF *pTask); 156 HRESULT importFSOVA(TaskOVF *pTask); 152 157 153 158 struct ImportStack; … … 175 180 XMLStack &stack); 176 181 177 HRESULT writeFS(const LocationInfo &locInfo, const OVFFormat enFormat, ComObjPtr<Progress> &pProgress); 182 HRESULT writeFS(TaskOVF *pTask); 183 HRESULT writeFSOVF(TaskOVF *pTask); 184 HRESULT writeFSOVA(TaskOVF *pTask); 178 185 HRESULT writeS3(TaskOVF *pTask); 179 186 -
trunk/src/VBox/Main/include/ApplianceImplPrivate.h
r31008 r31676 72 72 73 73 std::list<Utf8Str> llWarnings; 74 75 Utf8Str strManifestFile; // on import, contains path of manifest file if it exists76 74 77 75 ULONG ulWeightForXmlOperation;
Note:
See TracChangeset
for help on using the changeset viewer.