VirtualBox

Changeset 74822 in vbox for trunk/src/VBox/Main/src-server


Ignore:
Timestamp:
Oct 12, 2018 6:40:09 PM (7 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
125775
Message:

Main: bugref:9152 Implement the convertToStream API

Location:
trunk/src/VBox/Main/src-server
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/src-server/MediumIOImpl.cpp

    r74763 r74822  
    2424#include "MediumImpl.h"
    2525#include "MediumLock.h"
     26#include "DataStreamImpl.h"
    2627#include "Global.h"
     28#include "ProgressImpl.h"
     29#include "VirtualBoxImpl.h"
    2730
    2831#include "AutoCaller.h"
    2932#include "Logging.h"
     33#include "ThreadTask.h"
    3034
    3135#include <iprt/fsvfs.h>
    3236#include <iprt/dvm.h>
     37#include <iprt/zero.h>
    3338#include <iprt/cpp/utils.h>
    3439
     
    4247struct MediumIO::Data
    4348{
    44     Data(Medium * const a_pMedium, bool a_fWritable, uint32_t a_cbSector = 512)
     49    Data(Medium * const a_pMedium, VirtualBox * const a_pVirtualBox, bool a_fWritable, uint32_t a_cbSector = 512)
    4550        : ptrMedium(a_pMedium)
     51        , ptrVirtualBox(a_pVirtualBox)
    4652        , fWritable(a_fWritable)
    4753        , cbSector(a_cbSector)
     
    5460    /** Reference to the medium we're accessing. */
    5561    ComPtr<Medium>                  ptrMedium;
     62    /** Reference to the VirtualBox object the medium is part of. */
     63    ComPtr<VirtualBox>              ptrVirtualBox;
    5664    /** Set if writable, clear if readonly. */
    5765    bool                            fWritable;
     
    7482
    7583
     84/**
     85 * MediumIO::StreamTask class for asynchronous convert to stream operation.
     86 *
     87 * @note Instances of this class must be created using new() because the
     88 *       task thread function will delete them when the task is complete.
     89 *
     90 * @note The constructor of this class adds a caller on the managed Medium
     91 *       object which is automatically released upon destruction.
     92 */
     93class MediumIO::StreamTask : public ThreadTask
     94{
     95public:
     96    StreamTask(MediumIO *pMediumIO, DataStream *pDataStream, Progress *pProgress, const char *pszFormat,
     97               MediumVariant_T fMediumVariant)
     98        : ThreadTask("StreamTask"),
     99          mVDOperationIfaces(NULL),
     100          mMediumIO(pMediumIO),
     101          mMediumCaller(pMediumIO->m->ptrMedium),
     102          m_pDataStream(pDataStream),
     103          m_fMediumVariant(fMediumVariant),
     104          m_strFormat(pszFormat),
     105          mProgress(pProgress),
     106          mVirtualBoxCaller(NULL)
     107    {
     108        AssertReturnVoidStmt(pMediumIO, mRC = E_FAIL);
     109        AssertReturnVoidStmt(pDataStream, mRC = E_FAIL);
     110        mRC = mMediumCaller.rc();
     111        if (FAILED(mRC))
     112            return;
     113
     114        /* Get strong VirtualBox reference, see below. */
     115        VirtualBox *pVirtualBox = pMediumIO->m->ptrVirtualBox;
     116        mVirtualBox = pVirtualBox;
     117        mVirtualBoxCaller.attach(pVirtualBox);
     118        mRC = mVirtualBoxCaller.rc();
     119        if (FAILED(mRC))
     120            return;
     121
     122        /* Set up a per-operation progress interface, can be used freely (for
     123         * binary operations you can use it either on the source or target). */
     124        if (mProgress)
     125        {
     126            mVDIfProgress.pfnProgress = pProgress->i_vdProgressCallback;
     127            int vrc = VDInterfaceAdd(&mVDIfProgress.Core,
     128                                     "Medium::Task::vdInterfaceProgress",
     129                                     VDINTERFACETYPE_PROGRESS,
     130                                     mProgress,
     131                                     sizeof(mVDIfProgress),
     132                                     &mVDOperationIfaces);
     133            AssertRC(vrc);
     134            if (RT_FAILURE(vrc))
     135                mRC = E_FAIL;
     136        }
     137    }
     138
     139    // Make all destructors virtual. Just in case.
     140    virtual ~StreamTask()
     141    {
     142        /* send the notification of completion.*/
     143        if (   isAsync()
     144            && !mProgress.isNull())
     145            mProgress->i_notifyComplete(mRC);
     146    }
     147
     148    HRESULT rc() const { return mRC; }
     149    bool isOk() const { return SUCCEEDED(rc()); }
     150
     151    const ComPtr<Progress>& GetProgressObject() const {return mProgress;}
     152
     153    /**
     154     * Implementation code for the "create base" task.
     155     * Used as function for execution from a standalone thread.
     156     */
     157    void handler()
     158    {
     159        LogFlowFuncEnter();
     160        try
     161        {
     162            mRC = executeTask(); /* (destructor picks up mRC, see above) */
     163            LogFlowFunc(("rc=%Rhrc\n", mRC));
     164        }
     165        catch (...)
     166        {
     167            LogRel(("Some exception in the function MediumIO::StreamTask:handler()\n"));
     168        }
     169
     170        LogFlowFuncLeave();
     171    }
     172
     173    PVDINTERFACE mVDOperationIfaces;
     174
     175    const ComObjPtr<MediumIO> mMediumIO;
     176    AutoCaller mMediumCaller;
     177
     178protected:
     179    HRESULT         mRC;
     180
     181    DataStream      *m_pDataStream;
     182    MediumVariant_T m_fMediumVariant;
     183    Utf8Str         m_strFormat;
     184
     185private:
     186    HRESULT executeTask();
     187
     188    const ComObjPtr<Progress> mProgress;
     189
     190    VDINTERFACEPROGRESS mVDIfProgress;
     191
     192    /* Must have a strong VirtualBox reference during a task otherwise the
     193     * reference count might drop to 0 while a task is still running. This
     194     * would result in weird behavior, including deadlocks due to uninit and
     195     * locking order issues. The deadlock often is not detectable because the
     196     * uninit uses event semaphores which sabotages deadlock detection. */
     197    ComObjPtr<VirtualBox> mVirtualBox;
     198    AutoCaller mVirtualBoxCaller;
     199
     200    static DECLCALLBACK(int) i_vdStreamOpen(void *pvUser, const char *pszLocation, uint32_t fOpen,
     201                                            PFNVDCOMPLETED pfnCompleted, void **ppStorage);
     202    static DECLCALLBACK(int) i_vdStreamClose(void *pvUser, void *pStorage);
     203    static DECLCALLBACK(int) i_vdStreamDelete(void *pvUser, const char *pcszFilename);
     204    static DECLCALLBACK(int) i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove);
     205    static DECLCALLBACK(int) i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace);
     206    static DECLCALLBACK(int) i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime);
     207    static DECLCALLBACK(int) i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize);
     208    static DECLCALLBACK(int) i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize);
     209    static DECLCALLBACK(int) i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer,
     210                                            size_t *pcbRead);
     211    static DECLCALLBACK(int) i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset,
     212                                             const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten);
     213    static DECLCALLBACK(int) i_vdStreamFlush(void *pvUser, void *pStorage);
     214};
     215
     216
     217/**
     218 * State of a streamed file.
     219 */
     220typedef struct STREAMFILE
     221{
     222    /** The data stream for this file state. */
     223    DataStream              *pDataStream;
     224    /** The last seen offset used to stream zeroes for non consecutive writes. */
     225    uint64_t                uOffsetLast;
     226    /** Set file size. */
     227    uint64_t                cbFile;
     228} STREAMFILE;
     229/** Pointer to the stream file state. */
     230typedef STREAMFILE *PSTREAMFILE;
     231
     232
     233
     234DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamOpen(void *pvUser, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
     235                                                       void **ppStorage)
     236{
     237    RT_NOREF2(pvUser, pszLocation);
     238
     239    /* Validate input. */
     240    AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
     241    AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
     242    AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER);
     243
     244    int rc = VINF_SUCCESS;
     245    PSTREAMFILE pStreamFile = (PSTREAMFILE)RTMemAllocZ(sizeof(*pStreamFile));
     246    if (RT_LIKELY(pStreamFile))
     247    {
     248        pStreamFile->pDataStream = (DataStream *)pvUser;
     249        pStreamFile->uOffsetLast = 0;
     250        pStreamFile->cbFile      = 0;
     251        *ppStorage = pStreamFile;
     252    }
     253    else
     254        rc = VERR_NO_MEMORY;
     255
     256    return rc;
     257}
     258
     259DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamClose(void *pvUser, void *pStorage)
     260{
     261    RT_NOREF(pvUser);
     262    PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
     263    int rc = VINF_SUCCESS;
     264
     265    /* Fill up to the configured file size. */
     266    if (pStreamFile->uOffsetLast < pStreamFile->cbFile)
     267    {
     268        do
     269        {
     270            size_t cbThisWrite = RT_MIN(pStreamFile->cbFile - pStreamFile->uOffsetLast, sizeof(g_abRTZero64K));
     271            size_t cbWritten = 0;
     272
     273            rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
     274            if (RT_SUCCESS(rc))
     275                pStreamFile->uOffsetLast += cbWritten;
     276
     277        } while (   RT_SUCCESS(rc)
     278                 && pStreamFile->uOffsetLast < pStreamFile->cbFile);
     279    }
     280
     281    int rc2 = pStreamFile->pDataStream->i_close();
     282    if (RT_SUCCESS(rc))
     283        rc = rc2;
     284
     285    RTMemFree(pStreamFile);
     286    return rc;
     287}
     288
     289DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamDelete(void *pvUser, const char *pcszFilename)
     290{
     291    NOREF(pvUser);
     292    NOREF(pcszFilename);
     293    AssertFailedReturn(VERR_NOT_SUPPORTED);
     294}
     295
     296DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
     297{
     298    NOREF(pvUser);
     299    NOREF(pcszSrc);
     300    NOREF(pcszDst);
     301    NOREF(fMove);
     302    AssertFailedReturn(VERR_NOT_SUPPORTED);
     303}
     304
     305DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
     306{
     307    NOREF(pvUser);
     308    NOREF(pcszFilename);
     309    AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
     310    *pcbFreeSpace = INT64_MAX;
     311    return VINF_SUCCESS;
     312}
     313
     314DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
     315{
     316    NOREF(pvUser);
     317    NOREF(pcszFilename);
     318    AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
     319    AssertFailedReturn(VERR_NOT_SUPPORTED);
     320}
     321
     322DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
     323{
     324    NOREF(pvUser);
     325    PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
     326    AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
     327
     328    *pcbSize = pStreamFile->cbFile;
     329    return VINF_SUCCESS;
     330}
     331
     332DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
     333{
     334    RT_NOREF(pvUser);
     335    PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
     336
     337    /* Reducing the size is not supported. */
     338    int rc = VINF_SUCCESS;
     339    if (pStreamFile->cbFile < cbSize)
     340        pStreamFile->cbFile = cbSize;
     341    else
     342        rc = VERR_NOT_SUPPORTED;
     343
     344    return rc;
     345}
     346
     347DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuffer, size_t cbBuffer,
     348                                                       size_t *pcbRead)
     349{
     350    NOREF(pvUser);
     351    NOREF(pStorage);
     352    NOREF(uOffset);
     353    NOREF(cbBuffer);
     354    NOREF(pcbRead);
     355    AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
     356    AssertFailedReturn(VERR_NOT_SUPPORTED);
     357}
     358
     359DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamWrite(void *pvUser, void *pStorage, uint64_t uOffset, const void *pvBuffer, size_t cbBuffer,
     360                                                        size_t *pcbWritten)
     361{
     362    RT_NOREF(pvUser);
     363    PSTREAMFILE pStreamFile = (PSTREAMFILE)pStorage;
     364    int rc = VINF_SUCCESS;
     365
     366    /* Fill up to the new offset if there is non consecutive access. */
     367    if (pStreamFile->uOffsetLast < uOffset)
     368    {
     369        do
     370        {
     371            size_t cbThisWrite = RT_MIN(uOffset - pStreamFile->uOffsetLast, sizeof(g_abRTZero64K));
     372            size_t cbWritten = 0;
     373
     374            rc = pStreamFile->pDataStream->i_write(&g_abRTZero64K[0], cbThisWrite, &cbWritten);
     375            if (RT_SUCCESS(rc))
     376                pStreamFile->uOffsetLast += cbWritten;
     377
     378        } while (   RT_SUCCESS(rc)
     379                 && pStreamFile->uOffsetLast < uOffset);
     380    }
     381
     382    if (RT_SUCCESS(rc))
     383    {
     384        if (pcbWritten)
     385            rc = pStreamFile->pDataStream->i_write(pvBuffer, cbBuffer, pcbWritten);
     386        else
     387        {
     388            const uint8_t *pbBuf = (const uint8_t *)pvBuffer;
     389            size_t cbLeft = cbBuffer;
     390            size_t cbWritten = 0;
     391            while (   cbLeft > 0
     392                   && RT_SUCCESS(rc))
     393            {
     394                rc = pStreamFile->pDataStream->i_write(pbBuf, cbLeft, &cbWritten);
     395                if (RT_SUCCESS(rc))
     396                {
     397                    pbBuf  += cbWritten;
     398                    cbLeft -= cbWritten;
     399                }
     400            }
     401        }
     402
     403        if (RT_SUCCESS(rc))
     404        {
     405            size_t cbWritten = pcbWritten ? *pcbWritten : cbBuffer;
     406
     407            /* Adjust file size. */
     408            if (uOffset + cbWritten > pStreamFile->cbFile)
     409                pStreamFile->cbFile = uOffset + cbWritten;
     410
     411            pStreamFile->uOffsetLast = uOffset + cbWritten;
     412        }
     413    }
     414
     415    return rc;
     416}
     417
     418DECLCALLBACK(int) MediumIO::StreamTask::i_vdStreamFlush(void *pvUser, void *pStorage)
     419{
     420    NOREF(pvUser);
     421    NOREF(pStorage);
     422    return VINF_SUCCESS;
     423}
     424
     425/**
     426 * Implementation code for the "stream" task.
     427 */
     428HRESULT MediumIO::StreamTask::executeTask()
     429{
     430    HRESULT hrc = S_OK;
     431    VDINTERFACEIO IfsOutputIO;
     432    PVDINTERFACE pIfsOp = NULL;
     433    PVDISK pDstDisk;
     434
     435    IfsOutputIO.pfnOpen                   = i_vdStreamOpen;
     436    IfsOutputIO.pfnClose                  = i_vdStreamClose;
     437    IfsOutputIO.pfnDelete                 = i_vdStreamDelete;
     438    IfsOutputIO.pfnMove                   = i_vdStreamMove;
     439    IfsOutputIO.pfnGetFreeSpace           = i_vdStreamGetFreeSpace;
     440    IfsOutputIO.pfnGetModificationTime    = i_vdStreamGetModificationTime;
     441    IfsOutputIO.pfnGetSize                = i_vdStreamGetSize;
     442    IfsOutputIO.pfnSetSize                = i_vdStreamSetSize;
     443    IfsOutputIO.pfnReadSync               = i_vdStreamRead;
     444    IfsOutputIO.pfnWriteSync              = i_vdStreamWrite;
     445    IfsOutputIO.pfnFlushSync              = i_vdStreamFlush;
     446    VDInterfaceAdd(&IfsOutputIO.Core, "stream", VDINTERFACETYPE_IO,
     447                   m_pDataStream, sizeof(VDINTERFACEIO), &pIfsOp);
     448
     449    int vrc = VDCreate(NULL, VDTYPE_HDD, &pDstDisk);
     450    if (RT_SUCCESS(vrc))
     451    {
     452        /* Create the output image */
     453        vrc = VDCopy(mMediumIO->m->pHdd, VD_LAST_IMAGE, pDstDisk, m_strFormat.c_str(),
     454                     "stream", false, 0, m_fMediumVariant, NULL,
     455                     VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
     456                     pIfsOp, NULL);
     457        if (RT_FAILURE(vrc))
     458            hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
     459                                          tr("Failed to convert and stream disk image"));
     460
     461        VDDestroy(pDstDisk);
     462    }
     463    else
     464        hrc = mMediumIO->setErrorBoth(VBOX_E_FILE_ERROR, vrc,
     465                                      tr("Failed to create destination disk container"));
     466
     467    return hrc;
     468}
     469
     470
    76471/*********************************************************************************************************************************
    77472*   Boilerplate constructor & destructor                                                                                         *
     
    103498 *
    104499 * @param   pMedium         Pointer to the medium to access.
     500 * @param   pMedium         Pointer to the VirtualBox object the medium is part of.
    105501 * @param   fWritable       Read-write (true) or readonly (false) access.
    106502 * @param   rStrKeyId       The key ID for an encrypted medium.  Empty if not
     
    110506 *
    111507 */
    112 HRESULT MediumIO::initForMedium(Medium *pMedium, bool fWritable, com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword)
    113 {
    114     LogFlowThisFunc(("pMedium=%p fWritable=%RTbool\n", pMedium, fWritable));
     508HRESULT MediumIO::initForMedium(Medium *pMedium, VirtualBox *pVirtualBox, bool fWritable,
     509                                com::Utf8Str const &rStrKeyId, com::Utf8Str const &rStrPassword)
     510{
     511    LogFlowThisFunc(("pMedium=%p pVirtualBox=%p fWritable=%RTbool\n", pMedium, pVirtualBox, fWritable));
    115512    CheckComArgExpr(rStrPassword, rStrPassword.isEmpty() == rStrKeyId.isEmpty()); /* checked by caller */
    116513
     
    125522     */
    126523    HRESULT hrc = S_OK;
    127     m = new(std::nothrow) Data(pMedium, fWritable);
     524    m = new(std::nothrow) Data(pMedium, pVirtualBox, fWritable);
    128525    if (m)
    129526    {
     
    400797                                  ComPtr<IProgress> &aProgress)
    401798{
    402     RT_NOREF(aFormat, aVariant, aBufferSize, aStream, aProgress);
    403     return E_NOTIMPL;
     799    HRESULT rc = S_OK;
     800    ComObjPtr<Progress> pProgress;
     801    ComObjPtr<DataStream> pDataStream;
     802    MediumIO::StreamTask *pTask = NULL;
     803
     804    pProgress.createObject();
     805    pDataStream.createObject();
     806
     807    try
     808    {
     809        rc = pDataStream->init(aBufferSize);
     810        if (FAILED(rc))
     811            throw rc;
     812
     813        ULONG mediumVariantFlags = 0;
     814
     815        if (aVariant.size())
     816        {
     817            for (size_t i = 0; i < aVariant.size(); i++)
     818                mediumVariantFlags |= (ULONG)aVariant[i];
     819        }
     820
     821        /* setup task object to carry out the operation asynchronously */
     822        pTask = new MediumIO::StreamTask(this, pDataStream, pProgress,
     823                                         aFormat.c_str(), (MediumVariant_T)mediumVariantFlags);
     824        rc = pTask->rc();
     825        AssertComRC(rc);
     826        if (FAILED(rc))
     827            throw rc;
     828    }
     829    catch (HRESULT aRC) { rc = aRC; }
     830
     831    if (SUCCEEDED(rc))
     832    {
     833        rc = pTask->createThread();
     834
     835        if (SUCCEEDED(rc))
     836        {
     837            pDataStream.queryInterfaceTo(aStream.asOutParam());
     838            pProgress.queryInterfaceTo(aProgress.asOutParam());
     839        }
     840    }
     841    else if (pTask != NULL)
     842        delete pTask;
     843
     844    return rc;
    404845}
    405846
  • trunk/src/VBox/Main/src-server/MediumImpl.cpp

    r74353 r74822  
    38403840    if (SUCCEEDED(hrc))
    38413841    {
    3842         hrc = ptrIO->initForMedium(this, aWritable != FALSE, strKeyId, aPassword);
     3842        hrc = ptrIO->initForMedium(this, m->pVirtualBox, aWritable != FALSE, strKeyId, aPassword);
    38433843        if (SUCCEEDED(hrc))
    38443844            ptrIO.queryInterfaceTo(aMediumIO.asOutParam());
Note: See TracChangeset for help on using the changeset viewer.

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