Index: /trunk/doc/manual/en_US/man_VBoxManage-mediumio.xml
===================================================================
--- /trunk/doc/manual/en_US/man_VBoxManage-mediumio.xml	(revision 74821)
+++ /trunk/doc/manual/en_US/man_VBoxManage-mediumio.xml	(revision 74822)
@@ -56,4 +56,17 @@
       <arg>--offset=<replaceable>byte-offset</replaceable></arg>
       <arg>--size=<replaceable>bytes</replaceable></arg>
+      <arg>--output=<replaceable>-|filename</replaceable></arg>
+    </cmdsynopsis>
+    <cmdsynopsis id="synopsis-vboxmanage-mediumio-stream">
+      <command>VBoxManage mediumio</command>
+      <group choice="req">
+        <arg>--disk=<replaceable>uuid|filename</replaceable></arg>
+        <arg>--dvd=<replaceable>uuid|filename</replaceable></arg>
+        <arg>--floppy=<replaceable>uuid|filename</replaceable></arg>
+      </group>
+      <arg>--password-file<replaceable>-|filename</replaceable></arg>
+      <arg choice="plain">stream</arg>
+      <arg>--format=<replaceable>image-format</replaceable></arg>
+      <arg>--variant=<replaceable>image-variant</replaceable></arg>
       <arg>--output=<replaceable>-|filename</replaceable></arg>
     </cmdsynopsis>
@@ -126,4 +139,24 @@
     </refsect2>
 
+    <refsect2 id="vboxmanage-mediumio-stream">
+      <title>mediumio stream</title>
+      <remark role="help-copy-synopsis"/>
+      <para>
+        Converts the medium to a streamable format and dumps it to the given output.
+      </para>
+      <variablelist>
+        <varlistentry>
+          <term><option>--format</option></term><listitem><para>The format of the destination image.</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><option>--variant</option></term><listitem><para>The medium variant for the destination.</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><option>--output</option></term>
+          <listitem><para>The output filename.  As usual <option>-</option> is take to mean stdout.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
   </refsect1>
 
Index: /trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp	(revision 74821)
+++ /trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp	(revision 74822)
@@ -2373,4 +2373,142 @@
 }
 
