VirtualBox

Changeset 33289 in vbox


Ignore:
Timestamp:
Oct 21, 2010 10:00:15 AM (14 years ago)
Author:
vboxsync
Message:

Runtime;Main-OVF-Import: added online creation of SHA1 sums; preread/calc is done in a second worker thread; reading is cached; directly read out of an ova file; started to make reading fully streaming aware

Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/manifest.h

    r33057 r33289  
    4444{
    4545    /** The filename. */
    46     char *pszTestFile;
     46    const char *pszTestFile;
    4747    /** The SHA1 digest of the file. */
    48     char *pszTestDigest;
     48    const char *pszTestDigest;
    4949} RTMANIFESTTEST;
    5050/** Pointer to the input structure. */
     
    106106
    107107/**
     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 */
     121RTR3DECL(int) RTManifestVerifyFilesBuf(void *pvBuf, size_t cbSize, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed);
     122
     123/**
    108124 * Creates a manifest file in memory for a set of files. The manifest file
    109125 * contains SHA1 sums of every provided file and could be used to verify the
     
    114130 * @param   ppvBuf               Pointer to resulting memory buffer.
    115131 * @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.
    119134 */
    120 RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, const char * const *papszFileNames, const char * const *papszFileDigests, size_t cFiles);
     135RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, PRTMANIFESTTEST paFiles, size_t cFiles);
    121136
    122137/** @} */
  • trunk/include/iprt/tar.h

    r32751 r33289  
    6464 * @param   fMode          Open flags, i.e a combination of the RTFILE_O_* defines.
    6565 *                         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 */
     73RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char* pszTarname, uint32_t fMode, bool fStream);
    6874
    6975/**
     
    380386RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser);
    381387
     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 */
     400RTR3DECL(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 */
     409RTR3DECL(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 */
     423RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **ppszFilename, uint32_t fOpen);
     424
     425
    382426/** @} */
    383427
  • trunk/src/VBox/Main/ApplianceImpl.cpp

    r33238 r33289  
    1919#include <iprt/path.h>
    2020#include <iprt/cpp/utils.h>
    21 #include <iprt/tar.h>
    2221
    2322#include <VBox/com/array.h>
     
    633632
    634633/**
     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 */
     642HRESULT 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/**
    635743 * Called from the import and export background threads to synchronize the second
    636744 * background disk thread's progress object with the current progress object so
     
    746854}
    747855
    748 /**
    749  * Called from Appliance::importImpl() and Appliance::writeImpl() to set up a
    750  * progress object with the proper weights and maximum progress values.
    751  *
    752  * @param pProgress
    753  * @param bstrDescription
    754  * @param mode
    755  * @return
    756  */
    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 setup
    776                   + m->cDisks;      // plus one per disk
    777     if (m->ulTotalDisksMB)
    778     {
    779         m->ulWeightForXmlOperation = (ULONG)((double)m->ulTotalDisksMB * 1 / 100);    // use 1% of the progress for the XML
    780         ulTotalOperationsWeight = m->ulTotalDisksMB + m->ulWeightForXmlOperation;
    781     }
    782     else
    783     {
    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 packing
    797                 ++cOperations;
    798 
    799                 // assume that packing the files into the archive has the same weight than creating all files in the ovf exporting step
    800                 ulTotalOperationsWeight += m->ulTotalDisksMB;
    801             }
    802             break;
    803         }
    804         case ImportFileWithManifest:
    805         {
    806             ++cOperations;          // another one for creating the manifest
    807 
    808             // assume that creating the manifest will take 10% of the time it takes to export the disks
    809             m->ulWeightForManifestOperation = m->ulTotalDisksMB / 10;
    810             ulTotalOperationsWeight += m->ulWeightForManifestOperation;
    811             if (fOVA)
    812             {
    813                 // Another operation for packing
    814                 ++cOperations;
    815 
    816                 // assume that packing the files into the archive has the same weight than creating all files in the ovf exporting step
    817                 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 disks
    824             if (m->fManifest)
    825             {
    826                 ++cOperations;          // another one for creating the manifest
    827 
    828                 m->ulWeightForManifestOperation = (ULONG)((double)m->ulTotalDisksMB * .1 / 100);    // use .5% of the progress for the manifest
    829                 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 import
    836             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 import
    842             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 init
    847             ulTotalOperationsWeight += ulInitWeight;
    848             break;
    849         }
    850         case WriteS3:
    851         {
    852             cOperations += 1 + 1;     // another one for the mf & another one for temporary creation
    853 
    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             else
    860             {
    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 
    884856void Appliance::parseURI(Utf8Str strUri, LocationInfo &locInfo) const
    885857{
     
    947919}
    948920
    949 Utf8Str Appliance::manifestFileName(const Utf8Str& aPath) const
    950 {
    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 
    964921/**
    965922 *
     
    1005962        case TaskOVF::Read:
    1006963            if (task->locInfo.storageType == VFSType_File)
    1007                 taskrc = pAppliance->readFS(task->locInfo, task->pProgress);
     964                taskrc = pAppliance->readFS(task.get());
    1008965            else if (task->locInfo.storageType == VFSType_S3)
    1009966                taskrc = pAppliance->readS3(task.get());
  • trunk/src/VBox/Main/ApplianceImplExport.cpp

    r33232 r33289  
    613613////////////////////////////////////////////////////////////////////////////////
    614614
     615/*******************************************************************************
     616 * Export stuff
     617 ******************************************************************************/
     618
    615619/**
    616620 * Implementation for writing out the OVF to disk. This starts a new thread which will call
     
    636640    try
    637641    {
    638         rc = setUpProgress(aLocInfo,
    639                            aProgress,
     642        rc = setUpProgress(aProgress,
    640643                           BstrFmt(tr("Export appliance '%s'"), aLocInfo.strPath.c_str()),
    641644                           (aLocInfo.storageType == VFSType_File) ? WriteFile : WriteS3);
     
    16581661
    16591662    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);
    16611664    if (RT_FAILURE(vrc))
    16621665        return setError(VBOX_E_FILE_ERROR,
     
    17191722    HRESULT rc = S_OK;
    17201723
    1721     typedef pair<Utf8Str, Utf8Str> STRPAIR;
    17221724    list<STRPAIR> fileList;
    17231725    try
     
    18371839        {
    18381840            // Create & write the manifest file
    1839             Utf8Str strMfFilePath = manifestFileName(pTask->locInfo.strPath.c_str());
     1841            Utf8Str strMfFilePath = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf");
    18401842            Utf8Str strMfFileName = Utf8Str(strMfFilePath)
    18411843                .stripPath();
    18421844            pTask->pProgress->SetNextOperation(BstrFmt(tr("Creating manifest file '%s'"), strMfFileName.c_str()).raw(),
    18431845                                               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());
    18461847            size_t i = 0;
    18471848            list<STRPAIR>::const_iterator it1;
     
    18501851                 ++it1, ++i)
    18511852            {
    1852                 ppManifestFiles[i] = (*it1).first.c_str();
    1853                 ppManifestDigests[i] = (*it1).second.c_str();
     1853                paManifestFiles[i].pszTestFile  = (*it1).first.c_str();
     1854                paManifestFiles[i].pszTestDigest = (*it1).second.c_str();
    18541855            }
    18551856            void *pvBuf;
    18561857            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);
    18601860            if (RT_FAILURE(vrc))
    18611861                throw setError(VBOX_E_FILE_ERROR,
     
    19751975        if (m->fManifest)
    19761976        {
    1977             Utf8Str strMfFile = manifestFileName(strTmpOvf);
     1977            Utf8Str strMfFile = Utf8Str(strTmpOvf).stripExt().append(".mf");
    19781978            filesList.push_back(pair<Utf8Str, ULONG>(strMfFile , m->ulWeightForXmlOperation)); /* Use 1% of the total for the manifest file upload */
    19791979        }
  • trunk/src/VBox/Main/ApplianceImplIO.cpp

    r33090 r33289  
    3131#include <iprt/semaphore.h>
    3232#include <iprt/stream.h>
     33#include <iprt/circbuf.h>
    3334#include <VBox/VBoxHDD.h>
    3435
     
    5758    /** Completion callback. */
    5859    PFNVDCOMPLETED pfnCompleted;
     60    /** Our own storage handle. */
     61    PSHA1STORAGE pSha1Storage;
    5962    /** Storage handle for the next callback in chain. */
    6063    void *pvStorage;
     
    8386    /** SHA1 calculation context. */
    8487    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;
    8594} SHA1STORAGEINTERNAL, *PSHA1STORAGEINTERNAL;
    8695
     
    92101#define STATUS_CALC UINT32_C(1)
    93102#define STATUS_END  UINT32_C(3)
     103#define STATUS_READ UINT32_C(4)
    94104
    95105/* Enable for getting some flow history. */
     
    227237    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
    228238
    229     DEBUG_PRINT_FLOW();
     239//    DEBUG_PRINT_FLOW();
    230240
    231241    PRTFILESTORAGEINTERNAL pInt = (PRTFILESTORAGEINTERNAL)pvStorage;
     
    257267    AssertPtrReturn(ppInt, VERR_INVALID_POINTER);
    258268    AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
     269//    AssertReturn(!(fOpen & RTFILE_O_READWRITE), VERR_INVALID_PARAMETER);
    259270
    260271    RTTAR tar = (RTTAR)pvUser;
     
    268279    pInt->pfnCompleted = pfnCompleted;
    269280
    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);
    271323
    272324    if (RT_FAILURE(rc))
     
    425477        /* What should we do next? */
    426478        uint32_t u32Status = ASMAtomicReadU32(&pInt->u32Status);
     479//        RTPrintf("status: %d\n", u32Status);
    427480        switch (u32Status)
    428481        {
     
    442495                /* Reset the thread status and signal the main thread that we
    443496                   are finished. */
    444                 ASMAtomicWriteU32(&pInt->u32Status, STATUS_WAIT);
     497                ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WAIT, STATUS_CALC);
    445498                rc = RTSemEventSignal(pInt->calcFinishedEvent);
    446499                break;
     
    452505                break;
    453506            }
     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            }
    454576        }
    455577    }
     
    470592    {
    471593//        RTPrintf(" wait\n");
    472         if (ASMAtomicReadU32(&pInt->u32Status) != STATUS_CALC)
     594        if (!(   ASMAtomicReadU32(&pInt->u32Status) == STATUS_CALC
     595              || ASMAtomicReadU32(&pInt->u32Status) == STATUS_READ))
    473596            break;
    474597        rc = RTSemEventWait(pInt->calcFinishedEvent, 100);
     
    523646    AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
    524647    AssertPtrReturn(ppInt, VERR_INVALID_POINTER);
     648    AssertReturn((fOpen & RTFILE_O_READWRITE) != RTFILE_O_READWRITE, VERR_INVALID_PARAMETER); /* No read/write allowed */
    525649
    526650    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
     
    540664    {
    541665        pInt->pfnCompleted = pfnCompleted;
    542         /* For caching reasons and to be able to calculate the sha1 sum of the
    543            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)
    547671        {
    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;
    550676        }
    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)
    557678        {
    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            }
    560698        }
    561699
    562         if (pSha1Storage->fCreateDigest)
     700        if (   fOpen & RTFILE_O_READ
     701            || pSha1Storage->fCreateDigest)
    563702        {
    564             /* Create a sha1 context the sha1 worker thread will work with. */
    565             RTSha1Init(&pInt->ctx);
    566703            /* Create an event semaphore to indicate a state change for the sha1
    567704               worker thread. */
     
    579716                break;
    580717        }
     718
     719        if (pSha1Storage->fCreateDigest)
     720            /* Create a sha1 context the sha1 worker thread will work with. */
     721            RTSha1Init(&pInt->ctx);
     722
    581723        /* Open the file. */
    582724        rc = pCallbacks->pfnOpen(pIO->pvUser, pszLocation,
     
    588730    if (RT_FAILURE(rc))
    589731    {
    590         if (pSha1Storage->fCreateDigest)
     732        if (pInt->pMfThread)
    591733        {
    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);
    601736        }
     737        if (pInt->calcFinishedEvent)
     738            RTSemEventDestroy(pInt->calcFinishedEvent);
     739        if (pInt->newStatusEvent)
     740            RTSemEventDestroy(pInt->newStatusEvent);
     741        if (pInt->pCircBuf)
     742            RTCircBufDestroy(pInt->pCircBuf);
    602743        if (pInt->pvZeroBuf)
    603744            RTMemFree(pInt->pvZeroBuf);
     
    634775        rc = sha1FlushCurBuf(pIO, pCallbacks, pInt, pSha1Storage->fCreateDigest);
    635776
    636     if (pSha1Storage->fCreateDigest)
    637     {
     777    if (pInt->pMfThread)
    638778        /* Signal the worker thread to end himself */
    639779        rc = sha1SignalManifestThread(pInt, STATUS_END);
     780
     781    if (pSha1Storage->fCreateDigest)
     782    {
    640783        /* Finally calculate & format the SHA1 sum */
    641784        unsigned char auchDig[RTSHA1_HASH_SIZE];
     
    650793            RTStrFree(pszDigest);
    651794        }
     795    }
     796
     797    if (pInt->pMfThread)
    652798        /* Worker thread stopped? */
    653799        rc = RTThreadWait(pInt->pMfThread, RT_INDEFINITE_WAIT, 0);
    654     }
     800
    655801    /* Close the file */
    656802    rc = pCallbacks->pfnClose(pIO->pvUser, pInt->pvStorage);
    657803
     804//    RTPrintf("%lu %lu\n", pInt->calls, pInt->waits);
     805
    658806    /* 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);
    666813    if (pInt->pvZeroBuf)
    667814        RTMemFree(pInt->pvZeroBuf);
     
    760907
    761908    *pcbSize = RT_MAX(pInt->cbCurAll, cbSize);
     909
    762910    return VINF_SUCCESS;
    763911}
     
    8701018    PSHA1STORAGEINTERNAL pInt = (PSHA1STORAGEINTERNAL)pvStorage;
    8711019
    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;
    8731087}
    8741088
     
    9751189}
    9761190
     1191int 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
    9771249int Sha1WriteBuf(const char *pcszFilename, void *pvBuf, size_t cbSize, PVDINTERFACEIO pCallbacks, void *pvUser)
    9781250{
    9791251    /* 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);
    9811255
    9821256    void *pvStorage;
  • trunk/src/VBox/Main/ApplianceImplImport.cpp

    r33238 r33289  
    2626#include <iprt/stream.h>
    2727
     28#include <VBox/VBoxHDD.h>
    2829#include <VBox/com/array.h>
    2930
     
    3334#include "ProgressImpl.h"
    3435#include "MachineImpl.h"
     36#include "MediumImpl.h"
     37#include "MediumFormatImpl.h"
     38#include "SystemPropertiesImpl.h"
    3539
    3640#include "AutoCaller.h"
     
    7781    }
    7882
    79     // see if we can handle this file; for now we insist it has an ".ovf" extension
     83    // see if we can handle this file; for now we insist it has an ovf/ova extension
    8084    Utf8Str strPath (path);
    8185    if (!(   strPath.endsWith(".ovf", Utf8Str::CaseInsensitive)
     
    180184                               nameVBox);
    181185
     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
    182195            /* VM Product */
    183196            if (!vsysThis.strProduct.isEmpty())
     
    236249            /* CPU count */
    237250            ULONG cpuCountVBox = vsysThis.cCPUs;
    238             /* Check for the constrains */
     251            /* Check for the constraints */
    239252            if (cpuCountVBox > SchemaDefs::MaxCPUCount)
    240253            {
     
    496509                            strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
    497510
     511                        Utf8Str strTargetPath = Utf8Str(strMachineFolder)
     512                            .append(RTPATH_DELIMITER)
     513                            .append(di.strHref);;
     514                        searchUniqueDiskImageFilePath(strTargetPath);
     515
    498516                        /* find the description for the hard disk controller
    499517                         * that has the same ID as hd.idController */
     
    512530                                           hd.strDiskId,
    513531                                           di.strHref,
    514                                            strFilename,
     532                                           strTargetPath,
    515533                                           di.ulSuggestedSizeMB,
    516534                                           strExtraConfig);
     
    586604//
    587605////////////////////////////////////////////////////////////////////////////////
     606
     607
     608/*******************************************************************************
     609 * Read stuff
     610 ******************************************************************************/
    588611
    589612/**
     
    655678 * @return
    656679 */
    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 */)
     680HRESULT Appliance::readFS(TaskOVF *pTask)
    666681{
    667682    LogFlowFuncEnter();
     
    675690    HRESULT rc = S_OK;
    676691
     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
     703HRESULT 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
     751HRESULT 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
     808HRESULT 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;
    677817    try
    678818    {
    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);
    684823        if (RT_FAILURE(vrc))
    685824            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);
    690831    }
    691832    catch (iprt::Error &x)      // includes all XML exceptions
     
    699840    }
    700841
    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);
    767845
    768846    LogFlowFunc(("rc=%Rhrc\n", rc));
     
    899977    return rc;
    900978}
     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 */
     1000HRESULT 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 */
     1046HRESULT 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
     1109HRESULT 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
     1178HRESULT 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 */
     1291HRESULT 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
     1476HRESULT 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
     1493HRESULT 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
     1511HRESULT 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
    9011544
    9021545/**
     
    9961639
    9971640/**
    998  * Implementation for importing OVF data into VirtualBox. This starts a new thread which will call
    999  * Appliance::taskThreadImportOrExport().
    1000  *
    1001  * This creates one or more new machines according to the VirtualSystemScription instances created by
    1002  * 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 aLocInfo
    1010  * @param aProgress
    1011  * @return
    1012  */
    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     else
    1027          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) const
    1048 {
    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     else
    1057     {
    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, verifies
    1066  * that the relevant files (the OVF XML and the disks referenced by it, as
    1067  * represented by the VirtualSystemDescription instances contained in this appliance)
    1068  * match it. Requires a previous read() and interpret().
    1069  *
    1070  * @param locInfo
    1071  * @param reader
    1072  * @return
    1073  */
    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 originally
    1086 
    1087         list<Utf8Str> filesList;
    1088         Utf8Str strSrcDir(locInfo.strPath);
    1089         strSrcDir.stripFilename();
    1090         // add every disks of every virtual system to an internal list
    1091         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 list
    1105                 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 list
    1113         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 time
    1130         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 up
    1148         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 the
    1161  * 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(), which
    1168  *    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(), which
    1171  *    called Appliance::importS3(), which called Appliance::importImpl(), which then called this again.
    1172  *
    1173  * @param pTask
    1174  * @return
    1175  */
    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     else
    1181         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-consuming
    1195     // disk imports; also the below method calls do all kinds of locking which conflicts with
    1196     // the appliance object lock
    1197     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 started
    1207     // if pReader != NULL
    1208 
    1209     // rollback for errors:
    1210     ImportStack stack(pTask->locInfo, reader.m_mapDisks, pTask->pProgress);
    1211 
    1212     // clear the list of imported machines, if any
    1213     m->llGuidsMachinesCreated.clear();
    1214 
    1215     try
    1216     {
    1217         // if a manifest file exists, verify the content; we then need all files which are referenced by the OVF & the OVF itself
    1218         rc = manifestVerify(pTask->locInfo, reader, pTask->pProgress);
    1219         if (FAILED(rc)) throw rc;
    1220 
    1221         // create a session for the machine + disks we manipulate below
    1222         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> element
    1241             //    in the <VirtualSystem>; then the VirtualSystemDescription::Data has a settings::MachineConfigFile
    1242             //    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 the
    1244             //    VirtualSystemDescriptionEntry and do import work
    1245 
    1246             // Even for the vbox:Machine case, there are a number of configuration items that will be taken from
    1247             // the OVF because otherwise the "override import parameters" mechanism in the GUI won't work.
    1248 
    1249             // VM name
    1250             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 can
    1257             // put the disk images in the same directory
    1258             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 that
    1264             stack.strMachineFolder = bstrMachineFilename;
    1265             stack.strMachineFolder.stripFilename();
    1266 
    1267             // guest OS type
    1268             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 count
    1276             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 requested
    1283             if (stack.cCPUs > 1)
    1284             {
    1285                 stack.fForceHWVirt = true;
    1286                 stack.fForceIOAPIC = true;
    1287             }
    1288 
    1289             // RAM
    1290             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_USB
    1297             // USB controller
    1298             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 #endif
    1303             // audio adapter
    1304             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 config
    1310             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 now
    1315             if (vsdescThis->m->pConfig)
    1316                 // vbox:Machine config
    1317                 importVBoxMachine(vsdescThis, pNewMachine, stack);
    1318             else
    1319                 // generic OVF config
    1320                 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 of
    1332         // machines and disks we've created
    1333 
    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 state
    1354     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     try
    1385     {
    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 images
    1393          * 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 can
    1400          * 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 if
    1403            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 the
    1458            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 /**
    15021641 * Imports one disk image. This is common code shared between
    15031642 *  --  importMachineGeneric() for the OVF case; in that case the information comes from
     
    15231662void Appliance::importOneDiskImage(const ovf::DiskImage &di,
    15241663                                   const Utf8Str &strTargetPath,
    1525                                    ComPtr<IMedium> &pTargetHD,
    1526                                    ImportStack &stack)
     1664                                   ComObjPtr<Medium> &pTargetHD,
     1665                                   ImportStack &stack,
     1666                                   PVDINTERFACEIO pCallbacks,
     1667                                   PSHA1STORAGE pStorage)
    15271668{
    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)
    15361712           )
    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());
    15451717        if (FAILED(rc)) throw rc;
    15461718
    1547         // subprogress object for hard disk
    1548         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 disk
    1562             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 capacity
    1568             rc = pTargetHD->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam());
    1569             if (FAILED(rc)) throw rc;
    1570 
    1571             // advance to the next operation
    1572             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 originally
     1719        // 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"));
    15741746        }
    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));
    16331767}
    16341768
     
    16491783                                     ComObjPtr<VirtualSystemDescription> &vsdescThis,
    16501784                                     ComPtr<IMachine> &pNewMachine,
    1651                                      ImportStack &stack)
     1785                                     ImportStack &stack,
     1786                                     PVDINTERFACEIO pCallbacks,
     1787                                     PSHA1STORAGE pStorage)
    16521788{
    16531789    HRESULT rc;
     
    21122248                const ovf::VirtualDisk &ovfVdisk = itVirtualDisk->second;
    21132249
    2114                 ComPtr<IMedium> pTargetHD;
     2250                ComObjPtr<Medium> pTargetHD;
    21152251                importOneDiskImage(ovfDiskImage,
    21162252                                   vsdeHD->strVboxCurrent,
    21172253                                   pTargetHD,
    2118                                    stack);
     2254                                   stack,
     2255                                   pCallbacks,
     2256                                   pStorage);
    21192257
    21202258                // now use the new uuid to attach the disk image to our new machine
     
    21972335void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThis,
    21982336                                  ComPtr<IMachine> &pReturnNewMachine,
    2199                                   ImportStack &stack)
     2337                                  ImportStack &stack,
     2338                                  PVDINTERFACEIO pCallbacks,
     2339                                  PSHA1STORAGE pStorage)
    22002340{
    22012341    Assert(vsdescThis->m->pConfig);
     
    23042444                     *
    23052445                     */
    2306                     ComPtr<IMedium> pTargetHD;
     2446                    ComObjPtr<Medium> pTargetHD;
    23072447                    importOneDiskImage(di,
    23082448                                       strTargetPath,
    23092449                                       pTargetHD,
    2310                                        stack);
     2450                                       stack,
     2451                                       pCallbacks,
     2452                                       pStorage);
    23112453
    23122454                    // ... and replace the old UUID in the machine config with the one of
     
    23652507}
    23662508
    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)
     2509void Appliance::importMachines(ImportStack &stack,
     2510                               PVDINTERFACEIO pCallbacks,
     2511                               PSHA1STORAGE pStorage)
    23752512{
    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 
    23932513    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)
    24052550            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;
    24292585        }
    24302586
    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() ...
    25562621}
    25572622
  • trunk/src/VBox/Main/include/ApplianceImpl.h

    r33238 r33289  
    2424#include "VirtualBoxBase.h"
    2525
     26/* Todo: This file needs massive cleanup. Split IAppliance in a public and
     27 * private classes. */
     28#include <iprt/tar.h>
     29
    2630/* VBox forward declarations */
    2731class Progress;
     
    114118    VirtualBox* const   mVirtualBox;
    115119
     120    struct ImportStack;
     121    struct TaskOVF;
     122    struct LocationInfo;
    116123    struct Data;            // opaque, defined in ApplianceImpl.cpp
    117124    Data *m;
    118125
     126    enum SetUpProgressMode { ImportFile, ImportS3, WriteFile, WriteS3 };
     127
     128    /*******************************************************************************
     129     * General stuff
     130     ******************************************************************************/
     131
    119132    bool isApplianceIdle();
    120 
    121133    HRESULT searchUniqueVMName(Utf8Str& aName) const;
    122134    HRESULT searchUniqueDiskImageFilePath(Utf8Str& aName) const;
     135    HRESULT setUpProgress(ComObjPtr<Progress> &pProgress,
     136                          const Bstr &bstrDescription,
     137                          SetUpProgressMode mode);
    123138    void waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis, ComPtr<IProgress> &pProgressAsync);
    124139    void addWarning(const char* aWarning, ...);
    125 
    126140    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 
    134141    void parseURI(Utf8Str strUri, LocationInfo &locInfo) const;
    135142    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     ******************************************************************************/
    138149
    139150    HRESULT readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress);
    140151
    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);
    147156    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);
    148172
    149173    void convertDiskAttachmentValues(const ovf::HardDiskController &hdc,
     
    153177                                     int32_t &lDevice);
    154178
    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;
    163179    void importOneDiskImage(const ovf::DiskImage &di,
    164180                            const Utf8Str &strTargetPath,
    165                             ComPtr<IMedium> &pTargetHD,
    166                             ImportStack &stack);
     181                            ComObjPtr<Medium> &pTargetHD,
     182                            ImportStack &stack,
     183                            PVDINTERFACEIO pCallbacks,
     184                            PSHA1STORAGE pStorage);
    167185    void importMachineGeneric(const ovf::VirtualSystem &vsysThis,
    168186                              ComObjPtr<VirtualSystemDescription> &vsdescThis,
    169187                              ComPtr<IMachine> &pNewMachine,
    170                               ImportStack &stack);
     188                              ImportStack &stack,
     189                              PVDINTERFACEIO pCallbacks,
     190                              PSHA1STORAGE pStorage);
    171191    void importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThis,
    172192                           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     ******************************************************************************/
    176203
    177204    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);
    178211
    179212    struct XMLStack;
     
    186219                                     XMLStack &stack);
    187220
    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);
    193221
    194222    friend class Machine;
  • trunk/src/VBox/Main/include/ApplianceImplPrivate.h

    r33238 r33289  
    2929////////////////////////////////////////////////////////////////////////////////
    3030
     31typedef std::pair<Utf8Str, Utf8Str> STRPAIR;
     32
    3133/* Describe a location for the import/export. The location could be a file on a
    3234 * local hard disk or a remote target based on the supported inet protocols. */
     
    153155    bool                            fForceHWVirt;       // if true, we force enabling hardware virtualization
    154156    bool                            fForceIOAPIC;       // if true, we force enabling the IOAPIC
    155     uint32_t                        ulMemorySizeMB;     // virtual machien RAM in megabytes
     157    uint32_t                        ulMemorySizeMB;     // virtual machine RAM in megabytes
    156158#ifdef VBOX_WITH_USB
    157159    bool                            fUSBEnabled;
     
    167169    // and will be cleaned up on errors
    168170    std::list<MyHardDiskAttachment> llHardDiskAttachments;      // disks that were attached
    169     std::list< ComPtr<IMedium> >    llHardDisksCreated;         // media that were created
     171    std::list<STRPAIR>              llSrcDisksDigest;           // Digests of the source disks
    170172
    171173    ImportStack(const LocationInfo &aLocInfo,
     
    225227PVDINTERFACEIO RTFileCreateInterface();
    226228PVDINTERFACEIO RTTarCreateInterface();
     229int Sha1ReadBuf(const char *pcszFilename, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, void *pvUser);
    227230int Sha1WriteBuf(const char *pcszFilename, void *pvBuf, size_t cbSize, PVDINTERFACEIO pCallbacks, void *pvUser);
    228231
  • trunk/src/VBox/Runtime/common/checksum/manifest.cpp

    r33057 r33289  
    6767typedef RTMANIFESTCALLBACKDATA* PRTMANIFESTCALLBACKDATA;
    6868
     69/*******************************************************************************
     70*   Private functions
     71*******************************************************************************/
     72
     73DECLINLINE(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
     82DECLINLINE(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}
    6990
    7091int rtSHAProgressCallback(unsigned uPercent, void *pvUser)
     
    7697}
    7798
     99/*******************************************************************************
     100*   Public functions
     101*******************************************************************************/
     102
    78103RTR3DECL(int) RTManifestVerify(const char *pszManifestFile, PRTMANIFESTTEST paTests, size_t cTests, size_t *piFailed)
    79104{
    80105    /* Validate input */
    81106    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
     150RTR3DECL(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
     198RTR3DECL(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
     266RTR3DECL(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);
    82271    AssertPtrReturn(paTests, VERR_INVALID_POINTER);
    83272    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;
    90276
    91277    PRTMANIFESTFILEENTRY paFiles = (PRTMANIFESTFILEENTRY)RTMemTmpAllocZ(sizeof(RTMANIFESTFILEENTRY) * cTests);
    92278    if (!paFiles)
    93     {
    94         RTStrmClose(pStream);
    95279        return VERR_NO_MEMORY;
    96     }
    97280
    98281    /* Fill our compare list */
     
    100283        paFiles[i].pTestPattern = &paTests[i];
    101284
     285    char *pcBuf = (char*)pvBuf;
     286    size_t cbRead = 0;
    102287    /* Parse the manifest file line by line */
    103     char szLine[1024];
    104288    for (;;)
    105289    {
    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;
    113304            continue;
     305        }
    114306
    115307        /** @todo r=bird:
     308         *  -# Better deal with this EOF line platform dependency
    116309         *  -# The SHA1 test should probably include a blank space check.
    117310         *  -# If there is a specific order to the elements in the string, it would be
     
    122315        /* Check for the digest algorithm */
    123316        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'))
    128321        {
    129322            /* Digest unsupported */
     
    133326
    134327        /* Try to find the filename */
    135         char *pszNameStart = strchr(szLine, '(');
     328        char *pszNameStart = rtManifestPosOfCharInBuf(pcBuf, cch, '(');
    136329        if (!pszNameStart)
    137330        {
     
    139332            break;
    140333        }
    141         char *pszNameEnd = strchr(szLine, ')');
     334        char *pszNameEnd = rtManifestPosOfCharInBuf(pcBuf, cch, ')');
    142335        if (!pszNameEnd)
    143336        {
     
    158351
    159352        /* Try to find the digest sum */
    160         char *pszDigestStart = strchr(szLine, '=');
     353        char *pszDigestStart = rtManifestPosOfCharInBuf(pcBuf, cch, '=') + 1;
    161354        if (!pszDigestStart)
    162355        {
     
    165358            break;
    166359        }
    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';
    168378
    169379        /* Check our file list against the extracted data */
     
    181391        }
    182392        RTMemTmpFree(pszName);
     393        RTMemTmpFree(pszDigest);
    183394        if (!fFound)
    184395        {
     
    187398            break;
    188399        }
    189     }
    190     RTStrmClose(pStream);
     400
     401        pcBuf += cch;
     402        cbRead += cch;
     403    }
    191404
    192405    if (   rc == VINF_SUCCESS
     
    229442}
    230443
    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)
     444RTR3DECL(int) RTManifestWriteFilesBuf(void **ppvBuf, size_t *pcbSize, PRTMANIFESTTEST paFiles, size_t cFiles)
    331445{
    332446    /* Validate input */
    333447    AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER);
    334448    AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
    335     AssertPtrReturn(papszFileNames, VERR_INVALID_POINTER);
    336     AssertPtrReturn(papszFileDigests, VERR_INVALID_POINTER);
     449    AssertPtrReturn(paFiles, VERR_INVALID_POINTER);
    337450    AssertReturn(cFiles > 0, VERR_INVALID_PARAMETER);
    338451
     
    342455    for (size_t i = 0; i < cFiles; ++i)
    343456    {
    344         size_t cbTmp = strlen(RTPathFilename(papszFileNames[i])) + strlen(papszFileDigests[i]) + 10;
     457        size_t cbTmp = strlen(RTPathFilename(paFiles[i].pszTestFile)) + strlen(paFiles[i].pszTestDigest) + 10;
    345458        cbMaxSize = RT_MAX(cbMaxSize, cbTmp);
    346459        cbSize += cbTmp;
     
    357470    for (size_t i = 0; i < cFiles; ++i)
    358471    {
    359         size_t cch = RTStrPrintf(pszTmp, cbMaxSize + 1, "SHA1 (%s)= %s\n", RTPathFilename(papszFileNames[i]), papszFileDigests[i]);
     472        size_t cch = RTStrPrintf(pszTmp, cbMaxSize + 1, "SHA1 (%s)= %s\n", RTPathFilename(paFiles[i].pszTestFile), paFiles[i].pszTestDigest);
    360473        memcpy(&((char*)pvBuf)[cbPos], pszTmp, cch);
    361474        cbPos += cch;
  • trunk/src/VBox/Runtime/common/misc/tar.cpp

    r33058 r33289  
    9292#endif
    9393
     94typedef struct RTTARFILEINTERNAL* PRTTARFILEINTERNAL;
    9495typedef struct RTTARINTERNAL
    9596{
     
    9899    bool            fFileOpenForWrite;
    99100    uint32_t        fOpenMode;
     101    bool            fStreamMode;
     102    PRTTARFILEINTERNAL pFileCache;
    100103} RTTARINTERNAL;
    101104typedef RTTARINTERNAL* PRTTARINTERNAL;
     
    275278
    276279    return rc;
     280}
     281
     282DECLINLINE(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
     296DECLINLINE(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
     308DECLINLINE(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    }
    277317}
    278318
     
    563603 ******************************************************************************/
    564604
    565 RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char* pszTarname, uint32_t fMode)
     605RTR3DECL(int) RTTarOpen(PRTTAR phTar, const char* pszTarname, uint32_t fMode, bool fStream)
    566606{
    567607    PRTTARINTERNAL pInt = (PRTTARINTERNAL)RTMemAllocZ(sizeof(RTTARINTERNAL));
     
    571611    pInt->u32Magic = RTTAR_MAGIC;
    572612    pInt->fOpenMode = fMode;
     613    pInt->fStreamMode = fStream && (fMode & RTFILE_O_READ);
    573614
    574615    int rc = VINF_SUCCESS;
     
    618659        rc = RTFileClose(pInt->hTarFile);
    619660
     661    /* Delete any remaining cached file headers. */
     662    if (pInt->pFileCache)
     663    {
     664        rtDeleteTarFileInternal(pInt->pFileCache);
     665        pInt->pFileCache = 0;
     666    }
     667
    620668    pInt->u32Magic = RTTAR_MAGIC_DEAD;
    621669
     
    634682    if (!pInt->hTarFile)
    635683        return VERR_INVALID_HANDLE;
     684
     685    if (pInt->fStreamMode)
     686        return VERR_INVALID_STATE;
    636687
    637688    if (fOpen & RTFILE_O_WRITE)
     
    643694    }
    644695
    645     PRTTARFILEINTERNAL pFileInt = (PRTTARFILEINTERNAL)RTMemAllocZ(sizeof(RTTARFILEINTERNAL));
     696    PRTTARFILEINTERNAL pFileInt = rtCreateTarFileInternal(pInt, pszFilename, fOpen);
    646697    if (!pFileInt)
    647698        return VERR_NO_MEMORY;
    648699
    649     pFileInt->u32Magic = RTTARFILE_MAGIC;
    650 
    651700    int rc = VINF_SUCCESS;
    652701    do
    653702    {
    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 
    662703        if (pFileInt->fOpenMode & RTFILE_O_WRITE)
    663704        {
     
    718759
    719760    /* 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)
    721778    {
    722779        pFileInt->pTar->fFileOpenForWrite = false;
     
    756813        while(0);
    757814    }
    758     /* Nothing special in readmode. */
    759815
    760816    /* Now cleanup and delete the handle */
    761     RTStrFree(pFileInt->pszFilename);
    762     pFileInt->u32Magic = RTTARFILE_MAGIC_DEAD;
    763     RTMemFree(pFileInt);
     817    rtDeleteTarFileInternal(pFileInt);
    764818
    765819    return rc;
     
    770824    PRTTARFILEINTERNAL pFileInt = hFile;
    771825    RTTARFILE_VALID_RETURN(pFileInt);
     826
     827    if (pFileInt->pTar->fStreamMode)
     828        return VERR_INVALID_STATE;
    772829
    773830    switch (uMethod)
     
    822879    RTTARFILE_VALID_RETURN(pFileInt);
    823880
     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);
    824890    size_t cbTmpRead = 0;
    825     int rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->uStart + 512 + uOffset, pvBuf, cbToRead, &cbTmpRead);
     891    int rc = RTFileReadAt(pFileInt->pTar->hTarFile, pFileInt->uStart + 512 + uOffset, pvBuf, cbToCopy, &cbTmpRead);
    826892    pFileInt->uCurrentPos = uOffset + cbTmpRead;
    827893    if (pcbRead)
     
    10211087    /* Open the tar file */
    10221088    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);
    10241090    if (RT_FAILURE(rc))
    10251091        return rc;
     
    10451111    /* Open the tar file */
    10461112    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);
    10481114    if (RT_FAILURE(rc))
    10491115        return rc;
     
    11521218    do
    11531219    {
    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);
    11551221        if (RT_FAILURE(rc))
    11561222            break;
     
    12151281    /* Open the tar file */
    12161282    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);
    12181284    if (RT_FAILURE(rc))
    12191285        return rc;
     
    12861352
    12871353    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);
    12891355    if (RT_FAILURE(rc))
    12901356        return rc;
     
    13161382}
    13171383
     1384/******************************************************************************
     1385 *   Streaming Functions                                                      *
     1386 ******************************************************************************/
     1387
     1388RTR3DECL(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
     1407RTR3DECL(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
     1442RTR3DECL(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.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette