Index: trunk/src/VBox/Main/ApplianceImpl.cpp
===================================================================
--- trunk/src/VBox/Main/ApplianceImpl.cpp	(revision 33059)
+++ trunk/src/VBox/Main/ApplianceImpl.cpp	(revision 33060)
@@ -678,5 +678,5 @@
             break;
         }
-        /* Check if the current operation have changed. It is also possible
+        /* Check if the current operation has changed. It is also possible
            that in the meantime more than one async operation was finished. So
            we have to loop as long as we reached the same operation count. */
@@ -843,19 +843,11 @@
         case WriteFile:
         {
-            // assume that creating the manifest will take 10% of the time it takes to export the disks
+            // assume that creating the manifest will take .1% of the time it takes to export the disks
             if (m->fManifest)
             {
                 ++cOperations;          // another one for creating the manifest
 
-                m->ulWeightForManifestOperation = m->ulTotalDisksMB / 10;
+                m->ulWeightForManifestOperation = (ULONG)((double)m->ulTotalDisksMB * .1 / 100);    // use .5% of the progress for the manifest
                 ulTotalOperationsWeight += m->ulWeightForManifestOperation;
-            }
-            if (fOVA)
-            {
-                // Another operation for packing
-                ++cOperations;
-
-                // assume that packing the files into the archive has the same weight than creating all files in the ovf exporting step
-                ulTotalOperationsWeight += m->ulTotalDisksMB;
             }
             break;
Index: trunk/src/VBox/Main/ApplianceImplExport.cpp
===================================================================
--- trunk/src/VBox/Main/ApplianceImplExport.cpp	(revision 33059)
+++ trunk/src/VBox/Main/ApplianceImplExport.cpp	(revision 33060)
@@ -1577,30 +1577,152 @@
 HRESULT Appliance::writeFS(TaskOVF *pTask)
 {
+    LogFlowFuncEnter();
+    LogFlowFunc(("ENTER appliance %p\n", this));
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    HRESULT rc = S_OK;
+
+    // Lock the media tree early to make sure nobody else tries to make changes
+    // to the tree. Also lock the IAppliance object for writing.
+    AutoMultiWriteLock2 multiLock(&mVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
+    // Additional protect the IAppliance object, cause we leave the lock
+    // when starting the disk export and we don't won't block other
+    // callers on this lengthy operations.
+    m->state = Data::ApplianceExporting;
+
     if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
-        return writeFSOVF(pTask);
+        rc = writeFSOVF(pTask, multiLock);
     else
-        return writeFSOVA(pTask);
+        rc = writeFSOVA(pTask, multiLock);
+
+    // reset the state so others can call methods again
+    m->state = Data::ApplianceIdle;
+
+    LogFlowFunc(("rc=%Rhrc\n", rc));
+    LogFlowFuncLeave();
+    return rc;
 }
 
-HRESULT Appliance::writeFSOVF(TaskOVF *pTask)
+HRESULT Appliance::writeFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock)
 {
     LogFlowFuncEnter();
-    LogFlowFunc(("ENTER appliance %p\n", this));
-
-    AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
     HRESULT rc = S_OK;
 
+    PVDINTERFACEIO pSha1Callbacks = 0;
+    PVDINTERFACEIO pRTFileCallbacks = 0;
+    do
+    {
+        pSha1Callbacks = Sha1CreateInterface();
+        if (!pSha1Callbacks)
+        {
+            rc = E_OUTOFMEMORY;
+            break;
+        }
+        pRTFileCallbacks = RTFileCreateInterface();
+        if (!pRTFileCallbacks)
+        {
+            rc = E_OUTOFMEMORY;
+            break;
+        }
+
+        SHA1STORAGE storage;
+        RT_ZERO(storage);
+        storage.fCreateDigest = m->fManifest;
+        VDINTERFACE VDInterfaceIO;
+        int vrc = VDInterfaceAdd(&VDInterfaceIO, "Appliance::IORTFile",
+                                 VDINTERFACETYPE_IO, pRTFileCallbacks,
+                                 0, &storage.pVDImageIfaces);
+        if (RT_FAILURE(vrc))
+        {
+            rc = E_FAIL;
+            break;
+        }
+        rc = writeFSImpl(pTask, writeLock, pSha1Callbacks, &storage);
+    }while(0);
+
+    /* Cleanup */
+    if (pSha1Callbacks)
+        RTMemFree(pSha1Callbacks);
+    if (pRTFileCallbacks)
+        RTMemFree(pRTFileCallbacks);
+
+    LogFlowFuncLeave();
+    return rc;
+}
+
+HRESULT Appliance::writeFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock)
+{
+    LogFlowFuncEnter();
+
+    RTTAR tar;
+    int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL);
+    if (RT_FAILURE(vrc))
+        return setError(VBOX_E_FILE_ERROR,
+                        tr("Could not create OVA file '%s' (%Rrc)"),
+                        pTask->locInfo.strPath.c_str(), vrc);
+
+    HRESULT rc = S_OK;
+
+    PVDINTERFACEIO pSha1Callbacks = 0;
+    PVDINTERFACEIO pRTTarCallbacks = 0;
+    do
+    {
+        pSha1Callbacks = Sha1CreateInterface();
+        if (!pSha1Callbacks)
+        {
+            rc = E_OUTOFMEMORY;
+            break;
+        }
+        pRTTarCallbacks = RTTarCreateInterface();
+        if (!pRTTarCallbacks)
+        {
+            rc = E_OUTOFMEMORY;
+            break;
+        }
+        VDINTERFACE VDInterfaceIO;
+        SHA1STORAGE storage;
+        RT_ZERO(storage);
+        storage.fCreateDigest = m->fManifest;
+        vrc = VDInterfaceAdd(&VDInterfaceIO, "Appliance::IORTTar",
+                             VDINTERFACETYPE_IO, pRTTarCallbacks,
+                             tar, &storage.pVDImageIfaces);
+        if (RT_FAILURE(vrc))
+        {
+            rc = E_FAIL;
+            break;
+        }
+        rc = writeFSImpl(pTask, writeLock, pSha1Callbacks, &storage);
+    }while(0);
+
+    RTTarClose(tar);
+
+    /* Cleanup */
+    if (pSha1Callbacks)
+        RTMemFree(pSha1Callbacks);
+    if (pRTTarCallbacks)
+        RTMemFree(pRTTarCallbacks);
+
+    /* Delete ova file on error */
+    if(FAILED(rc))
+        RTFileDelete(pTask->locInfo.strPath.c_str());
+
+    LogFlowFuncLeave();
+    return rc;
+}
+
+HRESULT Appliance::writeFSImpl(TaskOVF *pTask, AutoWriteLockBase& writeLock, PVDINTERFACEIO pCallbacks, PSHA1STORAGE pStorage)
+{
+    LogFlowFuncEnter();
+
+    HRESULT rc = S_OK;
+
+    typedef pair<Utf8Str, Utf8Str> STRPAIR;
+    list<STRPAIR> fileList;
     try
     {
-        // Lock the media tree early to make sure nobody else tries to make changes
-        // to the tree. Also lock the IAppliance object for writing.
-        AutoMultiWriteLock2 multiLock(&mVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
-        // Additional protect the IAppliance object, cause we leave the lock
-        // when starting the disk export and we don't won't block other
-        // callers on this lengthy operations.
-        m->state = Data::ApplianceExporting;
-
+        int vrc;
         // the XML stack contains two maps for disks and networks, which allows us to
         // a) have a list of unique disk names (to make sure the same disk name is only added once)
@@ -1612,17 +1734,32 @@
             xml::Document doc;
             // Now fully build a valid ovf document in memory
-            buildXML(multiLock, doc, stack, pTask->locInfo.strPath, pTask->enFormat);
-            // Write the XML
-            xml::XmlFileWriter writer(doc);
-            writer.write(pTask->locInfo.strPath.c_str(), false /*fSafe*/);
+            buildXML(writeLock, doc, stack, pTask->locInfo.strPath, pTask->enFormat);
+            // Create a memory buffer containing the XML. */
+            void *pvBuf;
+            size_t cbSize;
+            xml::XmlMemWriter writer;
+            writer.write(doc, &pvBuf, &cbSize);
+            /* Extract the path */
+            Utf8Str tmpPath = pTask->locInfo.strPath;
+            /* Remove the extension and add ovf. */
+            tmpPath.stripExt()
+                .append(".ovf");
+            /* Write the ovf file to disk. */
+            vrc = Sha1WriteBuf(tmpPath.c_str(), pvBuf, cbSize, pCallbacks, pStorage);
+            if (RT_FAILURE(vrc))
+                throw setError(VBOX_E_FILE_ERROR,
+                               tr("Could not create OVF file '%s' (%Rrc)"),
+                               tmpPath.c_str(), vrc);
+            fileList.push_back(STRPAIR(tmpPath, pStorage->strDigest));
         }
 
         // We need a proper format description
         ComObjPtr<MediumFormat> format;
-        { // Scope for the AutoReadLock
+        // Scope for the AutoReadLock
+        {
             SystemProperties *pSysProps = mVirtualBox->getSystemProperties();
             AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
             // We are always exporting to VMDK stream optimized for now
-            format = pSysProps->mediumFormat("VMDK");
+            format = pSysProps->mediumFormat("VMDKstream");
             if (format.isNull())
                 throw setError(VBOX_E_NOT_SUPPORTED,
@@ -1631,5 +1768,4 @@
 
         // Finally, write out the disks!
-        list<Utf8Str> diskList;
         map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
         for (itS = stack.mapDisks.begin();
@@ -1664,10 +1800,10 @@
             // target path needs to be composed from where the output OVF is
             Utf8Str strTargetFilePath(pTask->locInfo.strPath);
-            strTargetFilePath.stripFilename();
-            strTargetFilePath.append("/");
-            strTargetFilePath.append(strTargetFileNameOnly);
+            strTargetFilePath.stripFilename()
+                .append("/")
+                .append(strTargetFileNameOnly);
 
             // The exporting requests a lock on the media tree. So leave our lock temporary.
-            multiLock.release();
+            writeLock.release();
             try
             {
@@ -1676,11 +1812,12 @@
                 rc = pProgress2->init(mVirtualBox, static_cast<IAppliance*>(this), BstrFmt(tr("Creating medium '%s'"), strTargetFilePath.c_str()).raw(), TRUE);
                 if (FAILED(rc)) throw rc;
-                // create a flat copy of the source disk image
-                rc = pSourceDisk->exportFile(strTargetFilePath.c_str(), format, MediumVariant_VmdkStreamOptimized, NULL, NULL, pProgress2);
-                if (FAILED(rc)) throw rc;
 
                 // advance to the next operation
                 pTask->pProgress->SetNextOperation(BstrFmt(tr("Exporting to disk image '%s'"), RTPathFilename(strTargetFilePath.c_str())).raw(),
-                                                   pDiskEntry->ulSizeMB);     // operation's weight, as set up with the IProgress originally);
+                                                   pDiskEntry->ulSizeMB);     // operation's weight, as set up with the IProgress originally
+
+                // create a flat copy of the source disk image
+                rc = pSourceDisk->exportFile(strTargetFilePath.c_str(), format, MediumVariant_VmdkStreamOptimized, pCallbacks, pStorage, pProgress2);
+                if (FAILED(rc)) throw rc;
 
                 ComPtr<IProgress> pProgress3(pProgress2);
@@ -1690,4 +1827,5 @@
             catch (HRESULT rc3)
             {
+                writeLock.acquire();
                 // Todo: file deletion on error? If not, we can remove that whole try/catch block.
                 throw rc3;
@@ -1695,6 +1833,6 @@
             // Finished, lock again (so nobody mess around with the medium tree
             // in the meantime)
-            multiLock.acquire();
-            diskList.push_back(strTargetFilePath);
+            writeLock.acquire();
+            fileList.push_back(STRPAIR(strTargetFilePath, pStorage->strDigest));
         }
 
@@ -1702,23 +1840,36 @@
         {
             // Create & write the manifest file
-            Utf8Str strMfFile = manifestFileName(pTask->locInfo.strPath.c_str());
-            const char *pcszManifestFileOnly = RTPathFilename(strMfFile.c_str());
-            pTask->pProgress->SetNextOperation(BstrFmt(tr("Creating manifest file '%s'"), pcszManifestFileOnly).raw(),
+            Utf8Str strMfFilePath = manifestFileName(pTask->locInfo.strPath.c_str());
+            Utf8Str strMfFileName = Utf8Str(strMfFilePath)
+                .stripPath();
+            pTask->pProgress->SetNextOperation(BstrFmt(tr("Creating manifest file '%s'"), strMfFileName.c_str()).raw(),
                                                m->ulWeightForManifestOperation);     // operation's weight, as set up with the IProgress originally);
-
-            const char** ppManifestFiles = (const char**)RTMemAlloc(sizeof(char*)*diskList.size() + 1);
-            ppManifestFiles[0] = pTask->locInfo.strPath.c_str();
-            list<Utf8Str>::const_iterator it1;
-            size_t i = 1;
-            for (it1 = diskList.begin();
-                 it1 != diskList.end();
+            const char** ppManifestFiles = (const char**)RTMemAlloc(sizeof(char*) * fileList.size());
+            const char** ppManifestDigests = (const char**)RTMemAlloc(sizeof(char*) * fileList.size());
+            size_t i = 0;
+            list<STRPAIR>::const_iterator it1;
+            for (it1 = fileList.begin();
+                 it1 != fileList.end();
                  ++it1, ++i)
-                ppManifestFiles[i] = (*it1).c_str();
-            int vrc = RTManifestWriteFiles(strMfFile.c_str(), ppManifestFiles, diskList.size()+1, NULL, NULL);
+            {
+                ppManifestFiles[i] = (*it1).first.c_str();
+                ppManifestDigests[i] = (*it1).second.c_str();
+            }
+            void *pvBuf;
+            size_t cbSize;
+            vrc = RTManifestWriteFilesBuf(&pvBuf, &cbSize, ppManifestFiles, ppManifestDigests, fileList.size());
             RTMemFree(ppManifestFiles);
+            RTMemFree(ppManifestDigests);
             if (RT_FAILURE(vrc))
                 throw setError(VBOX_E_FILE_ERROR,
                                tr("Could not create manifest file '%s' (%Rrc)"),
-                               pcszManifestFileOnly, vrc);
+                               strMfFileName.c_str(), vrc);
+            /* Write the manifest file to disk. */
+            vrc = Sha1WriteBuf(strMfFilePath.c_str(), pvBuf, cbSize, pCallbacks, pStorage);
+            RTMemFree(pvBuf);
+            if (RT_FAILURE(vrc))
+                throw setError(VBOX_E_FILE_ERROR,
+                               tr("Could not create manifest file '%s' (%Rrc)"),
+                               strMfFilePath.c_str(), vrc);
         }
     }
@@ -1733,156 +1884,13 @@
     }
 
-    AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
-    // reset the state so others can call methods again
-    m->state = Data::ApplianceIdle;
-
-    LogFlowFunc(("rc=%Rhrc\n", rc));
-    LogFlowFuncLeave();
-
-    return rc;
-}
-
-HRESULT Appliance::writeFSOVA(TaskOVF *pTask)
-{
-    LogFlowFuncEnter();
-    LogFlowFunc(("Appliance %p\n", this));
-
-    AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
-    HRESULT rc = S_OK;
-
-    AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
-
-    int vrc = VINF_SUCCESS;
-
-    char szOSTmpDir[RTPATH_MAX];
-    RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
-    /* The template for the temporary directory created below */
-    char *pszTmpDir;
-    RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
-    list<Utf8Str> filesList;
-    const char** paFiles = 0;
-
-    try
-    {
-        /* Extract the path */
-        Utf8Str tmpPath = pTask->locInfo.strPath;
-        /* Remove the ova extension */
-        tmpPath.stripExt();
-        tmpPath += ".ovf";
-
-        /* We need a temporary directory which we can put the OVF file & all
-         * disk images in */
-        vrc = RTDirCreateTemp(pszTmpDir);
-        if (RT_FAILURE(vrc))
-            throw setError(VBOX_E_FILE_ERROR,
-                           tr("Cannot create temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
-
-        /* The temporary name of the target OVF file */
-        Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
-
-        /* Prepare the temporary writing of the OVF */
-        ComObjPtr<Progress> progress;
-        /* Create a temporary file based location info for the sub task */
-        LocationInfo li;
-        li.strPath = strTmpOvf;
-        rc = writeImpl(pTask->enFormat, li, progress);
-        if (FAILED(rc)) throw rc;
-
-        /* Unlock the appliance for the writing thread */
-        appLock.release();
-        /* Wait until the writing is done, but report the progress back to the
-           caller */
-        ComPtr<IProgress> progressInt(progress);
-        waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
-
-        /* Again lock the appliance for the next steps */
-        appLock.acquire();
-
-        vrc = RTPathExists(strTmpOvf.c_str()); /* Paranoid check */
-        if (RT_FAILURE(vrc))
-            throw setError(VBOX_E_FILE_ERROR,
-                           tr("Cannot find source file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);
-        ULONG ulWeight = m->ulWeightForXmlOperation;
-        /* Add the OVF file */
-        filesList.push_back(strTmpOvf); /* Use 1% of the total for the OVF file upload */
-        /* Add the manifest file */
-        if (m->fManifest)
-        {
-            Utf8Str strMfFile = manifestFileName(strTmpOvf);
-            filesList.push_back(strMfFile); /* Use 1% of the total for the manifest file upload */
-            ulWeight += m->ulWeightForXmlOperation;
-        }
-
-        /* Now add every disks of every virtual system */
-        list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
-        for (it = m->virtualSystemDescriptions.begin();
-             it != m->virtualSystemDescriptions.end();
-             ++it)
-        {
-            ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
-            std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
-            std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
-            for (itH = avsdeHDs.begin();
-                 itH != avsdeHDs.end();
-                 ++itH)
-            {
-                const Utf8Str &strTargetFileNameOnly = (*itH)->strOvf;
-                /* Target path needs to be composed from where the output OVF is */
-                Utf8Str strTargetFilePath(strTmpOvf);
-                strTargetFilePath.stripFilename();
-                strTargetFilePath.append("/");
-                strTargetFilePath.append(strTargetFileNameOnly);
-                vrc = RTPathExists(strTargetFilePath.c_str()); /* Paranoid check */
-                if (RT_FAILURE(vrc))
-                    throw setError(VBOX_E_FILE_ERROR,
-                                   tr("Cannot find source file '%s' (%Rrc)"), strTargetFilePath.c_str(), vrc);
-                filesList.push_back(strTargetFilePath);
-                ulWeight += (*itH)->ulSizeMB;
-            }
-        }
-        ulWeight = m->ulTotalDisksMB;
-        paFiles = (const char**)RTMemAlloc(sizeof(char*) * filesList.size());
-        int i = 0;
-        for (list<Utf8Str>::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1, ++i)
-            paFiles[i] = (*it1).c_str();
-        pTask->pProgress->SetNextOperation(BstrFmt(tr("Packing into '%s'"), RTPathFilename(pTask->locInfo.strPath.c_str())).raw(), ulWeight);
-        /* Create the tar file out of our file list. */
-        vrc = RTTarCreate(pTask->locInfo.strPath.c_str(), paFiles, filesList.size(), pTask->updateProgress, &pTask);
-        if (RT_FAILURE(vrc))
-            throw setError(VBOX_E_FILE_ERROR,
-                           tr("Cannot create OVA file '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);
-    }
-    catch(HRESULT aRC)
-    {
-        rc = aRC;
-    }
-
-    /* Delete the temporary files list */
-    if (paFiles)
-        RTMemFree(paFiles);
-    /* Delete all files which where temporary created */
-    for (list<Utf8Str>::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
-    {
-        const char *pszFilePath = (*it1).c_str();
-        if (RTPathExists(pszFilePath))
-        {
-            vrc = RTFileDelete(pszFilePath);
-            if (RT_FAILURE(vrc))
-                rc = setError(VBOX_E_FILE_ERROR,
-                              tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
-        }
-    }
-    /* Delete the temporary directory */
-    if (RTPathExists(pszTmpDir))
-    {
-        vrc = RTDirRemove(pszTmpDir);
-        if (RT_FAILURE(vrc))
-            rc = setError(VBOX_E_FILE_ERROR,
-                          tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
-    }
-    if (pszTmpDir)
-        RTStrFree(pszTmpDir);
+    /* Cleanup on error */
+    if (FAILED(rc))
+    {
+        list<STRPAIR>::const_iterator it1;
+        for (it1 = fileList.begin();
+             it1 != fileList.end();
+             ++it1)
+             pCallbacks->pfnDelete(pStorage, (*it1).first.c_str());
+    }
 
     LogFlowFunc(("rc=%Rhrc\n", rc));
Index: trunk/src/VBox/Main/ApplianceImplIO.cpp
===================================================================
--- trunk/src/VBox/Main/ApplianceImplIO.cpp	(revision 33060)
+++ trunk/src/VBox/Main/ApplianceImplIO.cpp	(revision 33060)
@@ -0,0 +1,1017 @@
+/* $Id$ */
+/** @file
+ *
+ * IO helper for IAppliance COM class implementations.
+ */
+
+/*
+ * Copyright (C) 2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/******************************************************************************
+ *   Header Files                                                             *
+ ******************************************************************************/
+
+#include "ProgressImpl.h"
+#include "ApplianceImpl.h"
+#include "ApplianceImplPrivate.h"
+
+#include <iprt/tar.h>
+#include <iprt/sha.h>
+#include <iprt/path.h>
+#include <iprt/asm.h>
+#include <iprt/semaphore.h>
+#include <iprt/stream.h>
+#include <VBox/VBoxHDD.h>
+
+#include <openssl/sha.h>
+
+/******************************************************************************
+ *   Structures and Typedefs                                                  *
+ ******************************************************************************/
+
+typedef struct RTFILESTORAGEINTERNAL
+{
+    /** File handle. */
+    RTFILE         file;
+    /** Completion callback. */
+    PFNVDCOMPLETED pfnCompleted;
+} RTFILESTORAGEINTERNAL, *PRTFILESTORAGEINTERNAL;
+
+typedef struct RTTARSTORAGEINTERNAL
+{
+    /** Tar handle. */
+    RTTARFILE      file;
+    /** Completion callback. */
+    PFNVDCOMPLETED pfnCompleted;
+} RTTARSTORAGEINTERNAL, *PRTTARSTORAGEINTERNAL;
+
+typedef struct SHA1STORAGEINTERNAL
+{
+    /** Completion callback. */
+    PFNVDCOMPLETED pfnCompleted;
+    /** Storage handle for the next callback in chain. */
+    void *pvStorage;
+    /** Memory buffer used for caching and SHA1 calculation. */
+    char *pcBuf;
+    /** Size of the memory buffer. */
+    size_t cbBuf;
+    /** Memory buffer for writing zeros. */
+    void *pvZeroBuf;
+    /** Size of the zero memory buffer. */
+    size_t cbZeroBuf;
+    /** Current position in the caching memory buffer. */
+    size_t cbCurBuf;
+    /** Current absolute position. */
+    uint64_t cbCurAll;
+    /** Current real position in the file. */
+    uint64_t cbCurFile;
+    /** Handle of the SHA1 worker thread. */
+    RTTHREAD pMfThread;
+    /** Status of the worker thread. */
+    volatile uint32_t u32Status;
+    /** Event for signaling a new status. */
+    RTSEMEVENT newStatusEvent;
+    /** Event for signaling a finished SHA1 calculation. */
+    RTSEMEVENT calcFinishedEvent;
+    /** SHA1 calculation context. */
+    SHA_CTX ctx;
+} SHA1STORAGEINTERNAL, *PSHA1STORAGEINTERNAL;
+
+/******************************************************************************
+ *   Defined Constants And Macros                                             *
+ ******************************************************************************/
+
+#define STATUS_WAIT UINT32_C(0)
+#define STATUS_CALC UINT32_C(1)
+#define STATUS_END  UINT32_C(3)
+
+/* Enable for getting some flow history. */
+#if 0
+# define DEBUG_PRINT_FLOW() RTPrintf("%s\n", __FUNCTION__)
+#else
+# define DEBUG_PRINT_FLOW() do {} while(0)
+#endif
+
+/******************************************************************************
+ *   Internal Functions                                                       *
+ ******************************************************************************/
+
+/******************************************************************************
+ *   Internal: RTFile interface
+ ******************************************************************************/
+
+static int rtFileOpenCallback(void * /* pvUser */, const char *pszLocation, uint32_t fOpen,
+                              PFNVDCOMPLETED pfnCompleted, void **ppInt)
+{
+    /* Validate input. */
+    AssertPtrReturn(ppInt, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
+
+    DEBUG_PRINT_FLOW();
+
+    PRTFILESTORAGEINTERNAL pInt = (PRTFILESTORAGEINTERNAL)RTMemAllocZ(sizeof(RTFILESTORAGEINTERNAL));
+    if (!pInt)
+        return VERR_NO_MEMORY;
+
+    pInt->pfnCompleted = pfnCompleted;
+
+    int rc = RTFileOpen(&pInt->file, pszLocation, fOpen);
+
+    if (RT_FAILURE(rc))
+        RTMemFree(pInt);
+    else
+        *ppInt = pInt;
+
+    return rc;
+}
+
+static int rtFileCloseCallback(void * /* pvUser */, void *pvStorage)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PRTFILESTORAGEINTERNAL pInt = (PRTFILESTORAGEINTERNAL)pvStorage;
+
+    DEBUG_PRINT_FLOW();
+
+    int rc = RTFileClose(pInt->file);
+
+    /* Cleanup */
+    RTMemFree(pInt);
+
+    return rc;
+}
+
+static int rtFileDeleteCallback(void * /* pvUser */, const char *pcszFilename)
+{
+    DEBUG_PRINT_FLOW();
+
+    return RTFileDelete(pcszFilename);
+}
+
+static int rtFileMoveCallback(void * /* pvUser */, const char *pcszSrc, const char *pcszDst, unsigned fMove)
+{
+    DEBUG_PRINT_FLOW();
+
+    return RTFileMove(pcszSrc, pcszDst, fMove);
+}
+
+static int rtFileGetFreeSpaceCallback(void * /* pvUser */, const char *pcszFilename, int64_t *pcbFreeSpace)
+{
+    /* Validate input. */
+    AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
+    AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
+
+    DEBUG_PRINT_FLOW();
+
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int rtFileGetModificationTimeCallback(void * /* pvUser */, const char *pcszFilename, PRTTIMESPEC pModificationTime)
+{
+    /* Validate input. */
+    AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
+    AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
+
+    DEBUG_PRINT_FLOW();
+
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int rtFileGetSizeCallback(void * /* pvUser */, void *pvStorage, uint64_t *pcbSize)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PRTFILESTORAGEINTERNAL pInt = (PRTFILESTORAGEINTERNAL)pvStorage;
+
+//    DEBUG_PRINT_FLOW();
+
+    return RTFileGetSize(pInt->file, pcbSize);
+}
+
+static int rtFileSetSizeCallback(void * /* pvUser */, void *pvStorage, uint64_t cbSize)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PRTFILESTORAGEINTERNAL pInt = (PRTFILESTORAGEINTERNAL)pvStorage;
+
+    DEBUG_PRINT_FLOW();
+
+    return RTFileSetSize(pInt->file, cbSize);
+}
+
+static int rtFileWriteSyncCallback(void * /* pvUser */, void *pvStorage, uint64_t uOffset,
+                                   const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PRTFILESTORAGEINTERNAL pInt = (PRTFILESTORAGEINTERNAL)pvStorage;
+
+    return RTFileWriteAt(pInt->file, uOffset, pvBuf, cbWrite, pcbWritten);
+}
+
+static int rtFileReadSyncCallback(void * /* pvUser */, void *pvStorage, uint64_t uOffset,
+                                  void *pvBuf, size_t cbRead, size_t *pcbRead)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    DEBUG_PRINT_FLOW();
+
+    PRTFILESTORAGEINTERNAL pInt = (PRTFILESTORAGEINTERNAL)pvStorage;
+
+    return RTFileReadAt(pInt->file, uOffset, pvBuf, cbRead, pcbRead);
+}
+
+static int rtFileFlushSyncCallback(void * /* pvUser */, void *pvStorage)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    DEBUG_PRINT_FLOW();
+
+    PRTFILESTORAGEINTERNAL pInt = (PRTFILESTORAGEINTERNAL)pvStorage;
+
+    return RTFileFlush(pInt->file);
+}
+
+/******************************************************************************
+ *   Internal: RTTar interface
+ ******************************************************************************/
+
+static int rtTarOpenCallback(void *pvUser, const char *pszLocation, uint32_t fOpen,
+                             PFNVDCOMPLETED pfnCompleted, void **ppInt)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(ppInt, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
+
+    RTTAR tar = (RTTAR)pvUser;
+
+    DEBUG_PRINT_FLOW();
+
+    PRTTARSTORAGEINTERNAL pInt = (PRTTARSTORAGEINTERNAL)RTMemAllocZ(sizeof(RTTARSTORAGEINTERNAL));
+    if (!pInt)
+        return VERR_NO_MEMORY;
+
+    pInt->pfnCompleted = pfnCompleted;
+
+    int rc = RTTarFileOpen(tar, &pInt->file, RTPathFilename(pszLocation), fOpen);
+
+    if (RT_FAILURE(rc))
+        RTMemFree(pInt);
+    else
+        *ppInt = pInt;
+
+    return rc;
+}
+
+static int rtTarCloseCallback(void *pvUser, void *pvStorage)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PRTTARSTORAGEINTERNAL pInt = (PRTTARSTORAGEINTERNAL)pvStorage;
+
+    DEBUG_PRINT_FLOW();
+
+    int rc = RTTarFileClose(pInt->file);
+
+    /* Cleanup */
+    RTMemFree(pInt);
+
+    return rc;
+}
+
+static int rtTarDeleteCallback(void *pvUser, const char *pcszFilename)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
+
+    DEBUG_PRINT_FLOW();
+
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int rtTarMoveCallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned /* fMove */)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pcszSrc, VERR_INVALID_POINTER);
+    AssertPtrReturn(pcszDst, VERR_INVALID_POINTER);
+
+    DEBUG_PRINT_FLOW();
+
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int rtTarGetFreeSpaceCallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
+    AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
+
+    DEBUG_PRINT_FLOW();
+
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int rtTarGetModificationTimeCallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
+    AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
+
+    DEBUG_PRINT_FLOW();
+
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int rtTarGetSizeCallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PRTTARSTORAGEINTERNAL pInt = (PRTTARSTORAGEINTERNAL)pvStorage;
+
+//    DEBUG_PRINT_FLOW();
+
+    return RTTarFileGetSize(pInt->file, pcbSize);
+}
+
+static int rtTarSetSizeCallback(void *pvUser, void *pvStorage, uint64_t cbSize)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PRTTARSTORAGEINTERNAL pInt = (PRTTARSTORAGEINTERNAL)pvStorage;
+
+    DEBUG_PRINT_FLOW();
+
+    return RTTarFileSetSize(pInt->file, cbSize);
+}
+
+static int rtTarWriteSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset,
+                                  const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PRTTARSTORAGEINTERNAL pInt = (PRTTARSTORAGEINTERNAL)pvStorage;
+
+    DEBUG_PRINT_FLOW();
+
+    return RTTarFileWriteAt(pInt->file, uOffset, pvBuf, cbWrite, pcbWritten);
+}
+
+static int rtTarReadSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset,
+                                 void *pvBuf, size_t cbRead, size_t *pcbRead)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PRTTARSTORAGEINTERNAL pInt = (PRTTARSTORAGEINTERNAL)pvStorage;
+
+    DEBUG_PRINT_FLOW();
+
+    return RTTarFileReadAt(pInt->file, uOffset, pvBuf, cbRead, pcbRead);
+}
+
+static int rtTarFlushSyncCallback(void *pvUser, void *pvStorage)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    DEBUG_PRINT_FLOW();
+
+    return VERR_NOT_IMPLEMENTED;
+}
+
+/******************************************************************************
+ *   Internal: Sha1 interface
+ ******************************************************************************/
+
+DECLCALLBACK(int) sha1CalcWorkerThread(RTTHREAD /* aThread */, void *pvUser)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+
+    PSHA1STORAGEINTERNAL pInt = (PSHA1STORAGEINTERNAL)pvUser;
+
+    int rc = VINF_SUCCESS;
+    bool fLoop = true;
+    while(fLoop)
+    {
+        /* What should we do next? */
+        uint32_t u32Status = ASMAtomicReadU32(&pInt->u32Status);
+        switch (u32Status)
+        {
+            case STATUS_WAIT:
+            {
+                /* Wait for new work. */
+                rc = RTSemEventWait(pInt->newStatusEvent, 100);
+                if (   RT_FAILURE(rc)
+                    && rc != VERR_TIMEOUT)
+                    fLoop = false;
+                break;
+            }
+            case STATUS_CALC:
+            {
+                /* Update the SHA1 context with the next data block. */
+                if(!SHA1_Update(&pInt->ctx, pInt->pcBuf, pInt->cbCurBuf))
+                {
+                    rc = VERR_INTERNAL_ERROR;
+                    fLoop = false;
+                }
+                /* Reset the thread status and signal the main thread that we
+                   are finished. */
+                ASMAtomicWriteU32(&pInt->u32Status, STATUS_WAIT);
+                rc = RTSemEventSignal(pInt->calcFinishedEvent);
+                break;
+            }
+            case STATUS_END:
+            {
+                /* End signaled */
+                fLoop = false;
+                break;
+            }
+        }
+    }
+    return rc;
+}
+
+DECLINLINE(int) sha1SignalManifestThread(PSHA1STORAGEINTERNAL pInt, uint32_t uStatus)
+{
+    ASMAtomicWriteU32(&pInt->u32Status, uStatus);
+    return RTSemEventSignal(pInt->newStatusEvent);
+}
+
+DECLINLINE(int) sha1WaitForManifestThreadFinished(PSHA1STORAGEINTERNAL pInt)
+{
+//    RTPrintf("start\n");
+    int rc = VINF_SUCCESS;
+    for(;;)
+    {
+//        RTPrintf(" wait\n");
+        if (ASMAtomicReadU32(&pInt->u32Status) != STATUS_CALC)
+            break;
+        rc = RTSemEventWait(pInt->calcFinishedEvent, 100);
+    }
+    if (rc == VERR_TIMEOUT)
+        rc = VINF_SUCCESS;
+    return rc;
+}
+
+DECLINLINE(int) sha1FlushCurBuf(PVDINTERFACE pIO, PVDINTERFACEIO pCallbacks, PSHA1STORAGEINTERNAL pInt, bool fCreateDigest)
+{
+    int rc = VINF_SUCCESS;
+    if (fCreateDigest)
+    {
+        /* Let the sha1 worker thread start immediately. */
+        rc = sha1SignalManifestThread(pInt, STATUS_CALC);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+    /* Write the buffer content to disk. */
+    size_t cbAllWritten = 0;
+    for(;;)
+    {
+        /* Finished? */
+        if (cbAllWritten == pInt->cbCurBuf)
+            break;
+        size_t cbToWrite = pInt->cbCurBuf - cbAllWritten;
+        size_t cbWritten = 0;
+        rc = pCallbacks->pfnWriteSync(pIO->pvUser, pInt->pvStorage, pInt->cbCurFile, &pInt->pcBuf[cbAllWritten], cbToWrite, &cbWritten);
+        if (RT_FAILURE(rc))
+            return rc;
+        pInt->cbCurFile += cbWritten;
+        cbAllWritten += cbWritten;
+    }
+    if (fCreateDigest)
+    {
+        /* Wait until the sha1 worker thread has finished. */
+        rc = sha1WaitForManifestThreadFinished(pInt);
+    }
+    if (RT_SUCCESS(rc))
+        pInt->cbCurBuf = 0;
+
+    return rc;
+}
+
+static int sha1OpenCallback(void *pvUser, const char *pszLocation, uint32_t fOpen,
+                            PFNVDCOMPLETED pfnCompleted, void **ppInt)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pszLocation, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(ppInt, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    DEBUG_PRINT_FLOW();
+
+    PSHA1STORAGEINTERNAL pInt = (PSHA1STORAGEINTERNAL)RTMemAllocZ(sizeof(SHA1STORAGEINTERNAL));
+    if (!pInt)
+        return VERR_NO_MEMORY;
+
+    int rc = VINF_SUCCESS;
+    do
+    {
+        pInt->pfnCompleted = pfnCompleted;
+        /* For caching reasons and to be able to calculate the sha1 sum of the
+           data we need a memory buffer. */
+        pInt->cbBuf = _1M;
+        pInt->pcBuf = (char*)RTMemAlloc(pInt->cbBuf);
+        if (!pInt->pcBuf)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+        /* The zero buffer is used for appending empty parts at the end of the
+         * file (or our buffer) in setSize or when uOffset in writeSync is
+         * increased in steps bigger than a byte. */
+        pInt->cbZeroBuf = _1K;
+        pInt->pvZeroBuf = RTMemAllocZ(pInt->cbZeroBuf);
+        if (!pInt->pvZeroBuf)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        if (pSha1Storage->fCreateDigest)
+        {
+            /* Create a sha1 context the sha1 worker thread will work with. */
+            if (!SHA1_Init(&pInt->ctx))
+            {
+                rc = VERR_INTERNAL_ERROR;
+                break;
+            }
+            /* Create an event semaphore to indicate a state change for the sha1
+               worker thread. */
+            rc = RTSemEventCreate(&pInt->newStatusEvent);
+            if (RT_FAILURE(rc))
+                break;
+            /* Create an event semaphore to indicate a finished calculation of the
+               sha1 worker thread. */
+            rc = RTSemEventCreate(&pInt->calcFinishedEvent);
+            if (RT_FAILURE(rc))
+                break;
+            /* Create the sha1 worker thread. */
+            rc = RTThreadCreate(&pInt->pMfThread, sha1CalcWorkerThread, pInt, 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, RTTHREADFLAGS_WAITABLE, "SHA1-Worker");
+            if (RT_FAILURE(rc))
+                break;
+        }
+        /* Open the file. */
+        rc = pCallbacks->pfnOpen(pIO->pvUser, pszLocation,
+                                 fOpen, pInt->pfnCompleted,
+                                 &pInt->pvStorage);
+    }
+    while(0);
+
+    if (RT_FAILURE(rc))
+    {
+        if (pSha1Storage->fCreateDigest)
+        {
+            if (pInt->pMfThread)
+            {
+                sha1SignalManifestThread(pInt, STATUS_END);
+                RTThreadWait(pInt->pMfThread, RT_INDEFINITE_WAIT, 0);
+            }
+            if (pInt->calcFinishedEvent)
+                RTSemEventDestroy(pInt->calcFinishedEvent);
+            if (pInt->newStatusEvent)
+                RTSemEventDestroy(pInt->newStatusEvent);
+        }
+        if (pInt->pvZeroBuf)
+            RTMemFree(pInt->pvZeroBuf);
+        if (pInt->pcBuf)
+            RTMemFree(pInt->pcBuf);
+        RTMemFree(pInt);
+    }
+    else
+        *ppInt = pInt;
+
+    return rc;
+}
+
+static int sha1CloseCallback(void *pvUser, void *pvStorage)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    PSHA1STORAGEINTERNAL pInt = (PSHA1STORAGEINTERNAL)pvStorage;
+
+    DEBUG_PRINT_FLOW();
+
+    int rc = VINF_SUCCESS;
+
+    /* Make sure all pending writes are flushed */
+    if (pInt->cbCurBuf > 0)
+        rc = sha1FlushCurBuf(pIO, pCallbacks, pInt, pSha1Storage->fCreateDigest);
+
+    if (pSha1Storage->fCreateDigest)
+    {
+        /* Signal the worker thread to end himself */
+        rc = sha1SignalManifestThread(pInt, STATUS_END);
+        /* Finally calculate & format the SHA1 sum */
+        unsigned char auchDig[RTSHA1_HASH_SIZE];
+        char *pszDigest;
+        if (!SHA1_Final(auchDig, &pInt->ctx))
+            rc = VERR_INTERNAL_ERROR;
+        rc = RTStrAllocEx(&pszDigest, RTSHA1_DIGEST_LEN + 1);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTSha1ToString(auchDig, pszDigest, RTSHA1_DIGEST_LEN + 1);
+            if (RT_SUCCESS(rc))
+                pSha1Storage->strDigest = pszDigest;
+            RTStrFree(pszDigest);
+        }
+        /* Worker thread stopped? */
+        rc = RTThreadWait(pInt->pMfThread, RT_INDEFINITE_WAIT, 0);
+    }
+    /* Close the file */
+    rc = pCallbacks->pfnClose(pIO->pvUser, pInt->pvStorage);
+
+    /* Cleanup */
+    if (pSha1Storage->fCreateDigest)
+    {
+        if (pInt->calcFinishedEvent)
+            RTSemEventDestroy(pInt->calcFinishedEvent);
+        if (pInt->newStatusEvent)
+            RTSemEventDestroy(pInt->newStatusEvent);
+    }
+    if (pInt->pvZeroBuf)
+        RTMemFree(pInt->pvZeroBuf);
+    if (pInt->pcBuf)
+        RTMemFree(pInt->pcBuf);
+    RTMemFree(pInt);
+
+    return rc;
+}
+
+static int sha1DeleteCallback(void *pvUser, const char *pcszFilename)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    DEBUG_PRINT_FLOW();
+
+    return pCallbacks->pfnDelete(pIO->pvUser, pcszFilename);
+}
+
+static int sha1MoveCallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    DEBUG_PRINT_FLOW();
+
+    return pCallbacks->pfnMove(pIO->pvUser, pcszSrc, pcszDst, fMove);
+}
+
+static int sha1GetFreeSpaceCallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    DEBUG_PRINT_FLOW();
+
+    return pCallbacks->pfnGetFreeSpace(pIO->pvUser, pcszFilename, pcbFreeSpace);
+}
+
+static int sha1GetModificationTimeCallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    DEBUG_PRINT_FLOW();
+
+    return pCallbacks->pfnGetModificationTime(pIO->pvUser, pcszFilename, pModificationTime);
+}
+
+
+static int sha1GetSizeCallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    PSHA1STORAGEINTERNAL pInt = (PSHA1STORAGEINTERNAL)pvStorage;
+
+//    DEBUG_PRINT_FLOW();
+
+    uint64_t cbSize;
+    int rc = pCallbacks->pfnGetSize(pIO->pvUser, pInt->pvStorage, &cbSize);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    *pcbSize = RT_MAX(pInt->cbCurAll, cbSize);
+    return VINF_SUCCESS;
+}
+
+static int sha1SetSizeCallback(void *pvUser, void *pvStorage, uint64_t cbSize)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    PSHA1STORAGEINTERNAL pInt = (PSHA1STORAGEINTERNAL)pvStorage;
+
+    DEBUG_PRINT_FLOW();
+
+    return pCallbacks->pfnSetSize(pIO->pvUser, pInt->pvStorage, cbSize);
+}
+
+static int sha1WriteSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset,
+                                 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    PSHA1STORAGEINTERNAL pInt = (PSHA1STORAGEINTERNAL)pvStorage;
+
+//    DEBUG_PRINT_FLOW();
+
+    /* Check that the write is linear */
+    AssertMsgReturn(pInt->cbCurAll <= uOffset, ("Backward seeking is not allowed (uOffset: %7lu cbCurAll: %7lu)!", uOffset, pInt->cbCurAll), VERR_INVALID_PARAMETER);
+
+    int rc = VINF_SUCCESS;
+
+    /* Check if we have to add some free space at the end, before we start the
+     * real write. */
+    if (pInt->cbCurAll < uOffset)
+    {
+        size_t cbSize = uOffset - pInt->cbCurAll;
+        size_t cbAllWritten = 0;
+        for(;;)
+        {
+            /* Finished? */
+            if (cbAllWritten == cbSize)
+                break;
+            size_t cbToWrite = RT_MIN(pInt->cbZeroBuf, cbSize - cbAllWritten);
+            size_t cbWritten = 0;
+            rc = sha1WriteSyncCallback(pvUser, pvStorage, pInt->cbCurAll,
+                                       pInt->pvZeroBuf, cbToWrite, &cbWritten);
+            if (RT_FAILURE(rc))
+                break;
+            cbAllWritten += cbWritten;
+        }
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+//    RTPrintf("Write uOffset: %7lu cbWrite: %7lu = %7lu\n", uOffset, cbWrite, uOffset + cbWrite);
+
+    size_t cbAllWritten = 0;
+    for(;;)
+    {
+        /* Finished? */
+        if (cbAllWritten == cbWrite)
+            break;
+        size_t cbToWrite = RT_MIN(pInt->cbBuf - pInt->cbCurBuf, cbWrite - cbAllWritten);
+        memcpy(&pInt->pcBuf[pInt->cbCurBuf], &((char*)pvBuf)[cbAllWritten], cbToWrite);
+        pInt->cbCurBuf += cbToWrite;
+        pInt->cbCurAll += cbToWrite;
+        cbAllWritten += cbToWrite;
+        /* Need to start a real write? */
+        if (pInt->cbCurBuf == pInt->cbBuf)
+        {
+            rc = sha1FlushCurBuf(pIO, pCallbacks, pInt, pSha1Storage->fCreateDigest);
+            if (RT_FAILURE(rc))
+                break;
+        }
+    }
+    if (pcbWritten)
+        *pcbWritten = cbAllWritten;
+
+    return rc;
+}
+
+static int sha1ReadSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset,
+                                void *pvBuf, size_t cbRead, size_t *pcbRead)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    DEBUG_PRINT_FLOW();
+
+    PSHA1STORAGEINTERNAL pInt = (PSHA1STORAGEINTERNAL)pvStorage;
+
+    return pCallbacks->pfnReadSync(pIO->pvUser, pInt->pvStorage, uOffset, pvBuf, cbRead, pcbRead);
+}
+
+static int sha1FlushSyncCallback(void *pvUser, void *pvStorage)
+{
+    /* Validate input. */
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
+
+    PSHA1STORAGE pSha1Storage = (PSHA1STORAGE)pvUser;
+    PVDINTERFACE pIO = VDInterfaceGet(pSha1Storage->pVDImageIfaces, VDINTERFACETYPE_IO);
+    AssertPtrReturn(pIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIO pCallbacks = VDGetInterfaceIO(pIO);
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    DEBUG_PRINT_FLOW();
+
+    PSHA1STORAGEINTERNAL pInt = (PSHA1STORAGEINTERNAL)pvStorage;
+
+    int rc = VINF_SUCCESS;
+
+    /* Check if there is still something in the buffer. If yes, flush it. */
+    if (pInt->cbCurBuf > 0)
+    {
+        rc = sha1FlushCurBuf(pIO, pCallbacks, pInt, pSha1Storage->fCreateDigest);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    return pCallbacks->pfnFlushSync(pIO->pvUser, pInt->pvStorage);
+}
+
+/******************************************************************************
+ *   Public Functions                                                         *
+ ******************************************************************************/
+
+PVDINTERFACEIO Sha1CreateInterface()
+{
+    PVDINTERFACEIO pCallbacks = (PVDINTERFACEIO)RTMemAllocZ(sizeof(VDINTERFACEIO));
+    if (!pCallbacks)
+        return NULL;
+
+    pCallbacks->cbSize                 = sizeof(VDINTERFACEIO);
+    pCallbacks->enmInterface           = VDINTERFACETYPE_IO;
+    pCallbacks->pfnOpen                = sha1OpenCallback;
+    pCallbacks->pfnClose               = sha1CloseCallback;
+    pCallbacks->pfnDelete              = sha1DeleteCallback;
+    pCallbacks->pfnMove                = sha1MoveCallback;
+    pCallbacks->pfnGetFreeSpace        = sha1GetFreeSpaceCallback;
+    pCallbacks->pfnGetModificationTime = sha1GetModificationTimeCallback;
+    pCallbacks->pfnGetSize             = sha1GetSizeCallback;
+    pCallbacks->pfnSetSize             = sha1SetSizeCallback;
+    pCallbacks->pfnReadSync            = sha1ReadSyncCallback;
+    pCallbacks->pfnWriteSync           = sha1WriteSyncCallback;
+    pCallbacks->pfnFlushSync           = sha1FlushSyncCallback;
+
+    return pCallbacks;
+}
+
+PVDINTERFACEIO RTFileCreateInterface()
+{
+    PVDINTERFACEIO pCallbacks = (PVDINTERFACEIO)RTMemAllocZ(sizeof(VDINTERFACEIO));
+    if (!pCallbacks)
+        return NULL;
+
+    pCallbacks->cbSize                 = sizeof(VDINTERFACEIO);
+    pCallbacks->enmInterface           = VDINTERFACETYPE_IO;
+    pCallbacks->pfnOpen                = rtFileOpenCallback;
+    pCallbacks->pfnClose               = rtFileCloseCallback;
+    pCallbacks->pfnDelete              = rtFileDeleteCallback;
+    pCallbacks->pfnMove                = rtFileMoveCallback;
+    pCallbacks->pfnGetFreeSpace        = rtFileGetFreeSpaceCallback;
+    pCallbacks->pfnGetModificationTime = rtFileGetModificationTimeCallback;
+    pCallbacks->pfnGetSize             = rtFileGetSizeCallback;
+    pCallbacks->pfnSetSize             = rtFileSetSizeCallback;
+    pCallbacks->pfnReadSync            = rtFileReadSyncCallback;
+    pCallbacks->pfnWriteSync           = rtFileWriteSyncCallback;
+    pCallbacks->pfnFlushSync           = rtFileFlushSyncCallback;
+
+    return pCallbacks;
+}
+
+PVDINTERFACEIO RTTarCreateInterface()
+{
+    PVDINTERFACEIO pCallbacks = (PVDINTERFACEIO)RTMemAllocZ(sizeof(VDINTERFACEIO));
+    if (!pCallbacks)
+        return NULL;
+
+    pCallbacks->cbSize                 = sizeof(VDINTERFACEIO);
+    pCallbacks->enmInterface           = VDINTERFACETYPE_IO;
+    pCallbacks->pfnOpen                = rtTarOpenCallback;
+    pCallbacks->pfnClose               = rtTarCloseCallback;
+    pCallbacks->pfnDelete              = rtTarDeleteCallback;
+    pCallbacks->pfnMove                = rtTarMoveCallback;
+    pCallbacks->pfnGetFreeSpace        = rtTarGetFreeSpaceCallback;
+    pCallbacks->pfnGetModificationTime = rtTarGetModificationTimeCallback;
+    pCallbacks->pfnGetSize             = rtTarGetSizeCallback;
+    pCallbacks->pfnSetSize             = rtTarSetSizeCallback;
+    pCallbacks->pfnReadSync            = rtTarReadSyncCallback;
+    pCallbacks->pfnWriteSync           = rtTarWriteSyncCallback;
+    pCallbacks->pfnFlushSync           = rtTarFlushSyncCallback;
+
+    return pCallbacks;
+}
+
+int Sha1WriteBuf(const char *pcszFilename, void *pvBuf, size_t cbSize, PVDINTERFACEIO pCallbacks, void *pvUser)
+{
+    /* Validate input. */
+    AssertPtrReturn(pCallbacks, VERR_INVALID_PARAMETER);
+
+    void *pvStorage;
+    int rc = pCallbacks->pfnOpen(pvUser, pcszFilename,
+                                 RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL, 0,
+                                 &pvStorage);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    size_t cbAllWritten = 0;
+    for(;;)
+    {
+        if (cbAllWritten >= cbSize)
+            break;
+        size_t cbToWrite = cbSize - cbAllWritten;
+        size_t cbWritten = 0;
+        rc = pCallbacks->pfnWriteSync(pvUser, pvStorage, cbAllWritten, &((char*)pvBuf)[cbAllWritten], cbToWrite, &cbWritten);
+        if (RT_FAILURE(rc))
+            break;
+        cbAllWritten += cbWritten;
+    }
+
+    pCallbacks->pfnClose(pvUser, pvStorage);
+
+    return rc;
+}
+
Index: trunk/src/VBox/Main/Makefile.kmk
===================================================================
--- trunk/src/VBox/Main/Makefile.kmk	(revision 33059)
+++ trunk/src/VBox/Main/Makefile.kmk	(revision 33060)
@@ -284,4 +284,5 @@
 	VirtualBoxImpl.cpp \
 	ApplianceImpl.cpp \
+	ApplianceImplIO.cpp \
 	ApplianceImplExport.cpp \
 	ApplianceImplImport.cpp \
Index: trunk/src/VBox/Main/include/ApplianceImpl.h
===================================================================
--- trunk/src/VBox/Main/include/ApplianceImpl.h	(revision 33059)
+++ trunk/src/VBox/Main/include/ApplianceImpl.h	(revision 33060)
@@ -28,4 +28,7 @@
 class VirtualSystemDescription;
 struct VirtualSystemDescriptionEntry;
+typedef struct VDINTERFACE   *PVDINTERFACE;
+typedef struct VDINTERFACEIO *PVDINTERFACEIO;
+typedef struct SHA1STORAGE   *PSHA1STORAGE;
 
 namespace ovf
@@ -185,6 +188,7 @@
 
     HRESULT writeFS(TaskOVF *pTask);
-    HRESULT writeFSOVF(TaskOVF *pTask);
-    HRESULT writeFSOVA(TaskOVF *pTask);
+    HRESULT writeFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock);
+    HRESULT writeFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock);
+    HRESULT writeFSImpl(TaskOVF *pTask, AutoWriteLockBase& writeLock, PVDINTERFACEIO pCallbacks, PSHA1STORAGE pStorage);
     HRESULT writeS3(TaskOVF *pTask);
 
Index: trunk/src/VBox/Main/include/ApplianceImplPrivate.h
===================================================================
--- trunk/src/VBox/Main/include/ApplianceImplPrivate.h	(revision 33059)
+++ trunk/src/VBox/Main/include/ApplianceImplPrivate.h	(revision 33060)
@@ -214,3 +214,16 @@
 ovf::CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox);
 
+typedef struct SHA1STORAGE
+{
+    PVDINTERFACE pVDImageIfaces;
+    bool         fCreateDigest;
+    Utf8Str      strDigest;
+} SHA1STORAGE, *PSHA1STORAGE;
+
+PVDINTERFACEIO Sha1CreateInterface();
+PVDINTERFACEIO RTFileCreateInterface();
+PVDINTERFACEIO RTTarCreateInterface();
+int Sha1WriteBuf(const char *pcszFilename, void *pvBuf, size_t cbSize, PVDINTERFACEIO pCallbacks, void *pvUser);
+
 #endif // ____H_APPLIANCEIMPLPRIVATE
+