+/**
+ * mediumio stream
+ */
+static RTEXITCODE handleMediumIOStream(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
+{
+    /*
+     * Parse the options.
+     */
+    static const RTGETOPTDEF s_aOptions[] =
+    {
+        MEDIUMIOCOMMONOPT_DEFS(),
+        { "--output",   'O', RTGETOPT_REQ_STRING },
+        { "--format",   'F', RTGETOPT_REQ_STRING },
+        { "--variant",  'v', RTGETOPT_REQ_STRING }
+    };
+    const char *pszOutput = NULL;
+    MediumVariant_T enmMediumVariant = MediumVariant_Standard;
+    Bstr strFormat;
+
+    RTGETOPTSTATE GetState;
+    int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
+    AssertRC(rc);
+    RTGETOPTUNION ValueUnion;
+    while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+    {
+        switch (rc)
+        {
+            MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
+
+            case 'O':
+                pszOutput = ValueUnion.psz;
+                break;
+            case 'F':
+                strFormat = ValueUnion.psz;
+                break;
+            case 'v':   // --variant
+            {
+                int vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
+                if (RT_FAILURE(vrc))
+                    return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
+                break;
+            }
+
+            default:
+                return errorGetOpt(rc, &ValueUnion);
+        }
+    }
+
+    /*
+     * Open the medium for I/O.
+     */
+    ComPtr<IMediumIO>   ptrMediumIO;
+    uint64_t            cbMedium;
+    RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, false /*fWritable*/, ptrMediumIO, &cbMedium);
+    if (rcExit == RTEXITCODE_SUCCESS)
+    {
+        /*
+         * Do we have an output file or do we write to stdout?
+         */
+        PRTSTREAM pOut = NULL;
+        if (pszOutput && (pszOutput[0] != '-' || pszOutput[1] != '\0'))
+        {
+            int vrc = RTStrmOpen(pszOutput, "wb", &pOut);
+            if (RT_FAILURE(vrc))
+                rcExit = RTMsgErrorExitFailure("Error opening '%s' for writing: %Rrc", pszOutput, vrc);
+        }
+        else
+        {
+            pOut = g_pStdOut;
+            RTStrmSetMode(pOut, true, -1);
+        }
+
+        if (rcExit == RTEXITCODE_SUCCESS)
+        {
+            ComPtr<IDataStream> ptrDataStream;
+            ComPtr<IProgress> ptrProgress;
+
+            com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
+
+            for (ULONG i = 0; i < l_variants.size(); ++i)
+            {
+                ULONG temp = enmMediumVariant;
+                temp &= 1<<i;
+                l_variants [i] = (MediumVariant_T)temp;
+            }
+
+            HRESULT hrc = ptrMediumIO->ConvertToStream(strFormat.raw(), ComSafeArrayAsInParam(l_variants), 10 * _1M, ptrDataStream.asOutParam(), ptrProgress.asOutParam());
+            if (hrc == S_OK)
+            {
+                /* Read until we reached the end of the stream. */
+                for (;;)
+                {
+                    SafeArray<BYTE> SafeArrayBuf;
+
+                    hrc = ptrDataStream->Read(_64K, 0 /*Infinite wait*/, ComSafeArrayAsOutParam(SafeArrayBuf));
+                    if (   FAILED(hrc)
+                        || SafeArrayBuf.size() == 0)
+                        break;
+
+                    /* Output the data. */
+                    size_t const cbReturned = SafeArrayBuf.size();
+                    if (cbReturned)
+                    {
+                        BYTE const *pbBuf = SafeArrayBuf.raw();
+                        int vrc = VINF_SUCCESS;
+                        vrc = RTStrmWrite(pOut, pbBuf, cbReturned);
+                        if (RT_FAILURE(vrc))
+                        {
+                            rcExit = RTMsgErrorExitFailure("Error writing to '%s': %Rrc", pszOutput, vrc);
+                            break;
+                        }
+                    }
+
+                    /** @todo: Check progress. */
+                }
+            }
+            else
+            {
+                com::GlueHandleComError(ptrMediumIO, "ConvertToStream()", hrc, __FILE__, __LINE__);
+                rcExit = RTEXITCODE_FAILURE;
+            }
+
+            /*
+             * Close output.
+             */
+            if (pOut != g_pStdOut)
+            {
+                int vrc = RTStrmClose(pOut);
+                if (RT_FAILURE(vrc))
+                    rcExit = RTMsgErrorExitFailure("Error closing '%s': %Rrc", pszOutput, vrc);
+            }
+            else
+                RTStrmSetMode(pOut, false, -1);
+        }
+    }
+    return rcExit;
+}
+
 
 RTEXITCODE handleMediumIO(HandlerArg *a)
@@ -2385,4 +2523,5 @@
         { "formatfat",  1000, RTGETOPT_REQ_NOTHING },
         { "cat",        1001, RTGETOPT_REQ_NOTHING },
+        { "stream",     1002, RTGETOPT_REQ_NOTHING },
     };
     MEDIUMIOCOMMONOPT   CommonOpts = { NULL, DeviceType_Null, NULL };
@@ -2405,4 +2544,7 @@
                 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_CAT);
                 return handleMediumIOCat(a, GetState.iNext, &CommonOpts);
+            case 1002:
+                setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_STREAM);
+                return handleMediumIOStream(a, GetState.iNext, &CommonOpts);
 
             case VINF_GETOPT_NOT_OPTION:
Index: /trunk/src/VBox/Main/include/DataStreamImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/DataStreamImpl.h	(revision 74821)
+++ /trunk/src/VBox/Main/include/DataStreamImpl.h	(revision 74822)
@@ -22,4 +22,5 @@
 #include "DataStreamWrap.h"
 
+#include <iprt/circbuf.h>
 #include <iprt/semaphore.h>
 
@@ -51,8 +52,6 @@
 
 private:
-    /** Maximum number of bytes the buffer can hold. */
-    unsigned long     m_aBufferSize;
     /** The temporary buffer the conversion process writes into and the user reads from. */
-    std::vector<BYTE> m_aBuffer;
+    PRTCIRCBUF        m_pBuffer;
     /** Event semaphore for waiting until data is available. */
     RTSEMEVENT        m_hSemEvtDataAvail;
Index: /trunk/src/VBox/Main/include/MediumIOImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/MediumIOImpl.h	(revision 74821)
+++ /trunk/src/VBox/Main/include/MediumIOImpl.h	(revision 74822)
@@ -20,4 +20,6 @@
 
 #include "MediumIOWrap.h"
+#include "VirtualBoxBase.h"
+#include "AutoCaller.h"
 
 class ATL_NO_VTABLE MediumIO :
@@ -34,5 +36,6 @@
     /** @name Initializer & uninitializer.
      * @{ */
-    HRESULT initForMedium(Medium *pMedium, bool fWritable, com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword);
+    HRESULT initForMedium(Medium *pMedium, VirtualBox *pVirtualBox, bool fWritable,
+                          com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword);
     void    uninit();
     /** @} */
@@ -67,4 +70,7 @@
     struct Data;
     Data *m;
+
+    class StreamTask;
+    friend class StreamTask;
 };
 
Index: /trunk/src/VBox/Main/src-server/MediumIOImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/MediumIOImpl.cpp	(revision 74821)
+++ /trunk/src/VBox/Main/src-server/MediumIOImpl.cpp	(revision 74822)
@@ -24,11 +24,16 @@
 #include "MediumImpl.h"
 #include "MediumLock.h"
+#include "DataStreamImpl.h"
 #include "Global.h"
+#include "ProgressImpl.h"
+#include "VirtualBoxImpl.h"
 
 #include "AutoCaller.h"
 #include "Logging.h"
+#include "ThreadTask.h"
 
 #include <iprt/fsvfs.h>
 #include <iprt/dvm.h>
+#include <iprt/zero.h>
 #include <iprt/cpp/utils.h>
 
@@ -42,6 +47,7 @@
 struct MediumIO::Data
 {
-    Data(Medium * const a_pMedium, bool a_fWritable, uint32_t a_cbSector = 512)
+    Data(Medium * const a_pMedium, VirtualBox * const a_pVirtualBox, bool a_fWritable, uint32_t a_cbSector = 512)
         : ptrMedium(a_pMedium)
+        , ptrVirtualBox(a_pVirtualBox)
         , fWritable(a_fWritable)
         , cbSector(a_cbSector)
@@ -54,4 +60,6 @@
     /** Reference to the medium we're accessing. */
     ComPtr<Medium>                  ptrMedium;
+    /** Reference to the VirtualBox object the medium is part of. */
+    ComPtr<VirtualBox>              ptrVirtualBox;
     /** Set if writable, clear if readonly. */
     bool                            fWritable;
@@ -74,4 +82,391 @@
 
 
+/**
+ * MediumIO::StreamTask class for asynchronous convert to stream operation.
+ *
+ * @note Instances of this class must be created using new() because the
+ *       task thread function will delete them when the task is complete.
+ *
+ * @note The constructor of this class adds a caller on the managed Medium
+ *       object which is automatically released upon destruction.
+ */
+class MediumIO::StreamTask : public ThreadTask
+{
+public:
+    StreamTask(MediumIO *pMediumIO, DataStream *pDataStream, Progress *pProgress, const char *pszFormat,
+               MediumVariant_T fMediumVariant)
+        : ThreadTask("StreamTask"),
+          mVDOperationIfaces(NULL),
+          mMediumIO(pMediumIO),
+          mMediumCaller(pMediumIO->m->ptrMedium),
+          m_pDataStream(pDataStream),
+          m_fMediumVariant(fMediumVariant),
+          m_strFormat(pszFormat),
+          mProgress(pProgress),
+          mVirtualBoxCaller(NULL)
+    {
+        AssertReturnVoidStmt(pMediumIO, mRC = E_FAIL);
+        AssertReturnVoidStmt(pDataStream, mRC = E_FAIL);
+        mRC = mMediumCaller.rc();
+        if (FAILED(mRC))
+            return;
+
+        /* Get strong VirtualBox reference, see below. */
+        VirtualBox *pVirtualBox = pMediumIO->m->ptrVirtualBox;
+        mVirtualBox = pVirtualBox;
+        mVirtualBoxCaller.attach(pVirtualBox);
+        mRC = mVirtualBoxCaller.rc();
+        if (FAILED(mRC))
+            return;
+
+        /* Set up a per-operation progress interface, can be used freely (for
+         * binary operations you can use it either on the source or target). */
+        if (mProgress)
+        {
+            mVDIfProgress.pfnProgress = pProgress->i_vdProgressCallback;
+            int vrc = VDInterfaceAdd(&mVDIfProgress.Core,
+                                     "Medium::Task::vdInterfaceProgress",
+                                     VDINTERFACETYPE_PROGRESS,
+                                     mProgress,
+                                     sizeof(mVDIfProgress),
+                                     &mVDOperationIfaces);
+            AssertRC(vrc);
+            if (RT_FAILURE(vrc))
+                mRC = E_FAIL;
+        }
+    }
+
+    // Make all destructors virtual. Just in case.
+    virtual ~StreamTask()
+    {
+        /* send the notification of completion.*/
+        if (   isAsync()
+            && !mProgress.isNull())
+            mProgress->i_notifyComplete(mRC);
+    }
+
+    HRESULT rc() const { return mRC; }
+    bool isOk() const { return SUCCEEDED(rc()); }
+
+    const ComPtr<Progress>& GetProgressObject() const {return mProgress;}
+
+    /**
+     * Implementation code for the "create base" task.
+     * Used as function for execution from a standalone thread.
+     */
+    void handler()
+    {
+        LogFlowFuncEnter();
+        try
+        {
+            mRC = executeTask(); /* (destructor picks up mRC, see above) */
+            LogFlowFunc(("rc=%Rhrc\n", mRC));
+        }
+        catch (...)
+        {
+            LogRel(("Some exception in the function MediumIO::StreamTask:handler()\n"));
+        }
+
+        LogFlowFuncLeave();
+    }
+
+    PVDINTERFACE mVDOperationIfaces;
+
+    const ComObjPtr<MediumIO> mMediumIO;
+    AutoCaller mMediumCaller;
+
+protected:
+    HRESULT         mRC;
+
+    DataStream      *m_pDataStream;
+    MediumVariant_T m_fMediumVariant;
+    Utf8Str         m_strFormat;
+
+private:
+    HRESULT executeTask();
+
+    const ComObjPtr<Progress> mProgress;
+
+    VDINTERFACEPROGRESS mVDIfProgress;
+
+    /* Must have a strong VirtualBox reference during a task otherwise the
+     * reference count might drop to 0 while a task is still running. This
+     * would result in weird behavior, including deadlocks due to uninit and
+     * locking order issues. The deadlock often is not detectable because the
+     * uninit uses event semaphores which sabotages deadlock detection. */
+    ComObjPtr<VirtualBox> mVirtualBox;
+    AutoCaller mVirtualBoxCaller;
+
+    static DECLCALLBACK(int) i_vdStreamOpen(void *pvUser, const char *pszLocation, uint32_t fOpen,
+                                            PFNVDCOMPLETED pfnCompleted, void **ppStorage);
+    static DECLCALLBACK(int) i_vdStreamClose(void *pvUser, void *pStorage);
+    static DECLCALLBACK(int) i_vdStreamDelete(void *pvUser, const char *pcszFilename);
+    static DECLCALLBACK(int) i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove);
+    static DECLCALLBACK(int) i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace);
+    static DECLCALLBACK(int) i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime);
+    static DECLCALLBACK(int) i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize);
+    static DECLCALLBACK(int) i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize);
+    static DECLCALLBACK(int) i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer,
+                                            size_t *pcbRead);
+    static DECLCALLBACK(int) i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset,
+                                             const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten);
+    static DECLCALLBACK(int) i_vdStreamFlush(void *pvUser, void *pStorage);
+};
+
+
+/**
+ * State of a streamed file.
+ */
+typedef struct STREAMFILE
+{
+    /** The data stream for this file state. */
+    DataStream              *pDataStream;
+    /** The last seen offset used to stream zeroes for non consecutive writes. */
+    uint64_t                uOffsetLast;
+    /** Set file size. */
+    uint64_t                cbFile;
+} STREAMFILE;
+/** Pointer to the stream file state. */
+typedef STREAMFILE *PSTREAMFILE;
+
+
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamOpen(void *pvUser, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
+                                                       void **ppStorage)
+{
+    RT_NOREF2(pvUser, pszLocation);
+
+    /* Validate input. */
+    AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
+    AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER);
+
+    int rc = VINF_SUCCESS;
+    PSTREAMFILE pStreamFile = (PSTREAMFILE)RTMemAllocZ(sizeof(*pStreamFile));
+    if (RT_LIKELY(pStreamFile))
+    {
+        pStreamFile->pDataStream = (DataStream *)pvUser;
+        pStreamFile->uOffsetLast = 0;
+        pStreamFile->cbFile      = 0;
+        *ppStorage = pStreamFile;
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    return rc;
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamClose(void *pvUser, void *pStorage)
+{
+    RT_NOREF(pvUser);
+    PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
+    int rc = VINF_SUCCESS;
+
+    /* Fill up to the configured file size. */
+    if (pStreamFile->uOffsetLast < pStreamFile->cbFile)
+    {
+        do
+        {
+            size_t cbThisWrite = RT_MIN(pStreamFile->cbFile - pStreamFile->uOffsetLast, sizeof(g_abRTZero64K));
+            size_t cbWritten = 0;
+
+            rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
+            if (RT_SUCCESS(rc))
+                pStreamFile->uOffsetLast += cbWritten;
+
+        } while (   RT_SUCCESS(rc)
+                 && pStreamFile->uOffsetLast < pStreamFile->cbFile);
+    }
+
+    int rc2 = pStreamFile->pDataStream->i_close();
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    RTMemFree(pStreamFile);
+    return rc;
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamDelete(void *pvUser, const char *pcszFilename)
+{
+    NOREF(pvUser);
+    NOREF(pcszFilename);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
+{
+    NOREF(pvUser);
+    NOREF(pcszSrc);
+    NOREF(pcszDst);
+    NOREF(fMove);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
+{
+    NOREF(pvUser);
+    NOREF(pcszFilename);
+    AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
+    *pcbFreeSpace = INT64_MAX;
+    return VINF_SUCCESS;
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
+{
+    NOREF(pvUser);
+    NOREF(pcszFilename);
+    AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
+{
+    NOREF(pvUser);
+    PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
+    AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
+
+    *pcbSize = pStreamFile->cbFile;
+    return VINF_SUCCESS;
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
+{
+    RT_NOREF(pvUser);
+    PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
+
+    /* Reducing the size is not supported. */
+    int rc = VINF_SUCCESS;
+    if (pStreamFile->cbFile < cbSize)
+        pStreamFile->cbFile = cbSize;
+    else
+        rc = VERR_NOT_SUPPORTED;
+
+    return rc;
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer,
+                                                       size_t *pcbRead)
+{
+    NOREF(pvUser);
+    NOREF(pStorage);
+    NOREF(uOffset);
+    NOREF(cbBuffer);
+    NOREF(pcbRead);
+    AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset, const void *pvBuffer, size_t cbBuffer,
+                                                        size_t *pcbWritten)
+{
+    RT_NOREF(pvUser);
+    PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
+    int rc = VINF_SUCCESS;
+
+    /* Fill up to the new offset if there is non consecutive access. */
+    if (pStreamFile->uOffsetLast < uOffset)
+    {
+        do
+        {
+            size_t cbThisWrite = RT_MIN(uOffset - pStreamFile->uOffsetLast, sizeof(g_abRTZero64K));
+            size_t cbWritten = 0;
+
+            rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
+            if (RT_SUCCESS(rc))
+                pStreamFile->uOffsetLast += cbWritten;
+
+        } while (   RT_SUCCESS(rc)
+                 && pStreamFile->uOffsetLast < uOffset);
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcbWritten)
+            rc = pStreamFile->pDataStream->i_write(pvBuffer, cbBuffer, pcbWritten);
+        else
+        {
+            const uint8_t *pbBuf = (const uint8_t *)pvBuffer;
+            size_t cbLeft = cbBuffer;
+            size_t cbWritten = 0;
+            while (   cbLeft > 0
+                   && RT_SUCCESS(rc))
+            {
+                rc = pStreamFile->pDataStream->i_write(pbBuf, cbLeft, &cbWritten);
+                if (RT_SUCCESS(rc))
+                {
+                    pbBuf  += cbWritten;
+                    cbLeft -= cbWritten;
+                }
+            }
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+            size_t cbWritten = pcbWritten ? *pcbWritten : cbBuffer;
+
+            /* Adjust file size. */
+            if (uOffset + cbWritten > pStreamFile->cbFile)
+                pStreamFile->cbFile = uOffset + cbWritten;
+
+            pStreamFile->uOffsetLast = uOffset + cbWritten;
+        }
+    }
+
+    return rc;
+}
+
+DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamFlush(void *pvUser, void *pStorage)
+{
+    NOREF(pvUser);
+    NOREF(pStorage);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Implementation code for the "stream" task.
+ */
+HRESULT MediumIO::StreamTask::executeTask()
+{
+    HRESULT hrc = S_OK;
+    VDINTERFACEIO IfsOutputIO;
+    PVDINTERFACE pIfsOp = NULL;
+    PVDISK pDstDisk;
+
+    IfsOutputIO.pfnOpen                   = i_vdStreamOpen;
+    IfsOutputIO.pfnClose                  = i_vdStreamClose;
+    IfsOutputIO.pfnDelete                 = i_vdStreamDelete;
+    IfsOutputIO.pfnMove                   = i_vdStreamMove;
+    IfsOutputIO.pfnGetFreeSpace           = i_vdStreamGetFreeSpace;
+    IfsOutputIO.pfnGetModificationTime    = i_vdStreamGetModificationTime;
+    IfsOutputIO.pfnGetSize                = i_vdStreamGetSize;
+    IfsOutputIO.pfnSetSize                = i_vdStreamSetSize;
+    IfsOutputIO.pfnReadSync               = i_vdStreamRead;
+    IfsOutputIO.pfnWriteSync              = i_vdStreamWrite;
+    IfsOutputIO.pfnFlushSync              = i_vdStreamFlush;
+    VDInterfaceAdd(&IfsOutputIO.Core, "stream", VDINTERFACETYPE_IO,
+                   m_pDataStream, sizeof(VDINTERFACEIO), &pIfsOp);
+
+    int vrc = VDCreate(NULL, VDTYPE_HDD, &pDstDisk);
+    if (RT_SUCCESS(vrc))
+    {
+        /* Create the output image */
+        vrc = VDCopy(mMediumIO->m->pHdd, VD_LAST_IMAGE, pDstDisk, m_strFormat.c_str(),
+                     "stream", false, 0, m_fMediumVariant, NULL,
+                     VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
+                     pIfsOp, NULL);
+        if (RT_FAILURE(vrc))
+            hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
+                                          tr("Failed to convert and stream disk image"));
+
+        VDDestroy(pDstDisk);
+    }
+    else
+        hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
+                                      tr("Failed to create destination disk container"));
+
+    return hrc;
+}
+
+
 /*********************************************************************************************************************************
 *   Boilerplate constructor & destructor                                                                                         *
@@ -103,4 +498,5 @@
  *
  * @param   pMedium         Pointer to the medium to access.
+ * @param   pMedium         Pointer to the VirtualBox object the medium is part of.
  * @param   fWritable       Read-write (true) or readonly (false) access.
  * @param   rStrKeyId       The key ID for an encrypted medium.  Empty if not
@@ -110,7 +506,8 @@
  *
  */
-HRESULT MediumIO::initForMedium(Medium *pMedium, bool fWritable, com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword)
-{
-    LogFlowThisFunc(("pMedium=%p fWritable=%RTbool\n", pMedium, fWritable));
+HRESULT MediumIO::initForMedium(Medium *pMedium, VirtualBox *pVirtualBox, bool fWritable,
+                                com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword)
+{
+    LogFlowThisFunc(("pMedium=%p pVirtualBox=%p fWritable=%RTbool\n", pMedium, pVirtualBox, fWritable));
     CheckComArgExpr(rStrPassword, rStrPassword.isEmpty() == rStrKeyId.isEmpty()); /* checked by caller */
 
@@ -125,5 +522,5 @@
      */
     HRESULT hrc = S_OK;
-    m = new(std::nothrow) Data(pMedium, fWritable);
+    m = new(std::nothrow) Data(pMedium, pVirtualBox, fWritable);
     if (m)
     {
@@ -400,6 +797,50 @@
                                   ComPtr<IProgress> &aProgress)
 {
-    RT_NOREF(aFormat, aVariant, aBufferSize, aStream, aProgress);
-    return E_NOTIMPL;
+    HRESULT rc = S_OK;
+    ComObjPtr<Progress> pProgress;
+    ComObjPtr<DataStream> pDataStream;
+    MediumIO::StreamTask *pTask = NULL;
+
+    pProgress.createObject();
+    pDataStream.createObject();
+
+    try
+    {
+        rc = pDataStream->init(aBufferSize);
+        if (FAILED(rc))
+            throw rc;
+
+        ULONG mediumVariantFlags = 0;
+
+        if (aVariant.size())
+        {
+            for (size_t i = 0; i < aVariant.size(); i++)
+                mediumVariantFlags |= (ULONG)aVariant[i];
+        }
+
+        /* setup task object to carry out the operation asynchronously */
+        pTask = new MediumIO::StreamTask(this, pDataStream, pProgress,
+                                         aFormat.c_str(), (MediumVariant_T)mediumVariantFlags);
+        rc = pTask->rc();
+        AssertComRC(rc);
+        if (FAILED(rc))
+            throw rc;
+    }
+    catch (HRESULT aRC) { rc = aRC; }
+
+    if (SUCCEEDED(rc))
+    {
+        rc = pTask->createThread();
+
+        if (SUCCEEDED(rc))
+        {
+            pDataStream.queryInterfaceTo(aStream.asOutParam());
+            pProgress.queryInterfaceTo(aProgress.asOutParam());
+        }
+    }
+    else if (pTask != NULL)
+        delete pTask;
+
+    return rc;
 }
 
Index: /trunk/src/VBox/Main/src-server/MediumImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/MediumImpl.cpp	(revision 74821)
+++ /trunk/src/VBox/Main/src-server/MediumImpl.cpp	(revision 74822)
@@ -3840,5 +3840,5 @@
     if (SUCCEEDED(hrc))
     {
-        hrc = ptrIO->initForMedium(this, aWritable != FALSE, strKeyId, aPassword);
+        hrc = ptrIO->initForMedium(this, m->pVirtualBox, aWritable != FALSE, strKeyId, aPassword);
         if (SUCCEEDED(hrc))
             ptrIO.queryInterfaceTo(aMediumIO.asOutParam());
