VirtualBox

Changeset 28401 in vbox


Ignore:
Timestamp:
Apr 16, 2010 9:14:54 AM (15 years ago)
Author:
vboxsync
Message:

Main/Machine+Snapshot+Medium: Big medium locking cleanup and straightened up the responsibilities between Snapshot and Medium. Rewritten task handling and cleaned up task implementation for medium operations. Implemented IMedium::mergeTo (no way to call it via any frontend yet), making the method parameters similar to IMedium::cloneto. Plus lots of other minor cleanups.

Location:
trunk/src/VBox/Main
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/MachineImpl.cpp

    r28352 r28401  
    3737#endif
    3838
     39#include "Logging.h"
    3940#include "VirtualBoxImpl.h"
    4041#include "MachineImpl.h"
     
    4243#include "MediumAttachmentImpl.h"
    4344#include "MediumImpl.h"
     45#include "MediumLock.h"
    4446#include "USBControllerImpl.h"
    4547#include "HostImpl.h"
     
    5557
    5658#include "AutoCaller.h"
    57 #include "Logging.h"
    5859#include "Performance.h"
    5960
     
    20762077    AutoCaller autoCaller(this);
    20772078    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    2078     MultiResult rc (S_OK);
     2079    MultiResult rc(S_OK);
    20792080
    20802081# ifdef VBOX_WITH_USB
     
    31413142        if (FAILED(rc)) return rc;
    31423143
    3143         /* make sure the hard disk is not modified before createDiffStorage() */
    3144         rc = medium->LockRead(NULL);
     3144        /* Apply the normal locking logic to the entire chain. */
     3145        MediumLockList *pMediumLockList(new MediumLockList());
     3146        rc = diff->createMediumLockList(true, medium, *pMediumLockList);
    31453147        if (FAILED(rc)) return rc;
     3148        rc = pMediumLockList->Lock();
     3149        if (FAILED(rc))
     3150            return setError(rc,
     3151                            tr("Could not lock medium when creating diff '%s'"),
     3152                            diff->getLocationFull().c_str());
    31463153
    31473154        /* will leave the lock before the potentially lengthy operation, so
     
    31533160        alock.leave();
    31543161
    3155         rc = medium->createDiffStorageAndWait(diff, MediumVariant_Standard, &fNeedsSaveSettings);
     3162        rc = medium->createDiffStorage(diff, MediumVariant_Standard,
     3163                                       pMediumLockList, NULL /* aProgress */,
     3164                                       true /* aWait */, &fNeedsSaveSettings);
    31563165
    31573166        alock.enter();
     
    31603169        setMachineState(oldState);
    31613170
    3162         medium->UnlockRead(NULL);
     3171        /* Unlock the media and free the associated memory. */
     3172        delete pMediumLockList;
    31633173
    31643174        if (FAILED(rc)) return rc;
     
    32573267        alock.leave();
    32583268
    3259         rc = oldmedium->deleteStorageAndWait(NULL /*aProgress*/, &fNeedsSaveSettings);
     3269        rc = oldmedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/,
     3270                                      &fNeedsSaveSettings);
    32603271
    32613272        alock.enter();
     
    45974608
    45984609    PSSMHANDLE pSSM;
    4599     int rc = SSMR3Open(pStateFilePath->raw(), 0 /*fFlags*/, &pSSM);
    4600     if (RT_SUCCESS(rc))
     4610    int vrc = SSMR3Open(pStateFilePath->raw(), 0 /*fFlags*/, &pSSM);
     4611    if (RT_SUCCESS(vrc))
    46014612    {
    46024613        uint32_t uVersion;
    4603         rc = SSMR3Seek(pSSM, "DisplayScreenshot", 1100 /*iInstance*/, &uVersion);
    4604         if (RT_SUCCESS(rc))
     4614        vrc = SSMR3Seek(pSSM, "DisplayScreenshot", 1100 /*iInstance*/, &uVersion);
     4615        if (RT_SUCCESS(vrc))
    46054616        {
    46064617            if (uVersion == sSSMDisplayScreenshotVer)
    46074618            {
    46084619                uint32_t cBlocks;
    4609                 rc = SSMR3GetU32(pSSM, &cBlocks);
    4610                 AssertRCReturn(rc, rc);
     4620                vrc = SSMR3GetU32(pSSM, &cBlocks);
     4621                AssertRCReturn(vrc, vrc);
    46114622
    46124623                for (uint32_t i = 0; i < cBlocks; i++)
    46134624                {
    46144625                    uint32_t cbBlock;
    4615                     rc = SSMR3GetU32(pSSM, &cbBlock);
    4616                     AssertRCBreak(rc);
     4626                    vrc = SSMR3GetU32(pSSM, &cbBlock);
     4627                    AssertRCBreak(vrc);
    46174628
    46184629                    uint32_t typeOfBlock;
    4619                     rc = SSMR3GetU32(pSSM, &typeOfBlock);
    4620                     AssertRCBreak(rc);
     4630                    vrc = SSMR3GetU32(pSSM, &typeOfBlock);
     4631                    AssertRCBreak(vrc);
    46214632
    46224633                    LogFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
     
    46304641                            if (pu8Data == NULL)
    46314642                            {
    4632                                 rc = VERR_NO_MEMORY;
     4643                                vrc = VERR_NO_MEMORY;
    46334644                                break;
    46344645                            }
    46354646
    4636                             rc = SSMR3GetU32(pSSM, &u32Width);
    4637                             AssertRCBreak(rc);
    4638                             rc = SSMR3GetU32(pSSM, &u32Height);
    4639                             AssertRCBreak(rc);
    4640                             rc = SSMR3GetMem(pSSM, pu8Data, cbData);
    4641                             AssertRCBreak(rc);
     4647                            vrc = SSMR3GetU32(pSSM, &u32Width);
     4648                            AssertRCBreak(vrc);
     4649                            vrc = SSMR3GetU32(pSSM, &u32Height);
     4650                            AssertRCBreak(vrc);
     4651                            vrc = SSMR3GetMem(pSSM, pu8Data, cbData);
     4652                            AssertRCBreak(vrc);
    46424653                        }
    46434654                        else
    46444655                        {
    46454656                            /* No saved state data. */
    4646                             rc = VERR_NOT_SUPPORTED;
     4657                            vrc = VERR_NOT_SUPPORTED;
    46474658                        }
    46484659
     
    46564667                        if (cbBlock > 2 * sizeof (uint32_t))
    46574668                        {
    4658                             rc = SSMR3Skip(pSSM, cbBlock);
    4659                             AssertRCBreak(rc);
     4669                            vrc = SSMR3Skip(pSSM, cbBlock);
     4670                            AssertRCBreak(vrc);
    46604671                        }
    46614672                    }
     
    46644675            else
    46654676            {
    4666                 rc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
     4677                vrc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
    46674678            }
    46684679        }
     
    46714682    }
    46724683
    4673     if (RT_SUCCESS(rc))
     4684    if (RT_SUCCESS(vrc))
    46744685    {
    46754686        if (u32Type == 0 && cbData % 4 != 0)
    46764687        {
    46774688            /* Bitmap is 32bpp, so data is invalid. */
    4678             rc = VERR_SSM_UNEXPECTED_DATA;
    4679         }
    4680     }
    4681 
    4682     if (RT_SUCCESS(rc))
     4689            vrc = VERR_SSM_UNEXPECTED_DATA;
     4690        }
     4691    }
     4692
     4693    if (RT_SUCCESS(vrc))
    46834694    {
    46844695        *ppu8Data = pu8Data;
     
    46894700    }
    46904701
    4691     LogFlowFunc(("rc %Rrc\n", rc));
    4692     return rc;
     4702    LogFlowFunc(("vrc %Rrc\n", vrc));
     4703    return vrc;
    46934704}
    46944705
     
    63346345     * the VM after successfully re-checking the accessibility state. Note
    63356346     * that in case of normal Machine or SnapshotMachine uninitialization (as
    6336      * a result of unregistering or discarding the snapshot), outdated hard
     6347     * a result of unregistering or deleting the snapshot), outdated hard
    63376348     * disk attachments will already be uninitialized and deleted, so this
    63386349     * code will not affect them. */
     
    81368147 * machine and a new set of attachments to refer to created disks.
    81378148 *
    8138  * Used when taking a snapshot or when discarding the current state.
     8149 * Used when taking a snapshot or when deleting the current state.
    81398150 *
    81408151 * This method assumes that mMediaData contains the original hard disk attachments
     
    81898200    HRESULT rc = S_OK;
    81908201
    8191     MediaList lockedMedia;
     8202    MediumLockListMap lockedMediaOffline;
     8203    MediumLockListMap *lockedMediaMap;
     8204    if (aOnline)
     8205        lockedMediaMap = &mData->mSession.mLockedMedia;
     8206    else
     8207        lockedMediaMap = &lockedMediaOffline;
    81928208
    81938209    try
     
    82048220                if (pAtt->getType() == DeviceType_HardDisk)
    82058221                {
    8206                     Medium* pHD = pAtt->getMedium();
    8207                     Assert(pHD);
    8208                     rc = pHD->LockRead(NULL);
    8209                     if (FAILED(rc)) throw rc;
    8210                     lockedMedia.push_back(pHD);
     8222                    Medium* pMedium = pAtt->getMedium();
     8223                    Assert(pMedium);
     8224
     8225                    MediumLockList *pMediumLockList(new MediumLockList());
     8226                    rc = pMedium->createMediumLockList(false, NULL,
     8227                                                       *pMediumLockList);
     8228                    if (FAILED(rc))
     8229                    {
     8230                        delete pMediumLockList;
     8231                        throw rc;
     8232                    }
     8233                    rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
     8234                    if (FAILED(rc))
     8235                    {
     8236                        throw setError(rc,
     8237                                       tr("Collecting locking information for all attached media failed"));
     8238                    }
    82118239                }
     8240            }
     8241
     8242            /* Now lock all media. If this fails, nothing is locked. */
     8243            rc = lockedMediaMap->Lock();
     8244            if (FAILED(rc))
     8245            {
     8246                throw setError(rc,
     8247                               tr("Locking of attached media failed"));
    82128248            }
    82138249        }
     
    82298265
    82308266            DeviceType_T devType = pAtt->getType();
    8231             Medium* medium = pAtt->getMedium();
     8267            Medium* pMedium = pAtt->getMedium();
    82328268
    82338269            if (   devType != DeviceType_HardDisk
    8234                 || medium == NULL
    8235                 || medium->getType() != MediumType_Normal)
     8270                || pMedium == NULL
     8271                || pMedium->getType() != MediumType_Normal)
    82368272            {
    82378273                /* copy the attachment as is */
     
    82428278                if (devType == DeviceType_HardDisk)
    82438279                {
    8244                     if (medium == NULL)
     8280                    if (pMedium == NULL)
    82458281                        aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),
    82468282                                                    aWeight);        // weight
    82478283                    else
    82488284                        aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
    8249                                                             medium->getBase()->getName().raw()),
     8285                                                            pMedium->getBase()->getName().raw()),
    82508286                                                    aWeight);        // weight
    82518287                }
     
    82578293            /* need a diff */
    82588294            aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
    8259                                                 medium->getBase()->getName().raw()),
     8295                                                pMedium->getBase()->getName().raw()),
    82608296                                        aWeight);        // weight
    82618297
     
    82638299            diff.createObject();
    82648300            rc = diff->init(mParent,
    8265                             medium->preferredDiffFormat().raw(),
     8301                            pMedium->preferredDiffFormat().raw(),
    82668302                            BstrFmt("%ls"RTPATH_SLASH_STR,
    82678303                                    mUserData->mSnapshotFolderFull.raw()).raw(),
     
    82698305            if (FAILED(rc)) throw rc;
    82708306
    8271             /* leave the lock before the potentially lengthy operation */
    8272             alock.leave();
    8273 
    8274             rc = medium->createDiffStorageAndWait(diff,
    8275                                                   MediumVariant_Standard,
    8276                                                   pfNeedsSaveSettings);
    8277 
    82788307            /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
    82798308             *        the push_back?  Looks like we're going to leave medium with the
    82808309             *        wrong kind of lock (general issue with if we fail anywhere at all)
    82818310             *        and an orphaned VDI in the snapshots folder. */
    8282             // at this point, the old image is still locked for writing, but instead
    8283             // we need the new diff image locked for writing and lock the previously
    8284             // current one for reading only
     8311
     8312            /* update the appropriate lock list */
     8313            rc = lockedMediaMap->Unlock();
     8314            AssertComRCThrowRC(rc);
     8315            MediumLockList *pMediumLockList;
     8316            rc = lockedMediaMap->Get(pAtt, pMediumLockList);
     8317            AssertComRCThrowRC(rc);
    82858318            if (aOnline)
    82868319            {
    8287                 diff->LockWrite(NULL);
    8288                 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(diff), true));
    8289                 medium->UnlockWrite(NULL);
    8290                 medium->LockRead(NULL);
    8291                 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(medium), false));
     8320                rc = pMediumLockList->Update(pMedium, false);
     8321                AssertComRCThrowRC(rc);
    82928322            }
    8293 
     8323            rc = lockedMediaMap->Lock();
     8324            AssertComRCThrowRC(rc);
     8325
     8326            /* leave the lock before the potentially lengthy operation */
     8327            alock.leave();
     8328            rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
     8329                                            pMediumLockList,
     8330                                            NULL /* aProgress */,
     8331                                            true /* aWait */,
     8332                                            pfNeedsSaveSettings);
     8333            alock.enter();
    82948334            if (FAILED(rc)) throw rc;
    82958335
    8296             alock.enter();
     8336            rc = lockedMediaMap->Unlock();
     8337            AssertComRCThrowRC(rc);
     8338            rc = pMediumLockList->Append(diff, true);
     8339            AssertComRCThrowRC(rc);
     8340            rc = lockedMediaMap->Lock();
     8341            AssertComRCThrowRC(rc);
    82978342
    82988343            rc = diff->attachTo(mData->mUuid);
     
    83118356            if (FAILED(rc)) throw rc;
    83128357
     8358            rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
     8359            AssertComRCThrowRC(rc);
    83138360            mMediaData->mAttachments.push_back(attachment);
    83148361        }
     
    83218368        ErrorInfoKeeper eik;
    83228369
    8323         for (MediaList::const_iterator it = lockedMedia.begin();
    8324              it != lockedMedia.end();
    8325              ++it)
    8326         {
    8327             HRESULT rc2 = (*it)->UnlockRead(NULL);
    8328             AssertComRC(rc2);
    8329         }
     8370        rc = lockedMediaMap->Clear();
     8371        AssertComRC(rc);
    83308372    }
    83318373
     
    84278469            ComObjPtr<Medium> hd = (*it)->getMedium();
    84288470
    8429             rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings);
    8430 #if 1 /* HACK ALERT: Just make it kind of work */ /** @todo Fix this hack properly. The LockWrite / UnlockWrite / LockRead changes aren't undone! */
    8431             if (rc == VBOX_E_INVALID_OBJECT_STATE)
    8432             {
    8433                 LogFlowFunc(("Applying unlock hack on '%s'! FIXME!\n", (*it)->getLogName()));
    8434                 hd->UnlockWrite(NULL);
    8435                 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings);
    8436             }
    8437 #endif
     8471            rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/,
     8472                                   pfNeedsSaveSettings);
    84388473            AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
    84398474            mrc = rc;
     
    85638598
    85648599    MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
     8600    bool fMediaNeedsLocking = false;
    85658601
    85668602    /* enumerate new attachments */
     
    85818617
    85828618        /** @todo convert all this Machine-based voodoo to MediumAttachment
    8583         * based commit logic. */
     8619         * based commit logic. */
    85848620        if (fImplicit)
    85858621        {
     
    85928628                )
    85938629            {
    8594                 rc = pMedium->LockWrite(NULL);
     8630                ComObjPtr<Medium> parent = pMedium->getParent();
     8631                AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
     8632
     8633                /* update the appropriate lock list */
     8634                MediumLockList *pMediumLockList;
     8635                rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
    85958636                AssertComRC(rc);
    8596 
    8597                 mData->mSession.mLockedMedia.push_back(
    8598                     Data::Session::LockedMedia::value_type(
    8599                         ComPtr<IMedium>(pMedium), true));
    8600 
    8601                 /* also, relock the old hard disk which is a base for the
    8602                 * new diff for reading if the VM is online */
    8603 
    8604                 ComObjPtr<Medium> parent = pMedium->getParent();
    8605                 /* make the relock atomic */
    8606                 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
    8607                 rc = parent->UnlockWrite(NULL);
    8608                 AssertComRC(rc);
    8609                 rc = parent->LockRead(NULL);
    8610                 AssertComRC(rc);
    8611 
    8612                 /* XXX actually we should replace the old entry in that
    8613                 * vector (write lock => read lock) but this would take
    8614                 * some effort. So lets just ignore the error code in
    8615                 * SessionMachine::unlockMedia(). */
    8616                 mData->mSession.mLockedMedia.push_back(
    8617                     Data::Session::LockedMedia::value_type (
    8618                         ComPtr<IMedium>(parent), false));
     8637                if (pMediumLockList)
     8638                {
     8639                    /* unlock if there's a need to change the locking */
     8640                    if (!fMediaNeedsLocking)
     8641                    {
     8642                        rc = mData->mSession.mLockedMedia.Unlock();
     8643                        AssertComRC(rc);
     8644                        fMediaNeedsLocking = true;
     8645                    }
     8646                    rc = pMediumLockList->Update(parent, false);
     8647                    AssertComRC(rc);
     8648                    rc = pMediumLockList->Append(pMedium, true);
     8649                    AssertComRC(rc);
     8650                }
    86198651            }
    86208652
     
    86438675
    86448676    /* enumerate remaining old attachments and de-associate from the
    8645         * current machine state */
     8677     * current machine state */
    86468678    for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
    8647             it != oldAtts.end();
    8648             ++it)
     8679         it != oldAtts.end();
     8680         ++it)
    86498681    {
    86508682        MediumAttachment *pAttach = *it;
     
    86528684
    86538685        /* Detach only hard disks, since DVD/floppy media is detached
    8654             * instantly in MountMedium. */
     8686         * instantly in MountMedium. */
    86558687        if (pAttach->getType() == DeviceType_HardDisk && pMedium)
    86568688        {
     
    86618693            AssertComRC(rc);
    86628694
    8663             if (    aOnline
    8664                     && pAttach->getType() == DeviceType_HardDisk)
     8695            if (aOnline)
    86658696            {
    8666                 /* unlock since not used anymore */
    8667                 MediumState_T state;
    8668                 rc = pMedium->UnlockWrite(&state);
    8669                 /* the disk may be alredy relocked for reading above */
    8670                 Assert(SUCCEEDED(rc) || state == MediumState_LockedRead);
     8697                /* unlock since medium is not used anymore */
     8698                MediumLockList *pMediumLockList;
     8699                rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
     8700                AssertComRC(rc);
     8701                if (pMediumLockList)
     8702                {
     8703                    rc = mData->mSession.mLockedMedia.Remove(pAttach);
     8704                    AssertComRC(rc);
     8705                }
    86718706            }
    86728707        }
     8708    }
     8709
     8710    /* take media locks again so that the locking state is consistent */
     8711    if (fMediaNeedsLocking)
     8712    {
     8713        Assert(aOnline);
     8714        rc = mData->mSession.mLockedMedia.Lock();
     8715        AssertComRC(rc);
    86738716    }
    86748717
     
    1077410817                 || mData->mMachineState == MachineState_Restoring
    1077510818                 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
    10776 
    10777     try
    10778     {
    10779         HRESULT rc = S_OK;
    10780 
    10781         ErrorInfoKeeper eik(true /* aIsNull */);
    10782         MultiResult mrc(S_OK);
    10783 
    10784         /* Lock all medium objects attached to the VM.
    10785          * Get status for inaccessible media as well. */
    10786         for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
    10787              it != mMediaData->mAttachments.end();
    10788              ++it)
    10789         {
    10790             DeviceType_T devType = (*it)->getType();
    10791             ComObjPtr<Medium> medium = (*it)->getMedium();
    10792 
    10793             bool first = true;
    10794 
    10795             /** @todo split out the media locking, and put it into
    10796              * MediumImpl.cpp, as it needs this functionality too. */
    10797             while (!medium.isNull())
     10819    /* bail out if trying to lock things with already set up locking */
     10820    AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
     10821
     10822    MultiResult mrc(S_OK);
     10823
     10824    /* Collect locking information for all medium objects attached to the VM. */
     10825    for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
     10826         it != mMediaData->mAttachments.end();
     10827         ++it)
     10828    {
     10829        MediumAttachment* pAtt = *it;
     10830        DeviceType_T devType = pAtt->getType();
     10831        Medium *pMedium = pAtt->getMedium();
     10832
     10833        MediumLockList *pMediumLockList(new MediumLockList());
     10834        // There can be attachments without a medium (floppy/dvd), and thus
     10835        // it's impossible to create a medium lock list. It still makes sense
     10836        // to have the empty medium lock list in the map in case a medium is
     10837        // attached later.
     10838        if (pMedium != NULL)
     10839        {
     10840            mrc = pMedium->createMediumLockList(devType != DeviceType_DVD,
     10841                                                NULL, *pMediumLockList);
     10842            if (FAILED(mrc))
    1079810843            {
    10799                 MediumState_T mediumState = medium->getState();
    10800 
    10801                 /* accessibility check must be first, otherwise locking
    10802                  * interferes with getting the medium state. */
    10803                 if (mediumState == MediumState_Inaccessible)
    10804                 {
    10805                     rc = medium->RefreshState(&mediumState);
    10806                     if (FAILED(rc)) throw rc;
    10807 
    10808                     if (mediumState == MediumState_Inaccessible)
    10809                     {
    10810                         Bstr error;
    10811                         rc = medium->COMGETTER(LastAccessError)(error.asOutParam());
    10812                         if (FAILED(rc)) throw rc;
    10813 
    10814                         Bstr loc;
    10815                         rc = medium->COMGETTER(Location)(loc.asOutParam());
    10816                         if (FAILED(rc)) throw rc;
    10817 
    10818                         /* collect multiple errors */
    10819                         eik.restore();
    10820 
    10821                         /* be in sync with MediumBase::setStateError() */
    10822                         Assert(!error.isEmpty());
    10823                         mrc = setError(E_FAIL,
    10824                                        tr("Medium '%ls' is not accessible. %ls"),
    10825                                        loc.raw(),
    10826                                        error.raw());
    10827 
    10828                         eik.fetch();
    10829                     }
    10830                 }
    10831 
    10832                 if (first)
    10833                 {
    10834                     if (devType != DeviceType_DVD)
    10835                     {
    10836                         /* HardDisk and Floppy medium must be locked for writing */
    10837                         rc = medium->LockWrite(NULL);
    10838                         if (FAILED(rc)) throw rc;
    10839                     }
    10840                     else
    10841                     {
    10842                         /* DVD medium must be locked for reading */
    10843                         rc = medium->LockRead(NULL);
    10844                         if (FAILED(rc)) throw rc;
    10845                     }
    10846 
    10847                     mData->mSession.mLockedMedia.push_back(
    10848                         Data::Session::LockedMedia::value_type(
    10849                             ComPtr<IMedium>(medium), true));
    10850 
    10851                     first = false;
    10852                 }
    10853                 else
    10854                 {
    10855                     rc = medium->LockRead(NULL);
    10856                     if (FAILED(rc)) throw rc;
    10857 
    10858                     mData->mSession.mLockedMedia.push_back(
    10859                         Data::Session::LockedMedia::value_type(
    10860                             ComPtr<IMedium>(medium), false));
    10861                 }
    10862 
    10863 
    10864                 /* no locks or callers here since there should be no way to
    10865                  * change the hard disk parent at this point (as it is still
    10866                  * attached to the machine) */
    10867                 medium = medium->getParent();
     10844                delete pMediumLockList;
     10845                mData->mSession.mLockedMedia.Clear();
     10846                break;
    1086810847            }
    1086910848        }
    1087010849
    10871         /* @todo r=dj is this correct? first restoring the eik and then throwing? */
    10872         eik.restore();
    10873         HRESULT rc2 = (HRESULT)mrc;
    10874         if (FAILED(rc2)) throw rc2;
    10875     }
    10876     catch (HRESULT aRC)
    10877     {
    10878         /* Unlock all locked media on failure */
    10879         unlockMedia();
    10880         return aRC;
    10881     }
    10882 
    10883     return S_OK;
     10850        HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
     10851        if (FAILED(rc))
     10852        {
     10853            mData->mSession.mLockedMedia.Clear();
     10854            mrc = setError(rc,
     10855                           tr("Collecting locking information for all attached media failed"));
     10856            break;
     10857        }
     10858    }
     10859
     10860    if (SUCCEEDED(mrc))
     10861    {
     10862        /* Now lock all media. If this fails, nothing is locked. */
     10863        HRESULT rc = mData->mSession.mLockedMedia.Lock();
     10864        if (FAILED(rc))
     10865        {
     10866            mrc = setError(rc,
     10867                           tr("Locking of attached media failed"));
     10868        }
     10869    }
     10870
     10871    return mrc;
    1088410872}
    1088510873
     
    1089810886    ErrorInfoKeeper eik;
    1089910887
    10900     HRESULT rc = S_OK;
    10901 
    10902     for (Data::Session::LockedMedia::const_iterator
    10903          it = mData->mSession.mLockedMedia.begin();
    10904          it != mData->mSession.mLockedMedia.end(); ++it)
    10905     {
    10906         MediumState_T state;
    10907         if (it->second)
    10908             rc = it->first->UnlockWrite(&state);
    10909         else
    10910             rc = it->first->UnlockRead(&state);
    10911 
    10912         /* The second can happen if an object was re-locked in
    10913          * Machine::fixupMedia(). The last can happen when e.g a DVD/Floppy
    10914          * image was unmounted at runtime. */
    10915         Assert(SUCCEEDED(rc) || state == MediumState_LockedRead || state == MediumState_Created);
    10916     }
    10917 
    10918     mData->mSession.mLockedMedia.clear();
     10888    HRESULT rc = mData->mSession.mLockedMedia.Clear();
     10889    AssertComRC(rc);
    1091910890}
    1092010891
     
    1102110992    {
    1102210993        /*
    11023          *  delete the saved state after Console::DiscardSavedState() is called
     10994         *  delete the saved state after Console::ForgetSavedState() is called
    1102410995         *  or if the VM process (owning a direct VM session) crashed while the
    1102510996         *  VM was Saved
     
    1115511126        /* directControl may be already set to NULL here in #OnSessionEnd()
    1115611127         * called too early by the direct session process while there is still
    11157          * some operation (like discarding the snapshot) in progress. The client
     11128         * some operation (like deleting the snapshot) in progress. The client
    1115811129         * process in this case is waiting inside Session::close() for the
    1115911130         * "end session" process object to complete, while #uninit() called by
  • trunk/src/VBox/Main/MediumImpl.cpp

    r27831 r28401  
    190190public:
    191191    Task(Medium *aMedium, Progress *aProgress)
    192         : mMedium(aMedium),
     192        : mVDOperationIfaces(NULL),
     193          m_pfNeedsSaveSettings(NULL),
     194          mMedium(aMedium),
    193195          mMediumCaller(aMedium),
    194           m_pfNeedsSaveSettings(NULL),
    195           mVDOperationIfaces(NULL),
    196196          mThread(NIL_RTTHREAD),
    197197          mProgress(aProgress)
     
    229229    bool isAsync() { return mThread != NIL_RTTHREAD; }
    230230
    231     const ComObjPtr<Medium> mMedium;
    232     AutoCaller mMediumCaller;
     231    PVDINTERFACE mVDOperationIfaces;
    233232
    234233    // Whether the caller needs to call VirtualBox::saveSettings() after
     
    237236    bool *m_pfNeedsSaveSettings;
    238237
    239     PVDINTERFACE mVDOperationIfaces;
     238    const ComObjPtr<Medium> mMedium;
     239    AutoCaller mMediumCaller;
     240
     241    friend HRESULT Medium::runNow(Medium::Task*, bool*);
    240242
    241243protected:
     
    279281                   Progress *aProgress,
    280282                   Medium *aTarget,
    281                    MediumVariant_T aVariant)
     283                   MediumVariant_T aVariant,
     284                   MediumLockList *aMediumLockList,
     285                   bool fKeepMediumLockList = false)
    282286        : Medium::Task(aMedium, aProgress),
     287          mpMediumLockList(aMediumLockList),
    283288          mTarget(aTarget),
    284289          mVariant(aVariant),
    285           mTargetCaller(aTarget)
     290          mTargetCaller(aTarget),
     291          mfKeepMediumLockList(fKeepMediumLockList)
    286292    {
    287293        AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
     
    291297    }
    292298
     299    ~CreateDiffTask()
     300    {
     301        if (!mfKeepMediumLockList && mpMediumLockList)
     302            delete mpMediumLockList;
     303    }
     304
     305    MediumLockList *mpMediumLockList;
     306
    293307    const ComObjPtr<Medium> mTarget;
    294308    MediumVariant_T mVariant;
     
    298312
    299313    AutoCaller mTargetCaller;
     314    bool mfKeepMediumLockList;
    300315};
    301316
     
    306321              Progress *aProgress,
    307322              Medium *aTarget,
     323              MediumVariant_T aVariant,
    308324              Medium *aParent,
    309               ImageChain *aSourceChain,
    310               ImageChain *aParentChain,
    311               MediumVariant_T aVariant)
     325              MediumLockList *aSourceMediumLockList,
     326              MediumLockList *aTargetMediumLockList,
     327              bool fKeepSourceMediumLockList = false,
     328              bool fKeepTargetMediumLockList = false)
    312329        : Medium::Task(aMedium, aProgress),
    313330          mTarget(aTarget),
    314331          mParent(aParent),
    315           mSourceChain(aSourceChain),
    316           mParentChain(aParentChain),
     332          mpSourceMediumLockList(aSourceMediumLockList),
     333          mpTargetMediumLockList(aTargetMediumLockList),
    317334          mVariant(aVariant),
    318335          mTargetCaller(aTarget),
    319           mParentCaller(aParent)
     336          mParentCaller(aParent),
     337          mfKeepSourceMediumLockList(fKeepSourceMediumLockList),
     338          mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
    320339    {
    321340        AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
     
    327346        if (FAILED(mRC))
    328347            return;
    329         AssertReturnVoidStmt(aSourceChain != NULL, mRC = E_FAIL);
    330         AssertReturnVoidStmt(aParentChain != NULL, mRC = E_FAIL);
     348        AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
     349        AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
     350    }
     351
     352    ~CloneTask()
     353    {
     354        if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
     355            delete mpSourceMediumLockList;
     356        if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
     357            delete mpTargetMediumLockList;
    331358    }
    332359
    333360    const ComObjPtr<Medium> mTarget;
    334361    const ComObjPtr<Medium> mParent;
    335     std::auto_ptr<ImageChain> mSourceChain;
    336     std::auto_ptr<ImageChain> mParentChain;
     362    MediumLockList *mpSourceMediumLockList;
     363    MediumLockList *mpTargetMediumLockList;
    337364    MediumVariant_T mVariant;
    338365
     
    342369    AutoCaller mTargetCaller;
    343370    AutoCaller mParentCaller;
     371    bool mfKeepSourceMediumLockList;
     372    bool mfKeepTargetMediumLockList;
    344373};
    345374
     
    349378    CompactTask(Medium *aMedium,
    350379                Progress *aProgress,
    351                 ImageChain *aImageChain)
     380                MediumLockList *aMediumLockList,
     381                bool fKeepMediumLockList = false)
    352382        : Medium::Task(aMedium, aProgress),
    353           mImageChain(aImageChain)
    354     {
    355         AssertReturnVoidStmt(aImageChain != NULL, mRC = E_FAIL);
    356     }
    357 
    358     std::auto_ptr<ImageChain> mImageChain;
     383          mpMediumLockList(aMediumLockList),
     384          mfKeepMediumLockList(fKeepMediumLockList)
     385    {
     386        AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
     387    }
     388
     389    ~CompactTask()
     390    {
     391        if (!mfKeepMediumLockList && mpMediumLockList)
     392            delete mpMediumLockList;
     393    }
     394
     395    MediumLockList *mpMediumLockList;
    359396
    360397private:
    361398    virtual HRESULT handler();
     399
     400    bool mfKeepMediumLockList;
    362401};
    363402
     
    366405public:
    367406    ResetTask(Medium *aMedium,
    368               Progress *aProgress)
    369         : Medium::Task(aMedium, aProgress)
     407              Progress *aProgress,
     408              MediumLockList *aMediumLockList,
     409              bool fKeepMediumLockList = false)
     410        : Medium::Task(aMedium, aProgress),
     411          mpMediumLockList(aMediumLockList),
     412          mfKeepMediumLockList(fKeepMediumLockList)
    370413    {}
     414
     415    ~ResetTask()
     416    {
     417        if (!mfKeepMediumLockList && mpMediumLockList)
     418            delete mpMediumLockList;
     419    }
     420
     421    MediumLockList *mpMediumLockList;
    371422
    372423private:
    373424    virtual HRESULT handler();
     425
     426    bool mfKeepMediumLockList;
    374427};
    375428
     
    378431public:
    379432    DeleteTask(Medium *aMedium,
    380                Progress *aProgress)
    381         : Medium::Task(aMedium, aProgress)
     433               Progress *aProgress,
     434               MediumLockList *aMediumLockList,
     435               bool fKeepMediumLockList = false)
     436        : Medium::Task(aMedium, aProgress),
     437          mpMediumLockList(aMediumLockList),
     438          mfKeepMediumLockList(fKeepMediumLockList)
    382439    {}
     440
     441    ~DeleteTask()
     442    {
     443        if (!mfKeepMediumLockList && mpMediumLockList)
     444            delete mpMediumLockList;
     445    }
     446
     447    MediumLockList *mpMediumLockList;
    383448
    384449private:
    385450    virtual HRESULT handler();
     451
     452    bool mfKeepMediumLockList;
    386453};
    387454
     
    390457public:
    391458    MergeTask(Medium *aMedium,
     459              Medium *aTarget,
     460              bool fMergeForward,
     461              Medium *aParentForTarget,
     462              const MediaList &aChildrenToReparent,
    392463              Progress *aProgress,
    393               MergeChain *aMergeChain)
     464              MediumLockList *aMediumLockList,
     465              bool fKeepMediumLockList = false)
    394466        : Medium::Task(aMedium, aProgress),
    395           mMergeChain(aMergeChain)
    396     {
    397         AssertReturnVoidStmt(aMergeChain != NULL, mRC = E_FAIL);
    398     }
    399 
    400     std::auto_ptr<MergeChain> mMergeChain;
     467          mTarget(aTarget),
     468          mfMergeForward(fMergeForward),
     469          mParentForTarget(aParentForTarget),
     470          mChildrenToReparent(aChildrenToReparent),
     471          mpMediumLockList(aMediumLockList),
     472          mTargetCaller(aTarget),
     473          mParentForTargetCaller(aParentForTarget),
     474          mfChildrenCaller(false),
     475          mfKeepMediumLockList(fKeepMediumLockList)
     476    {
     477        AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
     478        for (MediaList::const_iterator it = mChildrenToReparent.begin();
     479             it != mChildrenToReparent.end();
     480             ++it)
     481        {
     482            HRESULT rc2 = (*it)->addCaller();
     483            if (FAILED(rc2))
     484            {
     485                mRC = E_FAIL;
     486                for (MediaList::const_iterator it2 = mChildrenToReparent.begin();
     487                     it2 != it;
     488                     --it2)
     489                {
     490                    (*it2)->releaseCaller();
     491                }
     492                return;
     493            }
     494        }
     495        mfChildrenCaller = true;
     496    }
     497
     498    ~MergeTask()
     499    {
     500        if (!mfKeepMediumLockList && mpMediumLockList)
     501            delete mpMediumLockList;
     502        if (mfChildrenCaller)
     503        {
     504            for (MediaList::const_iterator it = mChildrenToReparent.begin();
     505                 it != mChildrenToReparent.end();
     506                 ++it)
     507            {
     508                (*it)->releaseCaller();
     509            }
     510        }
     511    }
     512
     513    const ComObjPtr<Medium> mTarget;
     514    bool mfMergeForward;
     515    /* When mChildrenToReparent is empty then mParentForTarget is non-null.
     516     * In other words: they are used in different cases. */
     517    const ComObjPtr<Medium> mParentForTarget;
     518    MediaList mChildrenToReparent;
     519    MediumLockList *mpMediumLockList;
    401520
    402521private:
    403522    virtual HRESULT handler();
     523
     524    AutoCaller mTargetCaller;
     525    AutoCaller mParentForTargetCaller;
     526    bool mfChildrenCaller;
     527    bool mfKeepMediumLockList;
    404528};
    405529
     
    469593HRESULT Medium::CreateBaseTask::handler()
    470594{
    471     return mMedium->taskThreadCreateBase(*this);
     595    return mMedium->taskCreateBaseHandler(*this);
    472596}
    473597
     
    477601HRESULT Medium::CreateDiffTask::handler()
    478602{
    479     return mMedium->taskThreadCreateDiff(*this);
     603    return mMedium->taskCreateDiffHandler(*this);
    480604}
    481605
     
    485609HRESULT Medium::CloneTask::handler()
    486610{
    487     return mMedium->taskThreadClone(*this);
     611    return mMedium->taskCloneHandler(*this);
    488612}
    489613
     
    493617HRESULT Medium::CompactTask::handler()
    494618{
    495     return mMedium->taskThreadCompact(*this);
     619    return mMedium->taskCompactHandler(*this);
    496620}
    497621
     
    501625HRESULT Medium::ResetTask::handler()
    502626{
    503     return mMedium->taskThreadReset(*this);
     627    return mMedium->taskResetHandler(*this);
    504628}
    505629
     
    509633HRESULT Medium::DeleteTask::handler()
    510634{
    511     return mMedium->taskThreadDelete(*this);
     635    return mMedium->taskDeleteHandler(*this);
    512636}
    513637
     
    517641HRESULT Medium::MergeTask::handler()
    518642{
    519     return mMedium->taskThreadMerge(*this);
    520 }
    521 
    522 
    523 ////////////////////////////////////////////////////////////////////////////////
    524 //
    525 // Merge chain class
    526 //
    527 ////////////////////////////////////////////////////////////////////////////////
    528 
    529 /**
    530  * Helper class for merge operations.
    531  *
    532  * @note It is assumed that when modifying methods of this class are called,
    533  *       the medium tree lock is held in read mode.
    534  */
    535 class Medium::MergeChain : public MediaList,
    536                            public com::SupportErrorInfoBase
    537 {
    538 public:
    539 
    540     MergeChain(bool aForward, bool aIgnoreAttachments)
    541         : mForward(aForward)
    542         , mIgnoreAttachments(aIgnoreAttachments) {}
    543 
    544     ~MergeChain()
    545     {
    546         for (iterator it = mChildren.begin(); it != mChildren.end(); ++ it)
    547         {
    548             HRESULT rc = (*it)->UnlockWrite(NULL);
    549             AssertComRC(rc);
    550 
    551             (*it)->releaseCaller();
    552         }
    553 
    554         for (iterator it = begin(); it != end(); ++ it)
    555         {
    556             AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS);
    557             Assert((*it)->m->state == MediumState_LockedWrite ||
    558                    (*it)->m->state == MediumState_LockedRead ||
    559                    (*it)->m->state == MediumState_Deleting);
    560             if ((*it)->m->state == MediumState_LockedWrite)
    561                 (*it)->UnlockWrite(NULL);
    562             else if ((*it)->m->state == MediumState_LockedRead)
    563                 (*it)->UnlockRead(NULL);
    564             else
    565                 (*it)->m->state = MediumState_Created;
    566 
    567             (*it)->releaseCaller();
    568         }
    569 
    570         if (!mParent.isNull())
    571             mParent->releaseCaller();
    572     }
    573 
    574     HRESULT addSource(Medium *aMedium)
    575     {
    576         HRESULT rc = aMedium->addCaller();
    577         if (FAILED(rc)) return rc;
    578 
    579         AutoWriteLock alock(aMedium COMMA_LOCKVAL_SRC_POS);
    580 
    581         if (mForward)
    582         {
    583             rc = checkChildrenAndAttachmentsAndImmutable(aMedium);
    584             if (FAILED(rc))
    585             {
    586                 aMedium->releaseCaller();
    587                 return rc;
    588             }
    589         }
    590 
    591         /* We have to fetch the state with the COM method, cause it's possible
    592            that the medium isn't fully initialized yet. */
    593         MediumState_T m;
    594         rc = aMedium->RefreshState(&m);
    595         if (FAILED(rc)) return rc;
    596         /* go to Deleting */
    597         switch (m)
    598         {
    599             case MediumState_Created:
    600                 aMedium->m->state = MediumState_Deleting;
    601                 break;
    602             default:
    603                 aMedium->releaseCaller();
    604                 return aMedium->setStateError();
    605         }
    606 
    607         push_front(aMedium);
    608 
    609         if (mForward)
    610         {
    611             /* we will need parent to reparent target */
    612             if (!aMedium->m->pParent.isNull())
    613             {
    614                 rc = aMedium->m->pParent->addCaller();
    615                 if (FAILED(rc)) return rc;
    616 
    617                 mParent = aMedium->m->pParent;
    618             }
    619 
    620             /* Include all images from base to source. */
    621             ComObjPtr<Medium> pParent = aMedium->m->pParent;
    622             while (!pParent.isNull())
    623             {
    624                 rc = pParent->addCaller();
    625                 if (FAILED(rc)) return rc;
    626 
    627                 rc = pParent->LockRead(NULL);
    628                 if (FAILED(rc)) return rc;
    629 
    630                 push_front(pParent);
    631                 pParent = pParent->m->pParent;
    632             }
    633         }
    634         else
    635         {
    636             /* we will need to reparent children */
    637             for (MediaList::const_iterator it = aMedium->getChildren().begin();
    638                  it != aMedium->getChildren().end();
    639                  ++it)
    640             {
    641                 ComObjPtr<Medium> pMedium = *it;
    642                 rc = pMedium->addCaller();
    643                 if (FAILED(rc)) return rc;
    644 
    645                 rc = pMedium->LockWrite(NULL);
    646                 if (FAILED(rc))
    647                 {
    648                     pMedium->releaseCaller();
    649                     return rc;
    650                 }
    651 
    652                 mChildren.push_back(pMedium);
    653             }
    654         }
    655 
    656         mSource = aMedium;
    657 
    658         return S_OK;
    659     }
    660 
    661     HRESULT addTarget(Medium *aMedium)
    662     {
    663         HRESULT rc = aMedium->addCaller();
    664         if (FAILED(rc)) return rc;
    665 
    666         AutoWriteLock alock(aMedium COMMA_LOCKVAL_SRC_POS);
    667 
    668         if (!mForward)
    669         {
    670             rc = checkChildrenAndImmutable(aMedium);
    671             if (FAILED(rc))
    672             {
    673                 aMedium->releaseCaller();
    674                 return rc;
    675             }
    676         }
    677 
    678         /* go to LockedWrite */
    679         rc = aMedium->LockWrite(NULL);
    680         if (FAILED(rc))
    681         {
    682             aMedium->releaseCaller();
    683             return rc;
    684         }
    685 
    686         push_front(aMedium);
    687 
    688         mTarget = aMedium;
    689 
    690         return S_OK;
    691     }
    692 
    693     HRESULT addIntermediate(Medium *aMedium)
    694     {
    695         HRESULT rc = aMedium->addCaller();
    696         if (FAILED(rc)) return rc;
    697 
    698         AutoWriteLock alock(aMedium COMMA_LOCKVAL_SRC_POS);
    699 
    700         rc = checkChildrenAndAttachments(aMedium);
    701         if (FAILED(rc))
    702         {
    703             aMedium->releaseCaller();
    704             return rc;
    705         }
    706 
    707         /* go to Deleting */
    708         switch (aMedium->m->state)
    709         {
    710             case MediumState_Created:
    711                 aMedium->m->state = MediumState_Deleting;
    712                 break;
    713             default:
    714                 aMedium->releaseCaller();
    715                 return aMedium->setStateError();
    716         }
    717 
    718         push_front(aMedium);
    719 
    720         return S_OK;
    721     }
    722 
    723     int targetIdx()
    724     {
    725         Assert(!mTarget.isNull());
    726         int idx = 0;
    727 
    728         for (MediaList::const_iterator it = begin(); it != end(); ++it)
    729         {
    730             ComObjPtr<Medium> pMedium = *it;
    731 
    732             /* Do we have the target? */
    733             if (pMedium == mTarget)
    734                 break;
    735 
    736             idx++;
    737         }
    738 
    739         return idx;
    740     }
    741 
    742     int sourceIdx()
    743     {
    744         Assert(!mSource.isNull());
    745         int idx = 0;
    746 
    747         for (MediaList::const_iterator it = begin(); it != end(); ++it)
    748         {
    749             ComObjPtr<Medium> pMedium = *it;
    750 
    751             /* Do we have the source? */
    752             if (pMedium == mSource)
    753                 break;
    754 
    755             idx++;
    756         }
    757 
    758         return idx;
    759     }
    760 
    761     bool isForward() const { return mForward; }
    762     Medium *parent() const { return mParent; }
    763     const MediaList& children() const { return mChildren; }
    764 
    765     Medium *source() const
    766     { AssertReturn(size() > 0, NULL); return mSource; }
    767 
    768     Medium *target() const
    769     { AssertReturn(size() > 0, NULL); return mTarget; }
    770 
    771 protected:
    772 
    773     // SupportErrorInfoBase interface
    774     const GUID &mainInterfaceID() const { return COM_IIDOF(IMedium); }
    775     const char *componentName() const { return Medium::ComponentName(); }
    776 
    777 private:
    778 
    779     HRESULT check(Medium *aMedium, bool aChildren, bool aAttachments,
    780                   bool aImmutable)
    781     {
    782         if (aChildren)
    783         {
    784             /* not going to multi-merge as it's too expensive */
    785             if (aMedium->getChildren().size() > 1)
    786             {
    787                 return setError(E_FAIL,
    788                                 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
    789                                 aMedium->m->strLocationFull.raw(),
    790                                 aMedium->getChildren().size());
    791             }
    792         }
    793 
    794         if (aAttachments && !mIgnoreAttachments)
    795         {
    796             if (aMedium->m->backRefs.size() != 0)
    797                 return setError(E_FAIL,
    798                                 tr("Medium '%s' is attached to %d virtual machines"),
    799                                 aMedium->m->strLocationFull.raw(),
    800                                 aMedium->m->backRefs.size());
    801         }
    802 
    803         if (aImmutable)
    804         {
    805             if (aMedium->m->type == MediumType_Immutable)
    806                 return setError(E_FAIL,
    807                                 tr("Medium '%s' is immutable"),
    808                                 aMedium->m->strLocationFull.raw());
    809         }
    810 
    811         return S_OK;
    812     }
    813 
    814     HRESULT checkChildren(Medium *aMedium)
    815     { return check(aMedium, true, false, false); }
    816 
    817     HRESULT checkChildrenAndImmutable(Medium *aMedium)
    818     { return check(aMedium, true, false, true); }
    819 
    820     HRESULT checkChildrenAndAttachments(Medium *aMedium)
    821     { return check(aMedium, true, true, false); }
    822 
    823     HRESULT checkChildrenAndAttachmentsAndImmutable(Medium *aMedium)
    824     { return check(aMedium, true, true, true); }
    825 
    826     /** true if forward merge, false if backward */
    827     bool mForward : 1;
    828     /** true to not perform attachment checks */
    829     bool mIgnoreAttachments : 1;
    830 
    831     /** Parent of the source when forward merge (if any) */
    832     ComObjPtr <Medium> mParent;
    833     /** Children of the source when backward merge (if any) */
    834     MediaList mChildren;
    835     /** Source image */
    836     ComObjPtr <Medium> mSource;
    837     /** Target image */
    838     ComObjPtr <Medium> mTarget;
    839 };
    840 
    841 ////////////////////////////////////////////////////////////////////////////////
    842 //
    843 // ImageChain class
    844 //
    845 ////////////////////////////////////////////////////////////////////////////////
    846 
    847 /**
    848  * Helper class for image operations involving the entire parent chain.
    849  *
    850  * @note It is assumed that when modifying methods of this class are called,
    851  *       the medium tree lock is held in read mode.
    852  */
    853 class Medium::ImageChain : public MediaList,
    854                            public com::SupportErrorInfoBase
    855 {
    856 public:
    857 
    858     ImageChain() {}
    859 
    860     ~ImageChain()
    861     {
    862         /* empty? */
    863         if (begin() != end())
    864         {
    865             MediaList::const_iterator last = end();
    866             last--;
    867             for (MediaList::const_iterator it = begin(); it != end(); ++ it)
    868             {
    869                 AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS);
    870                 if (it == last)
    871                 {
    872                     Assert(   (*it)->m->state == MediumState_LockedRead
    873                            || (*it)->m->state == MediumState_LockedWrite);
    874                     if ((*it)->m->state == MediumState_LockedRead)
    875                         (*it)->UnlockRead(NULL);
    876                     else if ((*it)->m->state == MediumState_LockedWrite)
    877                         (*it)->UnlockWrite(NULL);
    878                 }
    879                 else
    880                 {
    881                     Assert((*it)->m->state == MediumState_LockedRead);
    882                     if ((*it)->m->state == MediumState_LockedRead)
    883                         (*it)->UnlockRead(NULL);
    884                 }
    885 
    886                 (*it)->releaseCaller();
    887             }
    888         }
    889     }
    890 
    891     HRESULT addImage(Medium *aMedium)
    892     {
    893         HRESULT rc = aMedium->addCaller();
    894         if (FAILED(rc)) return rc;
    895 
    896         push_front(aMedium);
    897 
    898         return S_OK;
    899     }
    900 
    901     HRESULT lockImagesRead()
    902     {
    903         /* Lock all disks in the chain in {parent, child} order,
    904          * and make sure they are accessible. */
    905         /// @todo code duplication with SessionMachine::lockMedia, see below
    906         ErrorInfoKeeper eik(true /* aIsNull */);
    907         MultiResult mrc(S_OK);
    908         for (MediaList::const_iterator it = begin(); it != end(); ++ it)
    909         {
    910             HRESULT rc = S_OK;
    911             MediumState_T mediumState = (*it)->getState();
    912 
    913             /* accessibility check must be first, otherwise locking
    914              * interferes with getting the medium state. */
    915             if (mediumState == MediumState_Inaccessible)
    916             {
    917                 rc = (*it)->RefreshState(&mediumState);
    918                 if (FAILED(rc)) return rc;
    919 
    920                 if (mediumState == MediumState_Inaccessible)
    921                 {
    922                     Bstr error;
    923                     rc = (*it)->COMGETTER(LastAccessError)(error.asOutParam());
    924                     if (FAILED(rc)) return rc;
    925 
    926                     Bstr loc;
    927                     rc = (*it)->COMGETTER(Location)(loc.asOutParam());
    928                     if (FAILED(rc)) return rc;
    929 
    930                     /* collect multiple errors */
    931                     eik.restore();
    932 
    933                     /* be in sync with Medium::setStateError() */
    934                     Assert(!error.isEmpty());
    935                     mrc = setError(E_FAIL,
    936                                    tr("Medium '%ls' is not accessible. %ls"),
    937                                    loc.raw(), error.raw());
    938 
    939                     eik.fetch();
    940                 }
    941             }
    942 
    943             rc = (*it)->LockRead(&mediumState);
    944             if (FAILED(rc)) return rc;
    945         }
    946 
    947         eik.restore();
    948         HRESULT rc2 = (HRESULT)mrc;
    949         if (FAILED(rc2)) return rc2;
    950 
    951         return S_OK;
    952     }
    953 
    954     HRESULT lockImagesReadAndLastWrite()
    955     {
    956         /* Lock all disks in the chain in {parent, child} order,
    957          * and make sure they are accessible. */
    958         /// @todo code duplication with SessionMachine::lockMedia, see below
    959         ErrorInfoKeeper eik(true /* aIsNull */);
    960         MultiResult mrc(S_OK);
    961         MediaList::const_iterator last = end();
    962         last--;
    963         for (MediaList::const_iterator it = begin(); it != end(); ++ it)
    964         {
    965             HRESULT rc = S_OK;
    966             MediumState_T mediumState = (*it)->getState();
    967 
    968             /* accessibility check must be first, otherwise locking
    969              * interferes with getting the medium state. */
    970             if (mediumState == MediumState_Inaccessible)
    971             {
    972                 rc = (*it)->RefreshState(&mediumState);
    973                 if (FAILED(rc)) return rc;
    974 
    975                 if (mediumState == MediumState_Inaccessible)
    976                 {
    977                     Bstr error;
    978                     rc = (*it)->COMGETTER(LastAccessError)(error.asOutParam());
    979                     if (FAILED(rc)) return rc;
    980 
    981                     Bstr loc;
    982                     rc = (*it)->COMGETTER(Location)(loc.asOutParam());
    983                     if (FAILED(rc)) return rc;
    984 
    985                     /* collect multiple errors */
    986                     eik.restore();
    987 
    988                     /* be in sync with Medium::setStateError() */
    989                     Assert(!error.isEmpty());
    990                     mrc = setError(E_FAIL,
    991                                    tr("Medium '%ls' is not accessible. %ls"),
    992                                    loc.raw(), error.raw());
    993 
    994                     eik.fetch();
    995                 }
    996             }
    997 
    998             if (it == last)
    999                 rc = (*it)->LockWrite(&mediumState);
    1000             else
    1001                 rc = (*it)->LockRead(&mediumState);
    1002         }
    1003 
    1004         eik.restore();
    1005         HRESULT rc2 = (HRESULT)mrc;
    1006         if (FAILED(rc2)) return rc2;
    1007 
    1008         return S_OK;
    1009     }
    1010 
    1011 protected:
    1012 
    1013     // SupportErrorInfoBase interface
    1014     const GUID &mainInterfaceID() const { return COM_IIDOF(IMedium); }
    1015     const char *componentName() const { return Medium::ComponentName(); }
    1016 
    1017 private:
    1018 
    1019 };
     643    return mMedium->taskMergeHandler(*this);
     644}
    1020645
    1021646
     
    1345970     * XML format version change if we wish) */
    1346971    for (settings::PropertiesMap::const_iterator it = data.properties.begin();
    1347          it != data.properties.end(); ++ it)
     972         it != data.properties.end(); ++it)
    1348973    {
    1349974        const Utf8Str &name = it->first;
     
    14961121        /* we are being uninitialized after've been deleted by merge.
    14971122         * Reparenting has already been done so don't touch it here (we are
    1498          * now orphans and remoeDependentChild() will assert) */
     1123         * now orphans and removeDependentChild() will assert) */
    14991124        Assert(m->pParent.isNull());
    15001125    }
     
    19571582        size_t i = 0;
    19581583        for (BackRefList::const_iterator it = m->backRefs.begin();
    1959              it != m->backRefs.end(); ++ it, ++ i)
     1584             it != m->backRefs.end(); ++it, ++i)
    19601585        {
    19611586             it->machineId.toUtf16().detachTo(&machineIds[i]);
     
    20131638    Guid id(aMachineId);
    20141639    for (BackRefList::const_iterator it = m->backRefs.begin();
    2015          it != m->backRefs.end(); ++ it)
     1640         it != m->backRefs.end(); ++it)
    20161641    {
    20171642        if (it->machineId == id)
     
    21351760            LogFlowThisFunc(("Failing - state=%d\n", m->state));
    21361761            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    2137                           tr ("Medium '%s' is not locked for reading"),
     1762                          tr("Medium '%s' is not locked for reading"),
    21381763                          m->strLocationFull.raw());
    21391764            break;
     
    22201845            LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
    22211846            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    2222                           tr ("Medium '%s' is not locked for writing"),
     1847                          tr("Medium '%s' is not locked for writing"),
    22231848                          m->strLocationFull.raw());
    22241849            break;
     
    24332058    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    24342059
    2435     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2436 
    2437     aVariant = (MediumVariant_T)((unsigned)aVariant & (unsigned)~MediumVariant_Diff);
    2438     if (    !(aVariant & MediumVariant_Fixed)
    2439         &&  !(m->formatObj->capabilities() & MediumFormatCapabilities_CreateDynamic))
    2440         return setError(VBOX_E_NOT_SUPPORTED,
    2441                         tr("Hard disk format '%s' does not support dynamic storage creation"),
    2442                         m->strFormat.raw());
    2443     if (    (aVariant & MediumVariant_Fixed)
    2444         &&  !(m->formatObj->capabilities() & MediumFormatCapabilities_CreateDynamic))
    2445         return setError(VBOX_E_NOT_SUPPORTED,
    2446                         tr("Hard disk format '%s' does not support fixed storage creation"),
    2447                         m->strFormat.raw());
    2448 
    2449     switch (m->state)
    2450     {
    2451         case MediumState_NotCreated:
    2452             break;
    2453         default:
    2454             return setStateError();
    2455     }
    2456 
    2457     ComObjPtr <Progress> progress;
    2458     progress.createObject();
    2459     HRESULT rc = progress->init(m->pVirtualBox,
    2460                                 static_cast<IMedium*>(this),
    2461                                 (aVariant & MediumVariant_Fixed)
    2462                                         ? BstrFmt(tr("Creating fixed hard disk storage unit '%s'"), m->strLocationFull.raw())
    2463                                         : BstrFmt(tr("Creating dynamic hard disk storage unit '%s'"), m->strLocationFull.raw()),
    2464                                 TRUE /* aCancelable */);
    2465     if (FAILED(rc)) return rc;
    2466 
    2467     /* setup task object to carry out the operation asynchronously */
    2468     Medium::Task *pTask(new Medium::CreateBaseTask(this, progress,
    2469                                                    aLogicalSize, aVariant));
    2470     rc = pTask->rc();
    2471     if (FAILED(rc))
    2472     {
     2060    HRESULT rc = S_OK;
     2061    ComObjPtr <Progress> pProgress;
     2062    Medium::Task *pTask = NULL;
     2063
     2064    try
     2065    {
     2066        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     2067
     2068        aVariant = (MediumVariant_T)((unsigned)aVariant & (unsigned)~MediumVariant_Diff);
     2069        if (    !(aVariant & MediumVariant_Fixed)
     2070            &&  !(m->formatObj->capabilities() & MediumFormatCapabilities_CreateDynamic))
     2071            throw setError(VBOX_E_NOT_SUPPORTED,
     2072                           tr("Hard disk format '%s' does not support dynamic storage creation"),
     2073                           m->strFormat.raw());
     2074        if (    (aVariant & MediumVariant_Fixed)
     2075            &&  !(m->formatObj->capabilities() & MediumFormatCapabilities_CreateDynamic))
     2076            throw setError(VBOX_E_NOT_SUPPORTED,
     2077                           tr("Hard disk format '%s' does not support fixed storage creation"),
     2078                           m->strFormat.raw());
     2079
     2080        if (m->state != MediumState_NotCreated)
     2081            throw setStateError();
     2082
     2083        pProgress.createObject();
     2084        rc = pProgress->init(m->pVirtualBox,
     2085                             static_cast<IMedium*>(this),
     2086                             (aVariant & MediumVariant_Fixed)
     2087                               ? BstrFmt(tr("Creating fixed hard disk storage unit '%s'"), m->strLocationFull.raw())
     2088                               : BstrFmt(tr("Creating dynamic hard disk storage unit '%s'"), m->strLocationFull.raw()),
     2089                             TRUE /* aCancelable */);
     2090        if (FAILED(rc)) throw rc;
     2091
     2092        /* setup task object to carry out the operation asynchronously */
     2093        pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize,
     2094                                           aVariant);
     2095        rc = pTask->rc();
    24732096        AssertComRC(rc);
     2097        if (FAILED(rc)) throw rc;
     2098
     2099        m->state = MediumState_Creating;
     2100    }
     2101    catch (HRESULT aRC) { rc = aRC; }
     2102
     2103    if (SUCCEEDED(rc))
     2104    {
     2105        rc = startThread(pTask);
     2106
     2107        if (SUCCEEDED(rc))
     2108            pProgress.queryInterfaceTo(aProgress);
     2109    }
     2110    else if (pTask != NULL)
    24742111        delete pTask;
    2475         return rc;
    2476     }
    2477 
    2478     rc = startThread(pTask);
    2479     if (FAILED(rc)) return rc;
    2480 
    2481     /* go to Creating state on success */
    2482     m->state = MediumState_Creating;
    2483 
    2484     progress.queryInterfaceTo(aProgress);
    2485 
    2486     return S_OK;
     2112
     2113    return rc;
    24872114}
    24882115
     
    24942121    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    24952122
    2496     ComObjPtr <Progress> progress;
    2497 
    2498     HRESULT rc = deleteStorageNoWait(progress);
     2123    ComObjPtr <Progress> pProgress;
     2124
     2125    HRESULT rc = deleteStorage(&pProgress, false /* aWait */,
     2126                               NULL /* pfNeedsSaveSettings */);
    24992127    if (SUCCEEDED(rc))
    2500         progress.queryInterfaceTo(aProgress);
     2128        pProgress.queryInterfaceTo(aProgress);
    25012129
    25022130    return rc;
     
    25222150                        m->strLocationFull.raw());
    25232151
    2524     /* We want to be locked for reading as long as our diff child is being
    2525      * created */
    2526     HRESULT rc = LockRead(NULL);
    2527     if (FAILED(rc)) return rc;
    2528 
    2529     ComObjPtr <Progress> progress;
    2530 
    2531     rc = createDiffStorageNoWait(diff, aVariant, progress);
     2152    /* Apply the normal locking logic to the entire chain. */
     2153    MediumLockList *pMediumLockList(new MediumLockList());
     2154    HRESULT rc = createMediumLockList(true, this, *pMediumLockList);
    25322155    if (FAILED(rc))
    25332156    {
    2534         HRESULT rc2 = UnlockRead(NULL);
    2535         AssertComRC(rc2);
    2536         /* Note: on success, the task will unlock this */
    2537     }
     2157        delete pMediumLockList;
     2158        return rc;
     2159    }
     2160
     2161    ComObjPtr <Progress> pProgress;
     2162
     2163    rc = createDiffStorage(diff, aVariant, pMediumLockList, &pProgress,
     2164                           false /* aWait */, NULL /* pfNeedsSaveSettings*/);
     2165    if (FAILED(rc))
     2166        delete pMediumLockList;
    25382167    else
    2539         progress.queryInterfaceTo(aProgress);
     2168        pProgress.queryInterfaceTo(aProgress);
    25402169
    25412170    return rc;
    25422171}
    25432172
    2544 STDMETHODIMP Medium::MergeTo(IN_BSTR /* aTargetId */, IProgress ** /* aProgress */)
    2545 {
     2173STDMETHODIMP Medium::MergeTo(IMedium *aTarget, IProgress **aProgress)
     2174{
     2175    CheckComArgNotNull(aTarget);
     2176    CheckComArgOutPointerValid(aProgress);
     2177    ComAssertRet(aTarget != this, E_INVALIDARG);
     2178
    25462179    AutoCaller autoCaller(this);
    25472180    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    25482181
    2549     ReturnComNotImplemented();
     2182    ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget);
     2183
     2184    bool fMergeForward = false;
     2185    ComObjPtr<Medium> pParentForTarget;
     2186    MediaList childrenToReparent;
     2187    MediumLockList *pMediumLockList = NULL;
     2188
     2189    HRESULT rc = S_OK;
     2190
     2191    rc = prepareMergeTo(pTarget, NULL, NULL, fMergeForward, pParentForTarget,
     2192                        childrenToReparent, pMediumLockList);
     2193    if (FAILED(rc)) return rc;
     2194
     2195    ComObjPtr <Progress> pProgress;
     2196
     2197    rc = mergeTo(pTarget, fMergeForward, pParentForTarget, childrenToReparent,
     2198                 pMediumLockList, &pProgress, false /* aWait */,
     2199                 NULL /* pfNeedsSaveSettings */);
     2200    if (FAILED(rc))
     2201        cancelMergeTo(childrenToReparent, pMediumLockList);
     2202    else
     2203        pProgress.queryInterfaceTo(aProgress);
     2204
     2205    return rc;
    25502206}
    25512207
     
    25572213    CheckComArgNotNull(aTarget);
    25582214    CheckComArgOutPointerValid(aProgress);
     2215    ComAssertRet(aTarget != this, E_INVALIDARG);
    25592216
    25602217    AutoCaller autoCaller(this);
    25612218    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    25622219
    2563     ComObjPtr<Medium> target = static_cast<Medium*>(aTarget);
    2564     ComObjPtr<Medium> parent;
     2220    ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget);
     2221    ComObjPtr<Medium> pParent;
    25652222    if (aParent)
    2566         parent = static_cast<Medium*>(aParent);
    2567 
    2568     ComObjPtr<Progress> progress;
     2223        pParent = static_cast<Medium*>(aParent);
     2224
    25692225    HRESULT rc = S_OK;
     2226    ComObjPtr<Progress> pProgress;
     2227    Medium::Task *pTask = NULL;
    25702228
    25712229    try
     
    25742232        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    25752233        // and we need to write-lock the images involved
    2576         AutoMultiWriteLock3 alock(this, target, parent COMMA_LOCKVAL_SRC_POS);
    2577 
    2578         if (    target->m->state != MediumState_NotCreated
    2579             &&  target->m->state != MediumState_Created)
    2580             throw target->setStateError();
    2581 
    2582         /** @todo separate out creating/locking an image chain from
    2583          * SessionMachine::lockMedia and use it from here too.
    2584          * logically this belongs into Medium functionality. */
    2585 
    2586         /* Build the source chain and lock images in the proper order. */
    2587         std::auto_ptr<ImageChain> sourceChain(new ImageChain());
    2588 
    2589         for (Medium *hd = this;
    2590              hd;
    2591              hd = hd->m->pParent)
    2592         {
    2593             rc = sourceChain->addImage(hd);
    2594             if (FAILED(rc)) throw rc;
    2595         }
    2596         rc = sourceChain->lockImagesRead();
     2234        AutoMultiWriteLock3 alock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
     2235
     2236        if (    pTarget->m->state != MediumState_NotCreated
     2237            &&  pTarget->m->state != MediumState_Created)
     2238            throw pTarget->setStateError();
     2239
     2240        /* Build the source lock list. */
     2241        MediumLockList *pSourceMediumLockList(new MediumLockList());
     2242        rc = createMediumLockList(false, NULL,
     2243                                  *pSourceMediumLockList);
     2244        if (FAILED(rc))
     2245        {
     2246            delete pSourceMediumLockList;
     2247            throw rc;
     2248        }
     2249
     2250        /* Build the target lock list (including the to-be parent chain). */
     2251        MediumLockList *pTargetMediumLockList(new MediumLockList());
     2252        rc = pTarget->createMediumLockList(true, pParent,
     2253                                           *pTargetMediumLockList);
     2254        if (FAILED(rc))
     2255        {
     2256            delete pSourceMediumLockList;
     2257            delete pTargetMediumLockList;
     2258            throw rc;
     2259        }
     2260
     2261        rc = pSourceMediumLockList->Lock();
     2262        if (FAILED(rc))
     2263        {
     2264            delete pSourceMediumLockList;
     2265            delete pTargetMediumLockList;
     2266            throw setError(rc,
     2267                           tr("Failed to lock source media '%ls'"),
     2268                           getLocationFull().raw());
     2269        }
     2270        rc = pTargetMediumLockList->Lock();
     2271        if (FAILED(rc))
     2272        {
     2273            delete pSourceMediumLockList;
     2274            delete pTargetMediumLockList;
     2275            throw setError(rc,
     2276                           tr("Failed to lock target media '%ls'"),
     2277                           pTarget->getLocationFull().raw());
     2278        }
     2279
     2280        pProgress.createObject();
     2281        rc = pProgress->init(m->pVirtualBox,
     2282                             static_cast <IMedium *>(this),
     2283                             BstrFmt(tr("Creating clone hard disk '%s'"), pTarget->m->strLocationFull.raw()),
     2284                             TRUE /* aCancelable */);
     2285        if (FAILED(rc))
     2286        {
     2287            delete pSourceMediumLockList;
     2288            delete pTargetMediumLockList;
     2289            throw rc;
     2290        }
     2291
     2292        /* setup task object to carry out the operation asynchronously */
     2293        pTask = new Medium::CloneTask(this, pProgress, pTarget, aVariant,
     2294                                      pParent, pSourceMediumLockList,
     2295                                      pTargetMediumLockList);
     2296        rc = pTask->rc();
     2297        AssertComRC(rc);
    25972298        if (FAILED(rc)) throw rc;
    25982299
    2599         /* Build the parent chain and lock images in the proper order. */
    2600         std::auto_ptr<ImageChain> parentChain(new ImageChain());
    2601 
    2602         for (Medium *hd = parent;
    2603              hd;
    2604              hd = hd->m->pParent)
    2605         {
    2606             rc = parentChain->addImage(hd);
    2607             if (FAILED(rc)) throw rc;
    2608         }
    2609         if (target->m->state == MediumState_Created)
    2610         {
    2611             /* If we're cloning to an existing image the parent chain also
    2612              * contains the target image, and it gets locked for writing. */
    2613             rc = parentChain->addImage(target);
    2614             if (FAILED(rc)) throw rc;
    2615             rc = parentChain->lockImagesReadAndLastWrite();
    2616             if (FAILED(rc)) throw rc;
    2617         }
    2618         else
    2619         {
    2620             rc = parentChain->lockImagesRead();
    2621             if (FAILED(rc)) throw rc;
    2622         }
    2623 
    2624         progress.createObject();
    2625         rc = progress->init(m->pVirtualBox,
    2626                             static_cast <IMedium *>(this),
    2627                             BstrFmt(tr("Creating clone hard disk '%s'"), target->m->strLocationFull.raw()),
    2628                             TRUE /* aCancelable */);
    2629         if (FAILED(rc)) throw rc;
    2630 
    2631         /* setup task object to carry out the operation asynchronously */
    2632         Medium::Task *pTask(new Medium::CloneTask(this, progress, target,
    2633                                                   parent,
    2634                                                   sourceChain.release(),
    2635                                                   parentChain.release(),
    2636                                                   aVariant));
    2637         rc = pTask->rc();
    2638         if (FAILED(rc))
    2639         {
    2640             AssertComRC(rc);
    2641             delete pTask;
    2642             throw rc;
    2643         }
    2644 
     2300        if (pTarget->m->state == MediumState_NotCreated)
     2301            pTarget->m->state = MediumState_Creating;
     2302    }
     2303    catch (HRESULT aRC) { rc = aRC; }
     2304
     2305    if (SUCCEEDED(rc))
     2306    {
    26452307        rc = startThread(pTask);
    2646         if (FAILED(rc)) throw rc;
    2647 
    2648         if (target->m->state == MediumState_NotCreated)
    2649         {
    2650             /* go to Creating state before leaving the lock */
    2651             target->m->state = MediumState_Creating;
    2652         }
    2653     }
    2654     catch (HRESULT aRC)
    2655     {
    2656         rc = aRC;
    2657     }
    2658 
    2659     if (SUCCEEDED(rc))
    2660         progress.queryInterfaceTo(aProgress);
     2308
     2309        if (SUCCEEDED(rc))
     2310            pProgress.queryInterfaceTo(aProgress);
     2311    }
     2312    else if (pTask != NULL)
     2313        delete pTask;
    26612314
    26622315    return rc;
     
    26702323    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    26712324
    2672     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2673 
    2674     ComObjPtr <Progress> progress;
    2675 
    26762325    HRESULT rc = S_OK;
     2326    ComObjPtr <Progress> pProgress;
     2327    Medium::Task *pTask = NULL;
    26772328
    26782329    try
    26792330    {
    2680         /** @todo separate out creating/locking an image chain from
    2681          * SessionMachine::lockMedia and use it from here too.
    2682          * logically this belongs into Medium functionality. */
    2683 
    2684         /* Build the image chain and lock images in the proper order. */
    2685         std::auto_ptr<ImageChain> imgChain(new ImageChain());
    2686 
    2687         /* we walk the image tree */
    2688         AutoReadLock srcTreeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    2689         for (Medium *hd = this;
    2690              hd;
    2691              hd = hd->m->pParent)
    2692         {
    2693             rc = imgChain->addImage(hd);
    2694             if (FAILED(rc)) throw rc;
    2695         }
    2696         rc = imgChain->lockImagesReadAndLastWrite();
     2331        /* We need to lock both the current object, and the tree lock (would
     2332         * cause a lock order violation otherwise) for createMediumLockList. */
     2333        AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
     2334                                      this->lockHandle()
     2335                                      COMMA_LOCKVAL_SRC_POS);
     2336
     2337        /* Build the medium lock list. */
     2338        MediumLockList *pMediumLockList(new MediumLockList());
     2339        rc = createMediumLockList(true, NULL,
     2340                                  *pMediumLockList);
     2341        if (FAILED(rc))
     2342        {
     2343            delete pMediumLockList;
     2344            throw rc;
     2345        }
     2346
     2347        rc = pMediumLockList->Lock();
     2348        if (FAILED(rc))
     2349        {
     2350            delete pMediumLockList;
     2351            throw setError(rc,
     2352                           tr("Failed to lock media when compacting '%ls'"),
     2353                           getLocationFull().raw());
     2354        }
     2355
     2356        pProgress.createObject();
     2357        rc = pProgress->init(m->pVirtualBox,
     2358                             static_cast <IMedium *>(this),
     2359                             BstrFmt(tr("Compacting hard disk '%s'"), m->strLocationFull.raw()),
     2360                             TRUE /* aCancelable */);
     2361        if (FAILED(rc))
     2362        {
     2363            delete pMediumLockList;
     2364            throw rc;
     2365        }
     2366
     2367        /* setup task object to carry out the operation asynchronously */
     2368        pTask = new Medium::CompactTask(this, pProgress, pMediumLockList);
     2369        rc = pTask->rc();
     2370        AssertComRC(rc);
    26972371        if (FAILED(rc)) throw rc;
    2698 
    2699         progress.createObject();
    2700         rc = progress->init(m->pVirtualBox,
    2701                             static_cast <IMedium *>(this),
    2702                             BstrFmt(tr("Compacting hard disk '%s'"), m->strLocationFull.raw()),
    2703                             TRUE /* aCancelable */);
    2704         if (FAILED(rc)) throw rc;
    2705 
    2706         /* setup task object to carry out the operation asynchronously */
    2707         Medium::Task *pTask(new Medium::CompactTask(this, progress,
    2708                                                     imgChain.release()));
    2709         rc = pTask->rc();
    2710         if (FAILED(rc))
    2711         {
    2712             AssertComRC(rc);
    2713             delete pTask;
    2714             throw rc;
    2715         }
    2716 
     2372    }
     2373    catch (HRESULT aRC) { rc = aRC; }
     2374
     2375    if (SUCCEEDED(rc))
     2376    {
    27172377        rc = startThread(pTask);
    2718         if (FAILED(rc)) throw rc;
    2719     }
    2720     catch (HRESULT aRC)
    2721     {
    2722         rc = aRC;
    2723     }
    2724 
    2725     if (SUCCEEDED(rc))
    2726         progress.queryInterfaceTo(aProgress);
     2378
     2379        if (SUCCEEDED(rc))
     2380            pProgress.queryInterfaceTo(aProgress);
     2381    }
     2382    else if (pTask != NULL)
     2383        delete pTask;
    27272384
    27282385    return rc;
     
    27482405    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    27492406
    2750     /* canClose() needs the tree lock */
    2751     AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
    2752                                   this->lockHandle()
    2753                                   COMMA_LOCKVAL_SRC_POS);
    2754 
    2755     LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
    2756 
    2757     if (m->pParent.isNull())
    2758         return setError(VBOX_E_NOT_SUPPORTED,
    2759                         tr ("Hard disk '%s' is not differencing"),
    2760                         m->strLocationFull.raw());
    2761 
    2762     HRESULT rc = canClose();
    2763     if (FAILED(rc)) return rc;
    2764 
    2765     rc = LockWrite(NULL);
    2766     if (FAILED(rc)) return rc;
    2767 
    2768     ComObjPtr <Progress> progress;
     2407    HRESULT rc = S_OK;
     2408    ComObjPtr <Progress> pProgress;
     2409    Medium::Task *pTask = NULL;
    27692410
    27702411    try
    27712412    {
    2772         progress.createObject();
    2773         rc = progress->init(m->pVirtualBox,
    2774                             static_cast<IMedium*>(this),
    2775                             BstrFmt(tr("Resetting differencing hard disk '%s'"), m->strLocationFull.raw()),
    2776                             FALSE /* aCancelable */);
     2413        /* canClose() needs the tree lock */
     2414        AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
     2415                                      this->lockHandle()
     2416                                      COMMA_LOCKVAL_SRC_POS);
     2417
     2418        LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
     2419
     2420        if (m->pParent.isNull())
     2421            throw setError(VBOX_E_NOT_SUPPORTED,
     2422                           tr("Hard disk '%s' is not differencing"),
     2423                           m->strLocationFull.raw());
     2424
     2425        rc = canClose();
    27772426        if (FAILED(rc)) throw rc;
    27782427
     2428        /* Build the medium lock list. */
     2429        MediumLockList *pMediumLockList(new MediumLockList());
     2430        rc = createMediumLockList(true, NULL,
     2431                                  *pMediumLockList);
     2432        if (FAILED(rc))
     2433        {
     2434            delete pMediumLockList;
     2435            throw rc;
     2436        }
     2437
     2438        rc = pMediumLockList->Lock();
     2439        if (FAILED(rc))
     2440        {
     2441            delete pMediumLockList;
     2442            throw setError(rc,
     2443                           tr("Failed to lock media when resetting '%ls'"),
     2444                           getLocationFull().raw());
     2445        }
     2446
     2447        pProgress.createObject();
     2448        rc = pProgress->init(m->pVirtualBox,
     2449                             static_cast<IMedium*>(this),
     2450                             BstrFmt(tr("Resetting differencing hard disk '%s'"), m->strLocationFull.raw()),
     2451                             FALSE /* aCancelable */);
     2452        if (FAILED(rc)) throw rc;
     2453
    27792454        /* setup task object to carry out the operation asynchronously */
    2780         Medium::Task *pTask(new Medium::ResetTask(this, progress));
     2455        pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
    27812456        rc = pTask->rc();
    2782         if (FAILED(rc))
    2783         {
    2784             AssertComRC(rc);
     2457        AssertComRC(rc);
     2458        if (FAILED(rc)) throw rc;
     2459    }
     2460    catch (HRESULT aRC) { rc = aRC; }
     2461
     2462    if (SUCCEEDED(rc))
     2463    {
     2464        rc = startThread(pTask);
     2465
     2466        if (SUCCEEDED(rc))
     2467            pProgress.queryInterfaceTo(aProgress);
     2468    }
     2469    else
     2470    {
     2471        /* Note: on success, the task will unlock this */
     2472        {
     2473            AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     2474            HRESULT rc2 = UnlockWrite(NULL);
     2475            AssertComRC(rc2);
     2476        }
     2477        if (pTask != NULL)
    27852478            delete pTask;
    2786             throw rc;
    2787         }
    2788 
    2789         rc = startThread(pTask);
    2790         if (FAILED(rc)) throw rc;
    2791     }
    2792     catch (HRESULT aRC)
    2793     {
    2794         rc = aRC;
    2795     }
    2796 
    2797     if (FAILED(rc))
    2798     {
    2799         HRESULT rc2 = UnlockWrite(NULL);
    2800         AssertComRC(rc2);
    2801         /* Note: on success, the task will unlock this */
    2802     }
    2803     else
    2804         progress.queryInterfaceTo(aProgress);
     2479    }
    28052480
    28062481    LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
     
    31262801    AssertComRCReturnVoid(autoCaller.rc());
    31272802
    3128     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3129 
    31302803    /* we access children() */
    31312804    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     2805
     2806    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    31322807
    31332808    updatePath(aOldPath, aNewPath);
     
    31362811    for (MediaList::const_iterator it = getChildren().begin();
    31372812         it != getChildren().end();
    3138          ++ it)
     2813         ++it)
    31392814    {
    31402815        (*it)->updatePaths(aOldPath, aNewPath);
     
    32142889
    32152890            for (BackRefList::const_iterator it = m->backRefs.begin();
    3216                  it != m->backRefs.end(); ++ it)
     2891                 it != m->backRefs.end(); ++it)
    32172892                if (it->llSnapshotIds.size() != 0)
    32182893                    return true;
     
    33523027
    33533028/**
    3354  * Checks that this hard disk may be discarded and performs necessary state
    3355  * changes. Must not be called for writethrough disks because there is nothing
    3356  * to discard then.
    3357  *
    3358  * This method is to be called prior to calling the #discard() to perform
    3359  * necessary consistency checks and place involved hard disks to appropriate
    3360  * states. If #discard() is not called or fails, the state modifications
    3361  * performed by this method must be undone by #cancelDiscard().
    3362  *
    3363  * See #discard() for more info about discarding hard disks.
    3364  *
    3365  * @param aChain        Where to store the created merge chain (may return NULL
    3366  *                      if no real merge is necessary).
    3367  *
    3368  * @note Caller must hold media tree lock for writing. This locks this object
    3369  *       and every medium object on the merge chain for writing.
    3370  */
    3371 HRESULT Medium::prepareDiscard(MergeChain * &aChain)
    3372 {
    3373     AutoCaller autoCaller(this);
    3374     AssertComRCReturnRC(autoCaller.rc());
    3375 
    3376     aChain = NULL;
    3377 
    3378     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3379 
    3380     Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    3381 
    3382     // Medium must not be writethrough at this point
    3383     AssertReturn(   m->type == MediumType_Normal
    3384                  || m->type == MediumType_Immutable, E_FAIL);
    3385 
    3386     if (getChildren().size() == 0)
    3387     {
    3388         /* special treatment of the last hard disk in the chain: */
    3389 
    3390         if (m->pParent.isNull())
    3391         {
    3392             /* lock only, to prevent any usage; discard() will unlock */
    3393             return LockWrite(NULL);
    3394         }
    3395 
    3396         /* the differencing hard disk w/o children will be deleted, protect it
    3397          * from attaching to other VMs (this is why Deleting) */
    3398 
    3399         switch (m->state)
    3400         {
    3401             case MediumState_Created:
    3402                 m->state = MediumState_Deleting;
    3403                 break;
    3404             default:
    3405                 return setStateError();
    3406         }
    3407 
    3408         /* aChain is intentionally NULL here */
    3409 
    3410         return S_OK;
    3411     }
    3412 
    3413     /* not going multi-merge as it's too expensive */
    3414     if (getChildren().size() > 1)
    3415         return setError(E_FAIL,
    3416                         tr ("Hard disk '%s' has more than one child hard disk (%d)"),
    3417                         m->strLocationFull.raw(), getChildren().size());
    3418 
    3419     /* this is a read-only hard disk with children; it must be associated with
    3420      * exactly one snapshot (when the snapshot is being taken, none of the
    3421      * current VM's hard disks may be attached to other VMs). Note that by the
    3422      * time when discard() is called, there must be no any attachments at all
    3423      * (the code calling prepareDiscard() should detach). */
    3424     AssertReturn(m->backRefs.size() == 1, E_FAIL);
    3425     AssertReturn(!m->backRefs.front().fInCurState, E_FAIL);
    3426     AssertReturn(m->backRefs.front().llSnapshotIds.size() == 1, E_FAIL);
    3427 
    3428     ComObjPtr<Medium> child = getChildren().front();
    3429 
    3430     /* we keep this locked, so lock the affected child to make sure the lock
    3431      * order is correct when calling prepareMergeTo() */
    3432     AutoWriteLock childLock(child COMMA_LOCKVAL_SRC_POS);
    3433 
    3434     /* delegate the rest to the profi */
    3435     if (m->pParent.isNull())
    3436     {
    3437         /* base hard disk, backward merge */
    3438         Assert(child->m->backRefs.size() == 1);
    3439         if (child->m->backRefs.front().machineId != m->backRefs.front().machineId)
    3440         {
    3441             /* backward merge is too tricky, we'll just detach on discard, so
    3442              * lock only, to prevent any usage; discard() will only unlock
    3443              * (since we return NULL in aChain) */
    3444             return LockWrite(NULL);
    3445         }
    3446 
    3447         return child->prepareMergeTo(this, aChain, true /* aIgnoreAttachments */);
    3448     }
    3449     else
    3450     {
    3451         /* forward merge */
    3452         return prepareMergeTo(child, aChain, true /* aIgnoreAttachments */);
    3453     }
    3454 }
    3455 
    3456 /**
    3457  * Discards this hard disk.
    3458  *
    3459  * Discarding the hard disk is merging its contents to its differencing child
    3460  * hard disk (forward merge) or contents of its child hard disk to itself
    3461  * (backward merge) if this hard disk is a base hard disk. If this hard disk is
    3462  * a differencing hard disk w/o children, then it will be simply deleted.
    3463  * Calling this method on a base hard disk w/o children will do nothing and
    3464  * silently succeed. If this hard disk has more than one child, the method will
    3465  * currently return an error (since merging in this case would be too expensive
    3466  * and result in data duplication).
    3467  *
    3468  * When the backward merge takes place (i.e. this hard disk is a target) then,
    3469  * on success, this hard disk will automatically replace the differencing child
    3470  * hard disk used as a source (which will then be deleted) in the attachment
    3471  * this child hard disk is associated with. This will happen only if both hard
    3472  * disks belong to the same machine because otherwise such a replace would be
    3473  * too tricky and could be not expected by the other machine. Same relates to a
    3474  * case when the child hard disk is not associated with any machine at all. When
    3475  * the backward merge is not applied, the method behaves as if the base hard
    3476  * disk were not attached at all -- i.e. simply detaches it from the machine but
    3477  * leaves the hard disk chain intact.
    3478  *
    3479  * This method is basically a wrapper around #mergeTo() that selects the correct
    3480  * merge direction and performs additional actions as described above and.
    3481  *
    3482  * Note that this method will not return until the merge operation is complete
    3483  * (which may be quite time consuming depending on the size of the merged hard
    3484  * disks).
    3485  *
    3486  * Note that #prepareDiscard() must be called before calling this method. If
    3487  * this method returns a failure, the caller must call #cancelDiscard(). On
    3488  * success, #cancelDiscard() must not be called (this method will perform all
    3489  * necessary steps such as resetting states of all involved hard disks and
    3490  * deleting @a aChain).
    3491  *
    3492  * @param aChain        Merge chain created by #prepareDiscard() (may be NULL if
    3493  *                      no real merge takes place).
    3494  *
    3495  * @note Locks the hard disks from the chain for writing. Locks the machine
    3496  *       object when the backward merge takes place. Locks medium tree for
    3497  *       reading or writing.
    3498  */
    3499 HRESULT Medium::discard(ComObjPtr<Progress> &aProgress,
    3500                         ULONG ulWeight,
    3501                         MergeChain *aChain,
    3502                         bool *pfNeedsSaveSettings)
    3503 {
    3504     AssertReturn(!aProgress.isNull(), E_FAIL);
    3505 
    3506     ComObjPtr <Medium> hdFrom;
    3507 
     3029 * Constructs a medium lock list for this medium. The lock is not taken.
     3030 *
     3031 * @note Locks the medium tree for reading.
     3032 *
     3033 * @param fMediumWritable   Whether to associate a write lock with this medium.
     3034 * @param pToBeParent       Medium which will become the parent of this medium.
     3035 * @param mediumLockList    Where to store the resulting list.
     3036 */
     3037HRESULT Medium::createMediumLockList(bool fMediumWritable,
     3038                                     Medium *pToBeParent,
     3039                                     MediumLockList &mediumLockList)
     3040{
    35083041    HRESULT rc = S_OK;
    35093042
    3510     {
    3511         AutoCaller autoCaller(this);
    3512         AssertComRCReturnRC(autoCaller.rc());
    3513 
    3514         aProgress->SetNextOperation(BstrFmt(tr("Merging differencing image '%s'"), getName().raw()),
    3515                                     ulWeight);        // weight
    3516 
    3517         if (aChain == NULL)
    3518         {
    3519             /* we access mParent & children() */
    3520             AutoMultiWriteLock2 mLock(&m->pVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
    3521 
    3522             Assert(getChildren().size() == 0);
    3523 
    3524             /* special treatment of the last hard disk in the chain: */
    3525 
    3526             if (m->pParent.isNull())
     3043    /* we access parent medium objects */
     3044    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     3045
     3046    /* paranoid sanity checking if the medium has a to-be parent medium */
     3047    if (pToBeParent)
     3048    {
     3049        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     3050        ComAssertRet(getParent().isNull(), E_FAIL);
     3051        ComAssertRet(getChildren().size() == 0, E_FAIL);
     3052    }
     3053
     3054    ErrorInfoKeeper eik;
     3055    MultiResult mrc(S_OK);
     3056
     3057    ComObjPtr<Medium> pMedium = this;
     3058    while (!pMedium.isNull())
     3059    {
     3060        // need write lock for RefreshState if medium is inaccessible
     3061        AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     3062
     3063        /* Accessibility check must be first, otherwise locking interferes
     3064         * with getting the medium state. Lock lists are not created for
     3065         * fun, and thus getting the image status is no luxury. */
     3066        MediumState_T mediumState = pMedium->getState();
     3067        if (mediumState == MediumState_Inaccessible)
     3068        {
     3069            rc = pMedium->RefreshState(&mediumState);
     3070            if (FAILED(rc)) return rc;
     3071
     3072            if (mediumState == MediumState_Inaccessible)
    35273073            {
    3528                 rc = UnlockWrite(NULL);
    3529                 AssertComRC(rc);
    3530                 return rc;
     3074                Bstr error;
     3075                rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
     3076                if (FAILED(rc)) return rc;
     3077
     3078                Bstr loc;
     3079                rc = pMedium->COMGETTER(Location)(loc.asOutParam());
     3080                if (FAILED(rc)) return rc;
     3081
     3082                /* collect multiple errors */
     3083                eik.restore();
     3084
     3085                /* be in sync with MediumBase::setStateError() */
     3086                Assert(!error.isEmpty());
     3087                mrc = setError(E_FAIL,
     3088                               tr("Medium '%ls' is not accessible. %ls"),
     3089                               loc.raw(),
     3090                               error.raw());
     3091
     3092                eik.fetch();
    35313093            }
    3532 
    3533             /* delete the differencing hard disk w/o children */
    3534 
    3535             Assert(m->state == MediumState_Deleting);
    3536 
    3537             /* go back to Created since deleteStorage() expects this state */
    3538             m->state = MediumState_Created;
    3539 
    3540             hdFrom = this;
    3541 
    3542             rc = deleteStorageAndWait(&aProgress, pfNeedsSaveSettings);
    3543         }
     3094        }
     3095
     3096        if (pMedium == this)
     3097            mediumLockList.Prepend(pMedium, fMediumWritable);
    35443098        else
    3545         {
    3546             hdFrom = aChain->source();
    3547 
    3548             rc = hdFrom->mergeToAndWait(aChain, &aProgress, pfNeedsSaveSettings);
    3549         }
    3550     }
    3551 
    3552     if (SUCCEEDED(rc))
    3553     {
    3554         /* mergeToAndWait() cannot uninitialize the initiator because of
    3555          * possible AutoCallers on the current thread, deleteStorageAndWait()
    3556          * doesn't do it either; do it ourselves */
    3557         hdFrom->uninit();
    3558     }
    3559 
    3560     return rc;
    3561 }
    3562 
    3563 /**
    3564  * Undoes what #prepareDiscard() did. Must be called if #discard() is not called
    3565  * or fails. Frees memory occupied by @a aChain.
    3566  *
    3567  * @param aChain        Merge chain created by #prepareDiscard() (may be NULL if
    3568  *                      no real merge takes place).
    3569  *
    3570  * @note Locks the hard disks from the chain for writing. Locks medium tree for
    3571  *       reading.
    3572  */
    3573 void Medium::cancelDiscard(MergeChain *aChain)
    3574 {
    3575     AutoCaller autoCaller(this);
    3576     AssertComRCReturnVoid(autoCaller.rc());
    3577 
    3578     if (aChain == NULL)
    3579     {
    3580         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3581 
    3582         /* we access mParent & children() */
    3583         AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    3584 
    3585         Assert(getChildren().size() == 0);
    3586 
    3587         /* special treatment of the last hard disk in the chain: */
    3588 
    3589         if (m->pParent.isNull())
    3590         {
    3591             HRESULT rc = UnlockWrite(NULL);
    3592             AssertComRC(rc);
    3593             return;
    3594         }
    3595 
    3596         /* the differencing hard disk w/o children will be deleted, protect it
    3597          * from attaching to other VMs (this is why Deleting) */
    3598 
    3599         Assert(m->state == MediumState_Deleting);
    3600         m->state = MediumState_Created;
    3601 
    3602         return;
    3603     }
    3604 
    3605     /* delegate the rest to the profi */
    3606     cancelMergeTo(aChain);
     3099            mediumLockList.Prepend(pMedium, false);
     3100
     3101        pMedium = pMedium->getParent();
     3102        if (pMedium.isNull() && pToBeParent)
     3103            pMedium = pToBeParent;
     3104    }
     3105
     3106    return mrc;
    36073107}
    36083108
     
    39153415    /* leave the lock before a lengthy operation */
    39163416    vrc = RTSemEventMultiReset(m->queryInfoSem);
    3917     ComAssertRCThrow(vrc, E_FAIL);
     3417    AssertRCReturn(vrc, E_FAIL);
    39183418    m->queryInfoRunning = true;
    39193419    alock.leave();
     
    39353435        {
    39363436            /** @todo This kind of opening of images is assuming that diff
    3937              * images can be opened as base images. Should be fixed ASAP. */
     3437             * images can be opened as base images. Should be documented if
     3438             * it must work for all medium format backends. */
    39383439            vrc = VDOpen(hdd,
    39393440                         format.c_str(),
     
    40333534
    40343535                    Guid id = parentId;
    4035                     ComObjPtr<Medium> parent;
     3536                    ComObjPtr<Medium> pParent;
    40363537                    rc = m->pVirtualBox->findHardDisk(&id, NULL,
    40373538                                                   false /* aSetError */,
    4038                                                    &parent);
     3539                                                   &pParent);
    40393540                    if (FAILED(rc))
    40403541                    {
     
    40503551
    40513552                    Assert(m->pParent.isNull());
    4052                     m->pParent = parent;
     3553                    m->pParent = pParent;
    40533554                    m->pParent->m->llChildren.push_back(this);
    40543555                }
     
    42533754    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
    42543755
    4255     /* we're accessing the media tree, and canClose() needs the tree lock too */
    4256     AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
    4257                                   this->lockHandle()
    4258                                   COMMA_LOCKVAL_SRC_POS);
    4259     LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
    4260 
    4261     if (    !(m->formatObj->capabilities() & (   MediumFormatCapabilities_CreateDynamic
    4262                                                | MediumFormatCapabilities_CreateFixed)))
    4263         return setError(VBOX_E_NOT_SUPPORTED,
    4264                         tr("Hard disk format '%s' does not support storage deletion"),
    4265                         m->strFormat.raw());
    4266 
    4267     /* Note that we are fine with Inaccessible state too: a) for symmetry with
    4268      * create calls and b) because it doesn't really harm to try, if it is
    4269      * really inaccessible, the delete operation will fail anyway. Accepting
    4270      * Inaccessible state is especially important because all registered hard
    4271      * disks are initially Inaccessible upon VBoxSVC startup until
    4272      * COMGETTER(State) is called. */
    4273 
     3756    HRESULT rc = S_OK;
     3757    ComObjPtr<Progress> pProgress;
     3758    Medium::Task *pTask = NULL;
     3759
     3760    try
     3761    {
     3762        /* we're accessing the media tree, and canClose() needs it too */
     3763        AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
     3764                                      this->lockHandle()
     3765                                      COMMA_LOCKVAL_SRC_POS);
     3766        LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
     3767
     3768        if (    !(m->formatObj->capabilities() & (   MediumFormatCapabilities_CreateDynamic
     3769                                                   | MediumFormatCapabilities_CreateFixed)))
     3770            throw setError(VBOX_E_NOT_SUPPORTED,
     3771                           tr("Hard disk format '%s' does not support storage deletion"),
     3772                           m->strFormat.raw());
     3773
     3774        /* Note that we are fine with Inaccessible state too: a) for symmetry
     3775         * with create calls and b) because it doesn't really harm to try, if
     3776         * it is really inaccessible, the delete operation will fail anyway.
     3777         * Accepting Inaccessible state is especially important because all
     3778         * registered hard disks are initially Inaccessible upon VBoxSVC
     3779         * startup until COMGETTER(RefreshState) is called. Accept Deleting
     3780         * state because some callers need to put the image in this state early
     3781         * to prevent races. */
     3782        switch (m->state)
     3783        {
     3784            case MediumState_Created:
     3785            case MediumState_Deleting:
     3786            case MediumState_Inaccessible:
     3787                break;
     3788            default:
     3789                throw setStateError();
     3790        }
     3791
     3792        if (m->backRefs.size() != 0)
     3793        {
     3794            Utf8Str strMachines;
     3795            for (BackRefList::const_iterator it = m->backRefs.begin();
     3796                it != m->backRefs.end();
     3797                ++it)
     3798            {
     3799                const BackRef &b = *it;
     3800                if (strMachines.length())
     3801                    strMachines.append(", ");
     3802                strMachines.append(b.machineId.toString().c_str());
     3803            }
     3804#ifdef DEBUG
     3805            dumpBackRefs();
     3806#endif
     3807            throw setError(VBOX_E_OBJECT_IN_USE,
     3808                           tr("Cannot delete storage: hard disk '%s' is still attached to the following %d virtual machine(s): %s"),
     3809                           m->strLocationFull.c_str(),
     3810                           m->backRefs.size(),
     3811                           strMachines.c_str());
     3812        }
     3813
     3814        rc = canClose();
     3815        if (FAILED(rc)) throw rc;
     3816
     3817        /* go to Deleting state, so that the medium is not actually locked */
     3818        rc = markForDeletion();
     3819        if (FAILED(rc)) throw rc;
     3820
     3821        /* Build the medium lock list. */
     3822        MediumLockList *pMediumLockList(new MediumLockList());
     3823        rc = createMediumLockList(true, NULL,
     3824                                  *pMediumLockList);
     3825        if (FAILED(rc))
     3826        {
     3827            delete pMediumLockList;
     3828            throw rc;
     3829        }
     3830
     3831        rc = pMediumLockList->Lock();
     3832        if (FAILED(rc))
     3833        {
     3834            delete pMediumLockList;
     3835            throw setError(rc,
     3836                           tr("Failed to lock media when deleting '%ls'"),
     3837                           getLocationFull().raw());
     3838        }
     3839
     3840        /* try to remove from the list of known hard disks before performing
     3841         * actual deletion (we favor the consistency of the media registry in
     3842         * the first place which would have been broken if
     3843         * unregisterWithVirtualBox() failed after we successfully deleted the
     3844         * storage) */
     3845        rc = unregisterWithVirtualBox(pfNeedsSaveSettings);
     3846        if (FAILED(rc)) throw rc;
     3847
     3848        if (aProgress != NULL)
     3849        {
     3850            /* use the existing progress object... */
     3851            pProgress = *aProgress;
     3852
     3853            /* ...but create a new one if it is null */
     3854            if (pProgress.isNull())
     3855            {
     3856                pProgress.createObject();
     3857                rc = pProgress->init(m->pVirtualBox,
     3858                                     static_cast<IMedium*>(this),
     3859                                     BstrFmt(tr("Deleting hard disk storage unit '%s'"), m->strLocationFull.raw()),
     3860                                     FALSE /* aCancelable */);
     3861                if (FAILED(rc)) throw rc;
     3862            }
     3863        }
     3864
     3865        /* setup task object to carry out the operation sync/async */
     3866        pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
     3867        rc = pTask->rc();
     3868        AssertComRC(rc);
     3869        if (FAILED(rc)) throw rc;
     3870    }
     3871    catch (HRESULT aRC) { rc = aRC; }
     3872
     3873    if (SUCCEEDED(rc))
     3874    {
     3875        if (aWait)
     3876            rc = runNow(pTask, NULL /* pfNeedsSaveSettings*/);
     3877        else
     3878            rc = startThread(pTask);
     3879
     3880        if (SUCCEEDED(rc) && aProgress != NULL)
     3881            *aProgress = pProgress;
     3882
     3883    }
     3884    else
     3885    {
     3886        if (pTask)
     3887            delete pTask;
     3888
     3889        /* Undo deleting state if necessary. */
     3890        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     3891        unmarkForDeletion();
     3892    }
     3893
     3894    return rc;
     3895}
     3896
     3897/**
     3898 * Mark a medium for deletion.
     3899 *
     3900 * @note Caller must hold the write lock on this medium!
     3901 */
     3902HRESULT Medium::markForDeletion()
     3903{
     3904    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
    42743905    switch (m->state)
    42753906    {
    42763907        case MediumState_Created:
    42773908        case MediumState_Inaccessible:
    4278             break;
     3909            m->preLockState = m->state;
     3910            m->state = MediumState_Deleting;
     3911            return S_OK;
    42793912        default:
    42803913            return setStateError();
    42813914    }
    4282 
    4283     if (m->backRefs.size() != 0)
    4284     {
    4285         Utf8Str strMachines;
    4286         for (BackRefList::const_iterator it = m->backRefs.begin();
    4287             it != m->backRefs.end();
    4288             ++it)
    4289         {
    4290             const BackRef &b = *it;
    4291             if (strMachines.length())
    4292                 strMachines.append(", ");
    4293             strMachines.append(b.machineId.toString().c_str());
    4294         }
    4295 #ifdef DEBUG
    4296         dumpBackRefs();
    4297 #endif
    4298         return setError(VBOX_E_OBJECT_IN_USE,
    4299                         tr("Cannot delete storage: hard disk '%s' is still attached to the following %d virtual machine(s): %s"),
    4300                         m->strLocationFull.c_str(),
    4301                         m->backRefs.size(),
    4302                         strMachines.c_str());
    4303     }
    4304 
    4305     HRESULT rc = canClose();
    4306     if (FAILED(rc)) return rc;
    4307 
    4308     /* go to Deleting state before leaving the lock */
    4309     m->state = MediumState_Deleting;
    4310 
    4311     /* try to remove from the list of known hard disks before performing actual
    4312      * deletion (we favor the consistency of the media registry in the first
    4313      * place which would have been broken if unregisterWithVirtualBox() failed
    4314      * after we successfully deleted the storage) */
    4315     rc = unregisterWithVirtualBox(pfNeedsSaveSettings);
    4316 
    4317     /* restore the state because we may fail below; we will set it later again*/
    4318     m->state = MediumState_Created;
    4319 
    4320     if (FAILED(rc)) return rc;
    4321 
    4322     ComObjPtr<Progress> progress;
    4323 
    4324     if (aProgress != NULL)
    4325     {
    4326         /* use the existing progress object... */
    4327         progress = *aProgress;
    4328 
    4329         /* ...but create a new one if it is null */
    4330         if (progress.isNull())
    4331         {
    4332             progress.createObject();
    4333             rc = progress->init(m->pVirtualBox,
    4334                                 static_cast<IMedium*>(this),
    4335                                 BstrFmt(tr("Deleting hard disk storage unit '%s'"), m->strLocationFull.raw()),
    4336                                 FALSE /* aCancelable */);
    4337             if (FAILED(rc)) return rc;
    4338         }
    4339     }
    4340 
    4341     /* setup task object to carry out the operation asynchronously */
    4342     Medium::Task *pTask(new Medium::DeleteTask(this, progress));
    4343     rc = pTask->rc();
    4344     if (FAILED(rc))
    4345     {
    4346         AssertComRC(rc);
    4347         delete pTask;
    4348         return rc;
    4349     }
    4350 
    4351     if (aWait)
    4352     {
    4353         /* go to Deleting state before starting the task */
    4354         m->state = MediumState_Deleting;
    4355 
    4356         rc = runNow(pTask, NULL /* pfNeedsSaveSettings*/ );        // there is no save settings to do in taskThreadDelete()
    4357         if (FAILED(rc)) return rc;
    4358     }
    4359     else
    4360     {
    4361         rc = startThread(pTask);
    4362         if (FAILED(rc)) return rc;
    4363 
    4364         /* go to Deleting state before leaving the lock */
    4365         m->state = MediumState_Deleting;
    4366     }
    4367 
    4368     if (aProgress != NULL)
    4369         *aProgress = progress;
    4370 
    4371     return rc;
     3915}
     3916
     3917/**
     3918 * Removes the "mark for deletion".
     3919 *
     3920 * @note Caller must hold the write lock on this medium!
     3921 */
     3922HRESULT Medium::unmarkForDeletion()
     3923{
     3924    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
     3925    switch (m->state)
     3926    {
     3927        case MediumState_Deleting:
     3928            m->state = m->preLockState;
     3929            return S_OK;
     3930        default:
     3931            return setStateError();
     3932    }
    43723933}
    43733934
     
    43763937 * format and the location. Note that @c aTarget must be NotCreated.
    43773938 *
    4378  * As opposed to the CreateDiffStorage() method, this method doesn't try to lock
    4379  * this hard disk for reading assuming that the caller has already done so. This
    4380  * is used when taking an online snapshot (where all original hard disks are
    4381  * locked for writing and must remain such). Note however that if @a aWait is
    4382  * @c false and this method returns a success then the thread started by
    4383  * this method will unlock the hard disk (unless it is in
    4384  * MediumState_LockedWrite state) so make sure the hard disk is either in
    4385  * MediumState_LockedWrite or call #LockRead() before calling this method! If @a
    4386  * aWait is @c true then this method neither locks nor unlocks the hard disk, so
    4387  * make sure you do it yourself as needed.
    4388  *
    4389  * If @a aProgress is not NULL but the object it points to is @c null then a new
    4390  * progress object will be created and assigned to @a *aProgress on success,
    4391  * otherwise the existing progress object is used. If @a aProgress is NULL, then no
    4392  * progress object is created/used at all.
     3939 * The @a aMediumLockList parameter contains the associated medium lock list,
     3940 * which must be in locked state. If @a aWait is @c true then the caller is
     3941 * responsible for unlocking.
     3942 *
     3943 * If @a aProgress is not NULL but the object it points to is @c null then a
     3944 * new progress object will be created and assigned to @a *aProgress on
     3945 * success, otherwise the existing progress object is used. If @a aProgress is
     3946 * NULL, then no progress object is created/used at all.
    43933947 *
    43943948 * When @a aWait is @c false, this method will create a thread to perform the
     
    43983952 * NULL when @a aWait is @c false (this method will assert in this case).
    43993953 *
    4400  * @param aTarget       Target hard disk.
    4401  * @param aVariant      Precise image variant to create.
     3954 * @param aTarget           Target hard disk.
     3955 * @param aVariant          Precise image variant to create.
     3956 * @param aMediumLockList   List of media which should be locked.
     3957 * @param aProgress         Where to find/store a Progress object to track
     3958 *                          operation completion.
     3959 * @param aWait             @c true if this method should block instead of
     3960 *                          creating an asynchronous thread.
     3961 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been
     3962 *                          initialized to false and that will be set to true
     3963 *                          by this function if the caller should invoke
     3964 *                          VirtualBox::saveSettings() because the global
     3965 *                          settings have changed. This only works in "wait"
     3966 *                          mode; otherwise saveSettings is called
     3967 *                          automatically by the thread that was created,
     3968 *                          and this parameter is ignored.
     3969 *
     3970 * @note Locks this object and @a aTarget for writing.
     3971 */
     3972HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
     3973                                  MediumVariant_T aVariant,
     3974                                  MediumLockList *aMediumLockList,
     3975                                  ComObjPtr<Progress> *aProgress,
     3976                                  bool aWait,
     3977                                  bool *pfNeedsSaveSettings)
     3978{
     3979    AssertReturn(!aTarget.isNull(), E_FAIL);
     3980    AssertReturn(aMediumLockList, E_FAIL);
     3981    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
     3982
     3983    AutoCaller autoCaller(this);
     3984    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     3985
     3986    AutoCaller targetCaller(aTarget);
     3987    if (FAILED(targetCaller.rc())) return targetCaller.rc();
     3988
     3989    HRESULT rc = S_OK;
     3990    ComObjPtr<Progress> pProgress;
     3991    Medium::Task *pTask = NULL;
     3992
     3993    try
     3994    {
     3995        AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
     3996
     3997        ComAssertThrow(m->type != MediumType_Writethrough, E_FAIL);
     3998        ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
     3999
     4000        if (aTarget->m->state != MediumState_NotCreated)
     4001            throw aTarget->setStateError();
     4002
     4003        /* Check that the hard disk is not attached to the current state of
     4004         * any VM referring to it. */
     4005        for (BackRefList::const_iterator it = m->backRefs.begin();
     4006             it != m->backRefs.end();
     4007             ++it)
     4008        {
     4009            if (it->fInCurState)
     4010            {
     4011                /* Note: when a VM snapshot is being taken, all normal hard
     4012                 * disks attached to the VM in the current state will be, as an
     4013                 * exception, also associated with the snapshot which is about
     4014                 * to create (see SnapshotMachine::init()) before deassociating
     4015                 * them from the current state (which takes place only on
     4016                 * success in Machine::fixupHardDisks()), so that the size of
     4017                 * snapshotIds will be 1 in this case. The extra condition is
     4018                 * used to filter out this legal situation. */
     4019                if (it->llSnapshotIds.size() == 0)
     4020                    throw setError(VBOX_E_INVALID_OBJECT_STATE,
     4021                                   tr("Hard disk '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing hard disks based on it may be created until it is detached"),
     4022                                   m->strLocationFull.raw(), it->machineId.raw());
     4023
     4024                Assert(it->llSnapshotIds.size() == 1);
     4025            }
     4026        }
     4027
     4028        if (aProgress != NULL)
     4029        {
     4030            /* use the existing progress object... */
     4031            pProgress = *aProgress;
     4032
     4033            /* ...but create a new one if it is null */
     4034            if (pProgress.isNull())
     4035            {
     4036                pProgress.createObject();
     4037                rc = pProgress->init(m->pVirtualBox,
     4038                                     static_cast<IMedium*>(this),
     4039                                     BstrFmt(tr("Creating differencing hard disk storage unit '%s'"), aTarget->m->strLocationFull.raw()),
     4040                                     TRUE /* aCancelable */);
     4041                if (FAILED(rc)) throw rc;
     4042            }
     4043        }
     4044
     4045        /* setup task object to carry out the operation sync/async */
     4046        pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
     4047                                           aMediumLockList,
     4048                                           aWait /* fKeepMediumLockList */);
     4049        rc = pTask->rc();
     4050        AssertComRC(rc);
     4051        if (FAILED(rc)) throw rc;
     4052
     4053        /* register a task (it will deregister itself when done) */
     4054        ++m->numCreateDiffTasks;
     4055        Assert(m->numCreateDiffTasks != 0); /* overflow? */
     4056
     4057        aTarget->m->state = MediumState_Creating;
     4058    }
     4059    catch (HRESULT aRC) { rc = aRC; }
     4060
     4061    if (SUCCEEDED(rc))
     4062    {
     4063        if (aWait)
     4064            rc = runNow(pTask, pfNeedsSaveSettings);
     4065        else
     4066            rc = startThread(pTask);
     4067
     4068        if (SUCCEEDED(rc) && aProgress != NULL)
     4069            *aProgress = pProgress;
     4070    }
     4071    else if (pTask != NULL)
     4072        delete pTask;
     4073
     4074    return rc;
     4075}
     4076
     4077/**
     4078 * Prepares this (source) hard disk, target hard disk and all intermediate hard
     4079 * disks for the merge operation.
     4080 *
     4081 * This method is to be called prior to calling the #mergeTo() to perform
     4082 * necessary consistency checks and place involved hard disks to appropriate
     4083 * states. If #mergeTo() is not called or fails, the state modifications
     4084 * performed by this method must be undone by #cancelMergeTo().
     4085 *
     4086 * See #mergeTo() for more information about merging.
     4087 *
     4088 * @param pTarget       Target hard disk.
     4089 * @param aMachineId    Allowed machine attachment. NULL means do not check.
     4090 * @param aSnapshotId   Allowed snapshot attachment. NULL or empty UUID means
     4091 *                      do not check.
     4092 * @param fMergeForward Resulting merge direction (out).
     4093 * @param pParentForTarget New parent for target medium after merge (out).
     4094 * @param aChildrenToReparent List of children of the source which will have
     4095 *                      to be reparented to the target after merge (out).
     4096 * @param aMediumLockList Medium locking information (out).
     4097 *
     4098 * @note Locks medium tree for reading. Locks this object, aTarget and all
     4099 *       intermediate hard disks for writing.
     4100 */
     4101HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
     4102                               const Guid *aMachineId,
     4103                               const Guid *aSnapshotId,
     4104                               bool &fMergeForward,
     4105                               ComObjPtr<Medium> &pParentForTarget,
     4106                               MediaList &aChildrenToReparent,
     4107                               MediumLockList * &aMediumLockList)
     4108{
     4109    AssertReturn(pTarget != NULL, E_FAIL);
     4110    AssertReturn(pTarget != this, E_FAIL);
     4111
     4112    AutoCaller autoCaller(this);
     4113    AssertComRCReturnRC(autoCaller.rc());
     4114
     4115    AutoCaller targetCaller(pTarget);
     4116    AssertComRCReturnRC(targetCaller.rc());
     4117
     4118    HRESULT rc = S_OK;
     4119    fMergeForward = false;
     4120    pParentForTarget.setNull();
     4121    aChildrenToReparent.clear();
     4122    Assert(aMediumLockList == NULL);
     4123    aMediumLockList = NULL;
     4124
     4125    try
     4126    {
     4127        // locking: we need the tree lock first because we access parent pointers
     4128        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     4129
     4130        /* more sanity checking and figuring out the merge direction */
     4131        ComObjPtr<Medium> pMedium = getParent();
     4132        while (!pMedium.isNull() && pMedium != pTarget)
     4133            pMedium = pMedium->getParent();
     4134        if (pMedium == pTarget)
     4135            fMergeForward = false;
     4136        else
     4137        {
     4138            pMedium = pTarget->getParent();
     4139            while (!pMedium.isNull() && pMedium != this)
     4140                pMedium = pMedium->getParent();
     4141            if (pMedium == this)
     4142                fMergeForward = true;
     4143            else
     4144            {
     4145                Utf8Str tgtLoc;
     4146                {
     4147                    AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
     4148                    tgtLoc = pTarget->getLocationFull();
     4149                }
     4150
     4151                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     4152                throw setError(E_FAIL,
     4153                               tr("Hard disks '%s' and '%s' are unrelated"),
     4154                               m->strLocationFull.raw(), tgtLoc.raw());
     4155            }
     4156        }
     4157
     4158        /* Build the lock list. */
     4159        aMediumLockList = new MediumLockList();
     4160        if (fMergeForward)
     4161            rc = pTarget->createMediumLockList(true, NULL, *aMediumLockList);
     4162        else
     4163            rc = createMediumLockList(false, NULL, *aMediumLockList);
     4164        if (FAILED(rc))
     4165        {
     4166            delete aMediumLockList;
     4167            aMediumLockList = NULL;
     4168            throw rc;
     4169        }
     4170
     4171        /* sanity checking */
     4172        {
     4173            AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     4174            if (m->state != MediumState_Created)
     4175                throw setStateError();
     4176        }
     4177        {
     4178            AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
     4179            if (pTarget->m->state != MediumState_Created)
     4180                throw pTarget->setStateError();
     4181        }
     4182
     4183        /* check medium attachment and other sanity conditions */
     4184        if (fMergeForward)
     4185        {
     4186            AutoReadLock(this COMMA_LOCKVAL_SRC_POS);
     4187            if (getChildren().size() > 1)
     4188            {
     4189                throw setError(E_FAIL,
     4190                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
     4191                               m->strLocationFull.raw(), getChildren().size());
     4192            }
     4193            /* One backreference is only allowed if the machine ID is not empty
     4194             * and it matches the machine the image is attached to (including
     4195             * the snapshot ID if not empty). */
     4196            if (   m->backRefs.size() != 0
     4197                && (   !aMachineId
     4198                    || m->backRefs.size() != 1
     4199                    || aMachineId->isEmpty()
     4200                    || *getFirstMachineBackrefId() != *aMachineId
     4201                    || (   (!aSnapshotId || !aSnapshotId->isEmpty())
     4202                        && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
     4203                throw setError(E_FAIL,
     4204                               tr("Medium '%s' is attached to %d virtual machines"),
     4205                               m->strLocationFull.raw(), m->backRefs.size());
     4206            if (m->type == MediumType_Immutable)
     4207                throw setError(E_FAIL,
     4208                               tr("Medium '%s' is immutable"),
     4209                               m->strLocationFull.raw());
     4210        }
     4211        else
     4212        {
     4213            AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
     4214            if (pTarget->getChildren().size() > 1)
     4215            {
     4216                throw setError(E_FAIL,
     4217                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
     4218                               pTarget->m->strLocationFull.raw(),
     4219                               pTarget->getChildren().size());
     4220            }
     4221            if (pTarget->m->type == MediumType_Immutable)
     4222                throw setError(E_FAIL,
     4223                               tr("Medium '%s' is immutable"),
     4224                               pTarget->m->strLocationFull.raw());
     4225        }
     4226        ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
     4227        ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
     4228        for (pLast = pLastIntermediate;
     4229             !pLast.isNull() && pLast != pTarget && pLast != this;
     4230             pLast = pLast->getParent())
     4231        {
     4232            AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
     4233            if (pLast->getChildren().size() > 1)
     4234            {
     4235                throw setError(E_FAIL,
     4236                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
     4237                               pLast->m->strLocationFull.raw(),
     4238                               pLast->getChildren().size());
     4239            }
     4240            if (pLast->m->backRefs.size() != 0)
     4241                throw setError(E_FAIL,
     4242                               tr("Medium '%s' is attached to %d virtual machines"),
     4243                               pLast->m->strLocationFull.raw(),
     4244                               pLast->m->backRefs.size());
     4245
     4246        }
     4247
     4248        /* Update medium states appropriately */
     4249        switch (m->state)
     4250        {
     4251            case MediumState_Created:
     4252                m->state = MediumState_Deleting;
     4253                break;
     4254            default:
     4255                throw setStateError();
     4256        }
     4257        if (fMergeForward)
     4258        {
     4259            /* we will need parent to reparent target */
     4260            pParentForTarget = m->pParent;
     4261        }
     4262        else
     4263        {
     4264            /* we will need to reparent children of the source */
     4265            for (MediaList::const_iterator it = getChildren().begin();
     4266                 it != getChildren().end();
     4267                 ++it)
     4268            {
     4269                pMedium = *it;
     4270                rc = pMedium->LockWrite(NULL);
     4271                if (FAILED(rc)) throw rc;
     4272
     4273                aChildrenToReparent.push_back(pMedium);
     4274            }
     4275        }
     4276        for (pLast = pLastIntermediate;
     4277             !pLast.isNull() && pLast != pTarget && pLast != this;
     4278             pLast = pLast->getParent())
     4279        {
     4280            AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
     4281            switch (pLast->m->state)
     4282            {
     4283                case MediumState_Created:
     4284                    pLast->m->state = MediumState_Deleting;
     4285                    break;
     4286                default:
     4287                    throw pLast->setStateError();
     4288            }
     4289        }
     4290
     4291        /* Tweak the lock list in the backward merge case, as the target
     4292         * isn't marked to be locked for writing yet. */
     4293        if (!fMergeForward)
     4294        {
     4295            MediumLockList::Base::iterator lockListBegin =
     4296                aMediumLockList->GetBegin();
     4297            MediumLockList::Base::iterator lockListEnd =
     4298                aMediumLockList->GetEnd();
     4299            lockListEnd--;
     4300            for (MediumLockList::Base::iterator it = lockListBegin;
     4301                 it != lockListEnd;
     4302                 ++it)
     4303            {
     4304                MediumLock &mediumLock = *it;
     4305                if (mediumLock.GetMedium() == pTarget)
     4306                {
     4307                    HRESULT rc2 = mediumLock.UpdateLock(true);
     4308                    AssertComRC(rc2);
     4309                    break;
     4310                }
     4311            }
     4312        }
     4313
     4314        rc = aMediumLockList->Lock();
     4315        if (FAILED(rc))
     4316        {
     4317            AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
     4318            throw setError(rc,
     4319                           tr("Failed to lock media when merging to '%ls'"),
     4320                           pTarget->getLocationFull().raw());
     4321        }
     4322    }
     4323    catch (HRESULT aRC) { rc = aRC; }
     4324
     4325    if (FAILED(rc))
     4326    {
     4327        delete aMediumLockList;
     4328        aMediumLockList = NULL;
     4329    }
     4330
     4331    return rc;
     4332}
     4333
     4334/**
     4335 * Merges this hard disk to the specified hard disk which must be either its
     4336 * direct ancestor or descendant.
     4337 *
     4338 * Given this hard disk is SOURCE and the specified hard disk is TARGET, we will
     4339 * get two varians of the merge operation:
     4340 *
     4341 *                forward merge
     4342 *                ------------------------->
     4343 *  [Extra] <- SOURCE <- Intermediate <- TARGET
     4344 *  Any        Del       Del             LockWr
     4345 *
     4346 *
     4347 *                            backward merge
     4348 *                <-------------------------
     4349 *             TARGET <- Intermediate <- SOURCE <- [Extra]
     4350 *             LockWr    Del             Del       LockWr
     4351 *
     4352 * Each diagram shows the involved hard disks on the hard disk chain where
     4353 * SOURCE and TARGET belong. Under each hard disk there is a state value which
     4354 * the hard disk must have at a time of the mergeTo() call.
     4355 *
     4356 * The hard disks in the square braces may be absent (e.g. when the forward
     4357 * operation takes place and SOURCE is the base hard disk, or when the backward
     4358 * merge operation takes place and TARGET is the last child in the chain) but if
     4359 * they present they are involved too as shown.
     4360 *
     4361 * Nor the source hard disk neither intermediate hard disks may be attached to
     4362 * any VM directly or in the snapshot, otherwise this method will assert.
     4363 *
     4364 * The #prepareMergeTo() method must be called prior to this method to place all
     4365 * involved to necessary states and perform other consistency checks.
     4366 *
     4367 * If @a aWait is @c true then this method will perform the operation on the
     4368 * calling thread and will not return to the caller until the operation is
     4369 * completed. When this method succeeds, all intermediate hard disk objects in
     4370 * the chain will be uninitialized, the state of the target hard disk (and all
     4371 * involved extra hard disks) will be restored. @a aMediumLockList will not be
     4372 * deleted, whether the operation is successful or not. The caller has to do
     4373 * this if appropriate. Note that this (source) hard disk is not uninitialized
     4374 * because of possible AutoCaller instances held by the caller of this method
     4375 * on the current thread. It's therefore the responsibility of the caller to
     4376 * call Medium::uninit() after releasing all callers.
     4377 *
     4378 * If @a aWait is @c false then this method will create a thread to perform the
     4379 * operation asynchronously and will return immediately. If the operation
     4380 * succeeds, the thread will uninitialize the source hard disk object and all
     4381 * intermediate hard disk objects in the chain, reset the state of the target
     4382 * hard disk (and all involved extra hard disks) and delete @a aMediumLockList.
     4383 * If the operation fails, the thread will only reset the states of all
     4384 * involved hard disks and delete @a aMediumLockList.
     4385 *
     4386 * When this method fails (regardless of the @a aWait mode), it is a caller's
     4387 * responsiblity to undo state changes and delete @a aMediumLockList using
     4388 * #cancelMergeTo().
     4389 *
     4390 * If @a aProgress is not NULL but the object it points to is @c null then a new
     4391 * progress object will be created and assigned to @a *aProgress on success,
     4392 * otherwise the existing progress object is used. If Progress is NULL, then no
     4393 * progress object is created/used at all. Note that @a aProgress cannot be
     4394 * NULL when @a aWait is @c false (this method will assert in this case).
     4395 *
     4396 * @param pTarget       Target hard disk.
     4397 * @param fMergeForward Merge direction.
     4398 * @param pParentForTarget New parent for target medium after merge.
     4399 * @param aChildrenToReparent List of children of the source which will have
     4400 *                      to be reparented to the target after merge.
     4401 * @param aMediumLockList Medium locking information.
    44024402 * @param aProgress     Where to find/store a Progress object to track operation
    44034403 *                      completion.
     
    44094409 *                and this parameter is ignored.
    44104410 *
    4411  * @note Locks this object and @a aTarget for writing.
    4412  */
    4413 HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
    4414                                   MediumVariant_T aVariant,
    4415                                   ComObjPtr<Progress> *aProgress,
    4416                                   bool aWait,
    4417                                   bool *pfNeedsSaveSettings)
    4418 {
    4419     AssertReturn(!aTarget.isNull(), E_FAIL);
    4420     AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
    4421 
    4422     AutoCaller autoCaller(this);
    4423     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4424 
    4425     AutoCaller targetCaller(aTarget);
    4426     if (FAILED(targetCaller.rc())) return targetCaller.rc();
    4427 
    4428     AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
    4429 
    4430     AssertReturn(m->type != MediumType_Writethrough, E_FAIL);
    4431 
    4432     /* Note: MediumState_LockedWrite is ok when taking an online snapshot */
    4433     AssertReturn(    m->state == MediumState_LockedRead
    4434                   || m->state == MediumState_LockedWrite, E_FAIL);
    4435 
    4436     if (aTarget->m->state != MediumState_NotCreated)
    4437         return aTarget->setStateError();
    4438 
    4439     HRESULT rc = S_OK;
    4440 
    4441     /* check that the hard disk is not attached to any VM in the current state*/
    4442     for (BackRefList::const_iterator it = m->backRefs.begin();
    4443          it != m->backRefs.end();
    4444          ++it)
    4445     {
    4446         if (it->fInCurState)
    4447         {
    4448             /* Note: when a VM snapshot is being taken, all normal hard disks
    4449              * attached to the VM in the current state will be, as an exception,
    4450              * also associated with the snapshot which is about to create  (see
    4451              * SnapshotMachine::init()) before deassociating them from the
    4452              * current state (which takes place only on success in
    4453              * Machine::fixupHardDisks()), so that the size of snapshotIds
    4454              * will be 1 in this case. The given condition is used to filter out
    4455              * this legal situatinon and do not report an error. */
    4456 
    4457             if (it->llSnapshotIds.size() == 0)
    4458                 return setError(VBOX_E_INVALID_OBJECT_STATE,
    4459                                 tr("Hard disk '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing hard disks based on it may be created until it is detached"),
    4460                                 m->strLocationFull.raw(), it->machineId.raw());
    4461 
    4462             Assert(it->llSnapshotIds.size() == 1);
    4463         }
    4464     }
    4465 
    4466     ComObjPtr<Progress> progress;
    4467 
    4468     if (aProgress != NULL)
    4469     {
    4470         /* use the existing progress object... */
    4471         progress = *aProgress;
    4472 
    4473         /* ...but create a new one if it is null */
    4474         if (progress.isNull())
    4475         {
    4476             progress.createObject();
    4477             rc = progress->init(m->pVirtualBox,
    4478                                 static_cast<IMedium*>(this),
    4479                                 BstrFmt(tr("Creating differencing hard disk storage unit '%s'"), aTarget->m->strLocationFull.raw()),
    4480                                 TRUE /* aCancelable */);
    4481             if (FAILED(rc)) return rc;
    4482         }
    4483     }
    4484 
    4485     /* setup task object to carry out the operation asynchronously */
    4486     Medium::Task *pTask(new Medium::CreateDiffTask(this, progress, aTarget,
    4487                                                    aVariant));
    4488     rc = pTask->rc();
    4489     if (FAILED(rc))
    4490     {
    4491         AssertComRC(rc);
    4492         delete pTask;
    4493         return rc;
    4494     }
    4495 
    4496     /* register a task (it will deregister itself when done) */
    4497     ++m->numCreateDiffTasks;
    4498     Assert(m->numCreateDiffTasks != 0); /* overflow? */
    4499 
    4500     if (aWait)
    4501     {
    4502         // go to Creating state before starting the task
    4503         aTarget->m->state = MediumState_Creating;
    4504 
    4505         // release the locks because the task function will acquire other locks;
    4506         // this is safe because both this and the target are not protected by
    4507         // their states; we have asserted above that *this* is locked read or write!
    4508         alock.release();
    4509 
    4510         rc = runNow(pTask, pfNeedsSaveSettings);
    4511         if (FAILED(rc)) return rc;
    4512     }
    4513     else
    4514     {
    4515         rc = startThread(pTask);
    4516         if (FAILED(rc)) return rc;
    4517 
    4518         /* go to Creating state before leaving the lock */
    4519         aTarget->m->state = MediumState_Creating;
    4520     }
    4521 
    4522     if (aProgress != NULL)
    4523         *aProgress = progress;
    4524 
    4525     return rc;
    4526 }
    4527 
    4528 /**
    4529  * Prepares this (source) hard disk, target hard disk and all intermediate hard
    4530  * disks for the merge operation.
    4531  *
    4532  * This method is to be called prior to calling the #mergeTo() to perform
    4533  * necessary consistency checks and place involved hard disks to appropriate
    4534  * states. If #mergeTo() is not called or fails, the state modifications
    4535  * performed by this method must be undone by #cancelMergeTo().
    4536  *
    4537  * Note that when @a aIgnoreAttachments is @c true then it's the caller's
    4538  * responsibility to detach the source and all intermediate hard disks before
    4539  * calling #mergeTo() (which will fail otherwise).
    4540  *
    4541  * See #mergeTo() for more information about merging.
    4542  *
    4543  * @param aTarget       Target hard disk.
    4544  * @param aChain        Where to store the created merge chain.
    4545  * @param aIgnoreAttachments    Don't check if the source or any intermediate
    4546  *                              hard disk is attached to any VM.
    4547  *
    4548  * @note Locks medium tree for reading. Locks this object, aTarget and all
    4549  *       intermediate hard disks for writing.
    4550  */
    4551 HRESULT Medium::prepareMergeTo(Medium *aTarget,
    4552                                MergeChain * &aChain,
    4553                                bool aIgnoreAttachments /*= false*/)
    4554 {
    4555     AssertReturn(aTarget != NULL, E_FAIL);
    4556 
    4557     AutoCaller autoCaller(this);
    4558     AssertComRCReturnRC(autoCaller.rc());
    4559 
    4560     AutoCaller targetCaller(aTarget);
    4561     AssertComRCReturnRC(targetCaller.rc());
    4562 
    4563     aChain = NULL;
    4564 
    4565     /* we walk the tree */
    4566     AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    4567 
    4568     HRESULT rc = S_OK;
    4569 
    4570     /* detect the merge direction */
    4571     bool forward;
    4572     {
    4573         Medium *parent = m->pParent;
    4574         while (parent != NULL && parent != aTarget)
    4575             parent = parent->m->pParent;
    4576         if (parent == aTarget)
    4577             forward = false;
    4578         else
    4579         {
    4580             parent = aTarget->m->pParent;
    4581             while (parent != NULL && parent != this)
    4582                 parent = parent->m->pParent;
    4583             if (parent == this)
    4584                 forward = true;
    4585             else
    4586             {
    4587                 Utf8Str tgtLoc;
    4588                 {
    4589                     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    4590                     tgtLoc = aTarget->getLocationFull();
    4591                 }
    4592 
    4593                 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    4594                 return setError(E_FAIL,
    4595                                 tr("Hard disks '%s' and '%s' are unrelated"),
    4596                                 m->strLocationFull.raw(), tgtLoc.raw());
    4597             }
    4598         }
    4599     }
    4600 
    4601     /* build the chain (will do necessary checks and state changes) */
    4602     std::auto_ptr<MergeChain> chain(new MergeChain(forward,
    4603                                                    aIgnoreAttachments));
    4604     {
    4605         Medium *last = forward ? aTarget : this;
    4606         Medium *first = forward ? this : aTarget;
    4607 
    4608         for (;;)
    4609         {
    4610             if (last == aTarget)
    4611                 rc = chain->addTarget(last);
    4612             else if (last == this)
    4613                 rc = chain->addSource(last);
    4614             else
    4615                 rc = chain->addIntermediate(last);
    4616             if (FAILED(rc)) return rc;
    4617 
    4618             if (last == first)
    4619                 break;
    4620 
    4621             last = last->m->pParent;
    4622         }
    4623     }
    4624 
    4625     aChain = chain.release();
    4626 
    4627     return S_OK;
    4628 }
    4629 
    4630 /**
    4631  * Merges this hard disk to the specified hard disk which must be either its
    4632  * direct ancestor or descendant.
    4633  *
    4634  * Given this hard disk is SOURCE and the specified hard disk is TARGET, we will
    4635  * get two varians of the merge operation:
    4636  *
    4637  *                forward merge
    4638  *                ------------------------->
    4639  *  [Extra] <- SOURCE <- Intermediate <- TARGET
    4640  *  Any        Del       Del             LockWr
    4641  *
    4642  *
    4643  *                            backward merge
    4644  *                <-------------------------
    4645  *             TARGET <- Intermediate <- SOURCE <- [Extra]
    4646  *             LockWr    Del             Del       LockWr
    4647  *
    4648  * Each scheme shows the involved hard disks on the hard disk chain where
    4649  * SOURCE and TARGET belong. Under each hard disk there is a state value which
    4650  * the hard disk must have at a time of the mergeTo() call.
    4651  *
    4652  * The hard disks in the square braces may be absent (e.g. when the forward
    4653  * operation takes place and SOURCE is the base hard disk, or when the backward
    4654  * merge operation takes place and TARGET is the last child in the chain) but if
    4655  * they present they are involved too as shown.
    4656  *
    4657  * Nor the source hard disk neither intermediate hard disks may be attached to
    4658  * any VM directly or in the snapshot, otherwise this method will assert.
    4659  *
    4660  * The #prepareMergeTo() method must be called prior to this method to place all
    4661  * involved to necessary states and perform other consistency checks.
    4662  *
    4663  * If @a aWait is @c true then this method will perform the operation on the
    4664  * calling thread and will not return to the caller until the operation is
    4665  * completed. When this method succeeds, all intermediate hard disk objects in
    4666  * the chain will be uninitialized, the state of the target hard disk (and all
    4667  * involved extra hard disks) will be restored and @a aChain will be deleted.
    4668  * Note that this (source) hard disk is not uninitialized because of possible
    4669  * AutoCaller instances held by the caller of this method on the current thread.
    4670  * It's therefore the responsibility of the caller to call Medium::uninit()
    4671  * after releasing all callers in this case!
    4672  *
    4673  * If @a aWait is @c false then this method will crea,te a thread to perform the
    4674  * create operation asynchronously and will return immediately. If the operation
    4675  * succeeds, the thread will uninitialize the source hard disk object and all
    4676  * intermediate hard disk objects in the chain, reset the state of the target
    4677  * hard disk (and all involved extra hard disks) and delete @a aChain. If the
    4678  * operation fails, the thread will only reset the states of all involved hard
    4679  * disks and delete @a aChain.
    4680  *
    4681  * When this method fails (regardless of the @a aWait mode), it is a caller's
    4682  * responsiblity to undo state changes and delete @a aChain using
    4683  * #cancelMergeTo().
    4684  *
    4685  * If @a aProgress is not NULL but the object it points to is @c null then a new
    4686  * progress object will be created and assigned to @a *aProgress on success,
    4687  * otherwise the existing progress object is used. If Progress is NULL, then no
    4688  * progress object is created/used at all. Note that @a aProgress cannot be
    4689  * NULL when @a aWait is @c false (this method will assert in this case).
    4690  *
    4691  * @param aChain        Merge chain created by #prepareMergeTo().
    4692  * @param aProgress     Where to find/store a Progress object to track operation
    4693  *                      completion.
    4694  * @param aWait         @c true if this method should block instead of creating
    4695  *                      an asynchronous thread.
    4696  * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
    4697  *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
    4698  *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
    4699  *                and this parameter is ignored.
    4700  *
    4701  * @note Locks the branch lock for writing. Locks the hard disks from the chain
     4411 * @note Locks the tree lock for writing. Locks the hard disks from the chain
    47024412 *       for writing.
    47034413 */
    4704 HRESULT Medium::mergeTo(MergeChain *aChain,
     4414HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
     4415                        bool &fMergeForward,
     4416                        ComObjPtr<Medium> pParentForTarget,
     4417                        const MediaList &aChildrenToReparent,
     4418                        MediumLockList *aMediumLockList,
    47054419                        ComObjPtr <Progress> *aProgress,
    47064420                        bool aWait,
    47074421                        bool *pfNeedsSaveSettings)
    47084422{
    4709     AssertReturn(aChain != NULL, E_FAIL);
     4423    AssertReturn(pTarget != NULL, E_FAIL);
     4424    AssertReturn(pTarget != this, E_FAIL);
     4425    AssertReturn(aMediumLockList != NULL, E_FAIL);
    47104426    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
    47114427
     
    47144430
    47154431    HRESULT rc = S_OK;
    4716 
    4717     ComObjPtr <Progress> progress;
    4718 
    4719     if (aProgress != NULL)
    4720     {
    4721         /* use the existing progress object... */
    4722         progress = *aProgress;
    4723 
    4724         /* ...but create a new one if it is null */
    4725         if (progress.isNull())
    4726         {
    4727             AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    4728 
    4729             progress.createObject();
    4730             rc = progress->init(m->pVirtualBox,
    4731                                 static_cast<IMedium*>(this),
    4732                                 BstrFmt(tr("Merging hard disk '%s' to '%s'"),
    4733                                         getName().raw(),
    4734                                         aChain->target()->getName().raw()),
    4735                                 TRUE /* aCancelable */);
    4736             if (FAILED(rc)) return rc;
    4737         }
    4738     }
    4739 
    4740     /* setup task object to carry out the operation asynchronously */
    4741     Medium::Task *pTask(new Medium::MergeTask(this, progress, aChain));
    4742     rc = pTask->rc();
    4743     if (FAILED(rc))
    4744     {
     4432    ComObjPtr <Progress> pProgress;
     4433    Medium::Task *pTask = NULL;
     4434
     4435    try
     4436    {
     4437        if (aProgress != NULL)
     4438        {
     4439            /* use the existing progress object... */
     4440            pProgress = *aProgress;
     4441
     4442            /* ...but create a new one if it is null */
     4443            if (pProgress.isNull())
     4444            {
     4445                Utf8Str tgtName;
     4446                {
     4447                    AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
     4448                    tgtName = pTarget->getName();
     4449                }
     4450
     4451                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     4452
     4453                pProgress.createObject();
     4454                rc = pProgress->init(m->pVirtualBox,
     4455                                     static_cast<IMedium*>(this),
     4456                                     BstrFmt(tr("Merging hard disk '%s' to '%s'"),
     4457                                             getName().raw(),
     4458                                             tgtName.raw()),
     4459                                     TRUE /* aCancelable */);
     4460                if (FAILED(rc)) throw rc;
     4461            }
     4462        }
     4463
     4464        /* setup task object to carry out the operation sync/async */
     4465        pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
     4466                                      pParentForTarget, aChildrenToReparent,
     4467                                      pProgress, aMediumLockList,
     4468                                      aWait /* fKeepMediumLockList */);
     4469        rc = pTask->rc();
    47454470        AssertComRC(rc);
     4471        if (FAILED(rc)) throw rc;
     4472    }
     4473    catch (HRESULT aRC) { rc = aRC; }
     4474
     4475    if (SUCCEEDED(rc))
     4476    {
     4477        if (aWait)
     4478            rc = runNow(pTask, pfNeedsSaveSettings);
     4479        else
     4480            rc = startThread(pTask);
     4481
     4482        if (SUCCEEDED(rc) && aProgress != NULL)
     4483            *aProgress = pProgress;
     4484    }
     4485    else if (pTask != NULL)
    47464486        delete pTask;
    4747         return rc;
    4748     }
    4749 
    4750     /* Note: task owns aChain (will delete it when not needed) in all cases
    4751      * except when @a aWait is @c true and runNow() fails -- in this case
    4752      * aChain will be left away because cancelMergeTo() will be applied by the
    4753      * caller on it as it is required in the documentation above */
    4754 
    4755     if (aWait)
    4756     {
    4757         rc = runNow(pTask, pfNeedsSaveSettings);
    4758         if (FAILED(rc)) return rc;
    4759     }
    4760     else
    4761     {
    4762         rc = startThread(pTask);
    4763         if (FAILED(rc)) return rc;
    4764     }
    4765 
    4766     if (aProgress != NULL)
    4767         *aProgress = progress;
    47684487
    47694488    return rc;
     
    47714490
    47724491/**
    4773  * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not called
    4774  * or fails. Frees memory occupied by @a aChain.
    4775  *
    4776  * @param aChain        Merge chain created by #prepareMergeTo().
     4492 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
     4493 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
     4494 * the medium objects in @a aChildrenToReparent.
     4495 *
     4496 * @param aChildrenToReparent List of children of the source which will have
     4497 *                      to be reparented to the target after merge.
     4498 * @param aMediumLockList Medium locking information.
    47774499 *
    47784500 * @note Locks the hard disks from the chain for writing.
    47794501 */
    4780 void Medium::cancelMergeTo(MergeChain *aChain)
     4502void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
     4503                           MediumLockList *aMediumLockList)
    47814504{
    47824505    AutoCaller autoCaller(this);
    47834506    AssertComRCReturnVoid(autoCaller.rc());
    47844507
    4785     AssertReturnVoid(aChain != NULL);
    4786 
    4787     /* the destructor will do the thing */
    4788     delete aChain;
     4508    AssertReturnVoid(aMediumLockList != NULL);
     4509
     4510    /* the destructor will do the work */
     4511    delete aMediumLockList;
     4512
     4513    /* unlock the children which had to be reparented */
     4514    for (MediaList::const_iterator it = aChildrenToReparent.begin();
     4515         it != aChildrenToReparent.end();
     4516         ++it)
     4517    {
     4518        const ComObjPtr<Medium> &pMedium = *it;
     4519
     4520        AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     4521        pMedium->UnlockWrite(NULL);
     4522    }
    47894523}
    47904524
     
    48084542        if (m->formatObj.isNull())
    48094543            return setError(E_INVALIDARG,
    4810                             tr("Invalid hard disk storage format '%ls'"), aFormat);
     4544                            tr("Invalid hard disk storage format '%ls'"),
     4545                            aFormat);
    48114546
    48124547        /* reference the format permanently to prevent its unexpected
     
    48244559                m->formatObj->properties().begin();
    48254560             it != m->formatObj->properties().end();
    4826              ++ it)
     4561             ++it)
    48274562        {
    48284563            m->properties.insert(std::make_pair(it->name, Bstr::Null));
     
    50474782HRESULT Medium::startThread(Medium::Task *pTask)
    50484783{
     4784#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
     4785    /* Extreme paranoia: The calling thread should not hold the medium
     4786     * tree lock or any medium lock. Since there is no separate lock class
     4787     * for medium objects be even more strict: no other object locks. */
     4788    Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
     4789    Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
     4790#endif
     4791
    50494792    /// @todo use a more descriptive task name
    50504793    int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
     
    50814824                       bool *pfNeedsSaveSettings)
    50824825{
     4826#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
     4827    /* Extreme paranoia: The calling thread should not hold the medium
     4828     * tree lock or any medium lock. Since there is no separate lock class
     4829     * for medium objects be even more strict: no other object locks. */
     4830    Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
     4831    Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
     4832#endif
     4833
    50834834    pTask->m_pfNeedsSaveSettings = pfNeedsSaveSettings;
    50844835
     
    50974848 * @return
    50984849 */
    5099 HRESULT Medium::taskThreadCreateBase(Medium::CreateBaseTask &task)
     4850HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
    51004851{
    51014852    HRESULT rc = S_OK;
     
    51074858    try
    51084859    {
    5109         /* The lock is also used as a signal from the task initiator (which
    5110         * releases it only after RTThreadCreate()) that we can start the job */
    51114860        AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
    51124861
     
    51224871        }
    51234872
     4873        Utf8Str format(m->strFormat);
     4874        Utf8Str location(m->strLocationFull);
     4875        uint64_t capabilities = m->formatObj->capabilities();
     4876        ComAssertThrow(capabilities & (  VD_CAP_CREATE_FIXED
     4877                                       | VD_CAP_CREATE_DYNAMIC), E_FAIL);
     4878        Assert(m->state == MediumState_Creating);
     4879
    51244880        PVBOXHDD hdd;
    51254881        int vrc = VDCreate(m->vdDiskIfaces, &hdd);
    51264882        ComAssertRCThrow(vrc, E_FAIL);
    51274883
    5128         Utf8Str format(m->strFormat);
    5129         Utf8Str location(m->strLocationFull);
    5130         /* uint64_t capabilities = */ m->formatObj->capabilities();
    5131 
    51324884        /* unlock before the potentially lengthy operation */
    5133         Assert(m->state == MediumState_Creating);
    51344885        thisLock.leave();
    51354886
     
    52144965 *
    52154966 * This task always gets started from Medium::createDiffStorage() and can run
    5216  * synchronously or asynchrously depending on the "wait" parameter passed to
     4967 * synchronously or asynchronously depending on the "wait" parameter passed to
    52174968 * that function. If we run synchronously, the caller expects the bool
    52184969 * *pfNeedsSaveSettings to be set before returning; otherwise (in asynchronous
     
    52224973 * @return
    52234974 */
    5224 HRESULT Medium::taskThreadCreateDiff(Medium::CreateDiffTask &task)
     4975HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
    52254976{
    52264977    HRESULT rc = S_OK;
     
    52354986    try
    52364987    {
    5237         /* Lock both in {parent,child} order. The lock is also used as a
    5238         * signal from the task initiator (which releases it only after
    5239         * RTThreadCreate()) that we can start the job*/
     4988        /* Lock both in {parent,child} order. */
    52404989        AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
    52414990
    52424991        /* The object may request a specific UUID (through a special form of
    5243         * the setLocation() argument). Otherwise we have to generate it */
     4992         * the setLocation() argument). Otherwise we have to generate it */
    52444993        Guid targetId = pTarget->m->id;
    52454994        fGenerateUuid = targetId.isEmpty();
     
    52515000        }
    52525001
     5002        Guid id = m->id;
     5003
     5004        Utf8Str targetFormat(pTarget->m->strFormat);
     5005        Utf8Str targetLocation(pTarget->m->strLocationFull);
     5006        uint64_t capabilities = m->formatObj->capabilities();
     5007        ComAssertThrow(capabilities & VD_CAP_CREATE_DYNAMIC, E_FAIL);
     5008
     5009        Assert(pTarget->m->state == MediumState_Creating);
     5010        Assert(m->state == MediumState_LockedRead);
     5011
    52535012        PVBOXHDD hdd;
    52545013        int vrc = VDCreate(m->vdDiskIfaces, &hdd);
    52555014        ComAssertRCThrow(vrc, E_FAIL);
    52565015
    5257         Guid id = m->id;
    5258         Utf8Str format(m->strFormat);
    5259         Utf8Str location(m->strLocationFull);
    5260 
    5261         Utf8Str targetFormat(pTarget->m->strFormat);
    5262         Utf8Str targetLocation(pTarget->m->strLocationFull);
    5263 
    5264         Assert(pTarget->m->state == MediumState_Creating);
    5265 
    5266         /* Note: MediumState_LockedWrite is ok when taking an online
    5267          * snapshot */
    5268         Assert(    m->state == MediumState_LockedRead
    5269                 || m->state == MediumState_LockedWrite);
    5270 
    52715016        /* the two media are now protected by their non-default states;
    5272            unlock the media before the potentially lengthy operation */
     5017         * unlock the media before the potentially lengthy operation */
    52735018        mediaLock.leave();
    52745019
    52755020        try
    52765021        {
    5277             vrc = VDOpen(hdd,
    5278                          format.c_str(),
    5279                          location.c_str(),
    5280                          VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
    5281                          m->vdDiskIfaces);
    5282             if (RT_FAILURE(vrc))
    5283                 throw setError(E_FAIL,
    5284                                 tr("Could not open the hard disk storage unit '%s'%s"),
    5285                                 location.raw(), vdError(vrc).raw());
     5022            /* Open all hard disk images in the target chain but the last. */
     5023            MediumLockList::Base::const_iterator targetListBegin =
     5024                task.mpMediumLockList->GetBegin();
     5025            MediumLockList::Base::const_iterator targetListEnd =
     5026                task.mpMediumLockList->GetEnd();
     5027            targetListEnd--;
     5028            for (MediumLockList::Base::const_iterator it = targetListBegin;
     5029                 it != targetListEnd;
     5030                 ++it)
     5031            {
     5032                const MediumLock &mediumLock = *it;
     5033                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
     5034
     5035                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     5036
     5037                /* sanity check */
     5038                Assert(pMedium->m->state == MediumState_LockedRead);
     5039
     5040                /* Open all images in appropriate mode. */
     5041                vrc = VDOpen(hdd,
     5042                             pMedium->m->strFormat.c_str(),
     5043                             pMedium->m->strLocationFull.c_str(),
     5044                             VD_OPEN_FLAGS_READONLY,
     5045                             pMedium->m->vdDiskIfaces);
     5046                if (RT_FAILURE(vrc))
     5047                    throw setError(E_FAIL,
     5048                                   tr("Could not open the hard disk storage unit '%s'%s"),
     5049                                   pMedium->m->strLocationFull.raw(),
     5050                                   vdError(vrc).raw());
     5051            }
    52865052
    52875053            /* ensure the target directory exists */
     
    53265092            * potential race! */
    53275093        /* diffs for immutable hard disks are auto-reset by default */
    5328         pTarget->m->autoReset =   getBase()->m->type == MediumType_Immutable
    5329                                ? TRUE
    5330                                : FALSE;
     5094        pTarget->m->autoReset = (getBase()->m->type == MediumType_Immutable);
    53315095
    53325096        /* register with mVirtualBox as the last step and move to
     
    53635127    if (task.isAsync())
    53645128    {
    5365         /* unlock ourselves when done (unless in MediumState_LockedWrite
    5366          * state because of taking the online snapshot*/
    5367         if (m->state != MediumState_LockedWrite)
    5368         {
    5369             HRESULT rc2 = UnlockRead(NULL);
    5370             AssertComRC(rc2);
    5371         }
    5372 
    53735129        if (fNeedsSaveSettings)
    53745130        {
     
    54055161 * @return
    54065162 */
    5407 HRESULT Medium::taskThreadMerge(Medium::MergeTask &task)
     5163HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
    54085164{
    54095165    HRESULT rc = S_OK;
    54105166
    5411     /* The lock is also used as a signal from the task initiator (which
    5412      * releases it only after RTThreadCreate()) that we can start the
    5413      * job. We don't actually need the lock for anything else since the
    5414      * object is protected by MediumState_Deleting and we don't modify
    5415      * its sensitive fields below */
    5416     {
    5417         AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
    5418     }
    5419 
    5420     MergeChain *chain = task.mMergeChain.get();
     5167    const ComObjPtr<Medium> &pTarget = task.mTarget;
    54215168
    54225169    try
     
    54285175        try
    54295176        {
    5430             /* Open all hard disks in the chain (they are in the
    5431              * {parent,child} order in there. Note that we don't lock
    5432              * objects in this chain since they must be in states
    5433              * (Deleting and LockedWrite) that prevent from changing
    5434              * their format and location fields from outside. */
    5435 
    5436             for (MergeChain::const_iterator it = chain->begin();
    5437                  it != chain->end();
     5177            unsigned uTargetIdx = VD_LAST_IMAGE;
     5178            unsigned uSourceIdx = VD_LAST_IMAGE;
     5179            /* Open all hard disks in the chain. */
     5180            MediumLockList::Base::iterator lockListBegin =
     5181                task.mpMediumLockList->GetBegin();
     5182            MediumLockList::Base::iterator lockListEnd =
     5183                task.mpMediumLockList->GetEnd();
     5184            unsigned i = 0;
     5185            for (MediumLockList::Base::iterator it = lockListBegin;
     5186                 it != lockListEnd;
    54385187                 ++it)
    54395188            {
     5189                MediumLock &mediumLock = *it;
     5190                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
     5191
     5192                if (pMedium == this)
     5193                    uSourceIdx = i;
     5194                else if (pMedium == pTarget)
     5195                    uTargetIdx = i;
     5196
     5197                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     5198
    54405199                /*
    54415200                 * complex sanity (sane complexity)
     
    54455204                 * If it is the target it must be in the LockedWrite state.
    54465205                 */
    5447                 Assert(   (   *it != chain->target()
    5448                            && (   (*it)->m->state == MediumState_Deleting
    5449                                || (*it)->m->state == MediumState_LockedRead))
    5450                        || (   *it == chain->target()
    5451                            && (*it)->m->state == MediumState_LockedWrite));
     5206                Assert(   (   pMedium != pTarget
     5207                           && (   pMedium->m->state == MediumState_Deleting
     5208                               || pMedium->m->state == MediumState_LockedRead))
     5209                       || (   pMedium == pTarget
     5210                           && pMedium->m->state == MediumState_LockedWrite));
    54525211
    54535212                /*
     
    54565215                 * to a virtual machine.
    54575216                 */
    5458                 Assert(   *it == chain->target()
    5459                        || (*it)->m->state == MediumState_LockedRead
    5460                        || (   (*it)->m->backRefs.size() == 0
    5461                            && (*it)->m->state == MediumState_Deleting)
    5462                       );
     5217                Assert(   pMedium == pTarget
     5218                       || pMedium->m->state == MediumState_LockedRead
     5219                       || (   pMedium->m->backRefs.size() == 0
     5220                           && pMedium->m->state == MediumState_Deleting));
     5221                /* The source medium must be in Deleting state. */
     5222                Assert(  pMedium != this
     5223                       || pMedium->m->state == MediumState_Deleting);
    54635224
    54645225                unsigned uOpenFlags = 0;
    54655226
    5466                 if (   (*it)->m->state == MediumState_LockedRead
    5467                     || (*it)->m->state == MediumState_Deleting)
     5227                if (   pMedium->m->state == MediumState_LockedRead
     5228                    || pMedium->m->state == MediumState_Deleting)
    54685229                    uOpenFlags = VD_OPEN_FLAGS_READONLY;
    54695230
    54705231                /* Open the image */
    54715232                vrc = VDOpen(hdd,
    5472                              (*it)->m->strFormat.c_str(),
    5473                              (*it)->m->strLocationFull.c_str(),
     5233                             pMedium->m->strFormat.c_str(),
     5234                             pMedium->m->strLocationFull.c_str(),
    54745235                             uOpenFlags,
    5475                              (*it)->m->vdDiskIfaces);
     5236                             pMedium->m->vdDiskIfaces);
    54765237                if (RT_FAILURE(vrc))
    54775238                    throw vrc;
     5239
     5240                i++;
    54785241            }
    54795242
    5480             vrc = VDMerge(hdd, chain->sourceIdx(), chain->targetIdx(),
     5243            ComAssertThrow(   uSourceIdx != VD_LAST_IMAGE
     5244                           && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
     5245
     5246            vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
    54815247                          task.mVDOperationIfaces);
    54825248            if (RT_FAILURE(vrc))
     
    54845250
    54855251            /* update parent UUIDs */
    5486             if (!chain->isForward())
     5252            if (!task.mfMergeForward)
    54875253            {
    54885254                /* we need to update UUIDs of all source's children
    54895255                 * which cannot be part of the container at once so
    54905256                 * add each one in there individually */
    5491                 if (chain->children().size() > 0)
     5257                if (task.mChildrenToReparent.size() > 0)
    54925258                {
    5493                     for (MediaList::const_iterator it = chain->children().begin();
    5494                          it != chain->children().end();
     5259                    for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
     5260                         it != task.mChildrenToReparent.end();
    54955261                         ++it)
    54965262                    {
     
    55055271
    55065272                        vrc = VDSetParentUuid(hdd, 1,
    5507                                               chain->target()->m->id);
     5273                                              pTarget->m->id);
    55085274                        if (RT_FAILURE(vrc))
    55095275                            throw vrc;
     
    55215287            throw setError(E_FAIL,
    55225288                            tr("Could not merge the hard disk '%s' to '%s'%s"),
    5523                             chain->source()->m->strLocationFull.raw(),
    5524                             chain->target()->m->strLocationFull.raw(),
     5289                            m->strLocationFull.raw(), m->strLocationFull.raw(),
    55255290                            vdError(aVRC).raw());
    55265291        }
     
    55355300    {
    55365301        /* all hard disks but the target were successfully deleted by
    5537          * VDMerge; reparent the last one and uninitialize deleted */
     5302         * VDMerge; reparent the last one and uninitialize deleted media. */
    55385303
    55395304        AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    55405305
    5541         Medium *pSource = chain->source();
    5542         Medium *pTarget = chain->target();
    5543 
    5544         if (chain->isForward())
     5306        if (task.mfMergeForward)
    55455307        {
    55465308            /* first, unregister the target since it may become a base
     
    55525314             * both ends (chain->parent() is source's parent) */
    55535315            pTarget->deparent();
    5554             pTarget->m->pParent = chain->parent();
     5316            pTarget->m->pParent = task.mParentForTarget;
    55555317            if (pTarget->m->pParent)
    55565318            {
    55575319                pTarget->m->pParent->m->llChildren.push_back(pTarget);
    5558                 pSource->deparent();
     5320                deparent();
    55595321            }
    55605322
     
    55715333            targetChild->deparent();
    55725334
    5573             const MediaList &children = chain->children();
    5574 
    55755335            /* reparent source's chidren and disconnect the deleted
    55765336             * branch at the younger end m*/
    5577             if (children.size() > 0)
     5337            if (task.mChildrenToReparent.size() > 0)
    55785338            {
    55795339                /* obey {parent,child} lock order */
    5580                 AutoWriteLock sourceLock(pSource COMMA_LOCKVAL_SRC_POS);
    5581 
    5582                 for (MediaList::const_iterator it = children.begin();
    5583                      it != children.end();
    5584                      ++it)
     5340                AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
     5341
     5342                for (MediaList::iterator it = task.mChildrenToReparent.begin();
     5343                     it != task.mChildrenToReparent.end();
     5344                     it++)
    55855345                {
    5586                     AutoWriteLock childLock(*it COMMA_LOCKVAL_SRC_POS);
    5587 
    5588                     Medium *p = *it;
    5589                     p->deparent();  // removes p from source
    5590                     pTarget->m->llChildren.push_back(p);
    5591                     p->m->pParent = pTarget;
     5346                    Medium *pMedium = *it;
     5347                    AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
     5348
     5349                    pMedium->deparent();  // removes pMedium from source
     5350                    pTarget->m->llChildren.push_back(pMedium);
     5351                    pMedium->m->pParent = pTarget;
    55925352                }
    55935353            }
    55945354        }
    55955355
    5596         /* unregister and uninitialize all hard disks in the chain but the target */
    5597         for (MergeChain::iterator it = chain->begin();
    5598              it != chain->end();
    5599             )
    5600         {
     5356        /* unregister and uninitialize all hard disks removed by the merge */
     5357        MediumLockList::Base::iterator lockListBegin =
     5358            task.mpMediumLockList->GetBegin();
     5359        MediumLockList::Base::iterator lockListEnd =
     5360            task.mpMediumLockList->GetEnd();
     5361        for (MediumLockList::Base::iterator it = lockListBegin;
     5362             it != lockListEnd;
     5363             )
     5364        {
     5365            MediumLock &mediumLock = *it;
     5366            /* Create a real copy of the medium pointer, as the medium
     5367             * lock deletion below would invalidate the referenced object. */
     5368            const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
     5369
    56015370            /* The target and all images not merged (readonly) are skipped */
    5602             if (   *it == chain->target()
    5603                 || (*it)->m->state == MediumState_LockedRead)
     5371            if (   pMedium == pTarget
     5372                || pMedium->m->state == MediumState_LockedRead)
    56045373            {
    56055374                ++it;
     
    56075376            }
    56085377
    5609             rc2 = (*it)->m->pVirtualBox->unregisterHardDisk(*it, NULL /*pfNeedsSaveSettings*/);
     5378            rc2 = pMedium->m->pVirtualBox->unregisterHardDisk(pMedium,
     5379                                                              NULL /*pfNeedsSaveSettings*/);
    56105380            AssertComRC(rc2);
    56115381
     
    56225392             * and therefore we cannot uninit() it (it's therefore
    56235393             * the caller's responsibility) */
    5624             if (*it == this)
     5394            if (pMedium == this)
     5395            {
     5396                Assert(getChildren().size() == 0);
     5397                Assert(m->backRefs.size() == 0);
    56255398                task.mMediumCaller.release();
    5626 
    5627             /* release the caller added by MergeChain before uninit() */
    5628             (*it)->releaseCaller();
    5629 
    5630             if (task.isAsync() || *it != this)
    5631                 (*it)->uninit();
    5632 
    5633             /* delete (to prevent uninitialization in MergeChain
    5634              * dtor) and advance to the next item */
    5635             it = chain->erase(it);
     5399            }
     5400
     5401            /* Delete the medium lock list entry, which also releases the
     5402             * caller added by MergeChain before uninit() and updates the
     5403             * iterator to point to the right place. */
     5404            rc2 = task.mpMediumLockList->RemoveByIterator(it);
     5405            AssertComRC(rc2);
     5406
     5407            if (task.isAsync() || pMedium != this)
     5408                pMedium->uninit();
    56365409        }
    56375410    }
     
    56605433         * in the mergeTo() docs. The latter also implies that we
    56615434         * don't own the merge chain, so release it in this case. */
    5662 
    5663         if (!task.isAsync())
    5664             task.mMergeChain.release();
     5435        if (task.isAsync())
     5436        {
     5437            Assert(task.mChildrenToReparent.size() == 0);
     5438            cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
     5439        }
    56655440    }
    56665441
     
    56775452 * @return
    56785453 */
    5679 HRESULT Medium::taskThreadClone(Medium::CloneTask &task)
     5454HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
    56805455{
    56815456    HRESULT rc = S_OK;
     
    56975472
    56985473        fCreatingTarget = pTarget->m->state == MediumState_Creating;
    5699 
    5700         ImageChain *sourceChain = task.mSourceChain.get();
    5701         ImageChain *parentChain = task.mParentChain.get();
    57025474
    57035475        /* The object may request a specific UUID (through a special form of
     
    57195491        {
    57205492            /* Open all hard disk images in the source chain. */
    5721             for (MediaList::const_iterator it = sourceChain->begin();
    5722                  it != sourceChain->end();
     5493            MediumLockList::Base::const_iterator sourceListBegin =
     5494                task.mpSourceMediumLockList->GetBegin();
     5495            MediumLockList::Base::const_iterator sourceListEnd =
     5496                task.mpSourceMediumLockList->GetEnd();
     5497            for (MediumLockList::Base::const_iterator it = sourceListBegin;
     5498                 it != sourceListEnd;
    57235499                 ++it)
    57245500            {
     5501                const MediumLock &mediumLock = *it;
     5502                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
     5503                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     5504
    57255505                /* sanity check */
    5726                 Assert((*it)->m->state == MediumState_LockedRead);
     5506                Assert(pMedium->m->state == MediumState_LockedRead);
    57275507
    57285508                /** Open all images in read-only mode. */
    57295509                vrc = VDOpen(hdd,
    5730                              (*it)->m->strFormat.c_str(),
    5731                              (*it)->m->strLocationFull.c_str(),
     5510                             pMedium->m->strFormat.c_str(),
     5511                             pMedium->m->strLocationFull.c_str(),
    57325512                             VD_OPEN_FLAGS_READONLY,
    5733                              (*it)->m->vdDiskIfaces);
     5513                             pMedium->m->vdDiskIfaces);
    57345514                if (RT_FAILURE(vrc))
    57355515                    throw setError(E_FAIL,
    57365516                                    tr("Could not open the hard disk storage unit '%s'%s"),
    5737                                     (*it)->m->strLocationFull.raw(),
     5517                                    pMedium->m->strLocationFull.raw(),
    57385518                                    vdError(vrc).raw());
    57395519            }
     
    57605540            try
    57615541            {
    5762                 /* Open all hard disk images in the parent chain. */
    5763                 for (MediaList::const_iterator it = parentChain->begin();
    5764                      it != parentChain->end();
     5542                /* Open all hard disk images in the target chain. */
     5543                MediumLockList::Base::const_iterator targetListBegin =
     5544                    task.mpTargetMediumLockList->GetBegin();
     5545                MediumLockList::Base::const_iterator targetListEnd =
     5546                    task.mpTargetMediumLockList->GetEnd();
     5547                for (MediumLockList::Base::const_iterator it = targetListBegin;
     5548                     it != targetListEnd;
    57655549                     ++it)
    57665550                {
    5767                     /** @todo r=klaus (*it) is not locked, lots of
    5768                         * race opportunities below */
     5551                    const MediumLock &mediumLock = *it;
     5552                    const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
     5553
     5554                    /* If the target medium is not created yet there's no
     5555                     * reason to open it. */
     5556                    if (pMedium == pTarget && fCreatingTarget)
     5557                        continue;
     5558
     5559                    AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     5560
    57695561                    /* sanity check */
    5770                     Assert(    (*it)->m->state == MediumState_LockedRead
    5771                             || (*it)->m->state == MediumState_LockedWrite);
     5562                    Assert(    pMedium->m->state == MediumState_LockedRead
     5563                            || pMedium->m->state == MediumState_LockedWrite);
    57725564
    57735565                    /* Open all images in appropriate mode. */
    57745566                    vrc = VDOpen(targetHdd,
    5775                                  (*it)->m->strFormat.c_str(),
    5776                                  (*it)->m->strLocationFull.c_str(),
    5777                                  ((*it)->m->state == MediumState_LockedWrite) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
    5778                                  (*it)->m->vdDiskIfaces);
     5567                                 pMedium->m->strFormat.c_str(),
     5568                                 pMedium->m->strLocationFull.c_str(),
     5569                                 (pMedium->m->state == MediumState_LockedWrite) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
     5570                                 pMedium->m->vdDiskIfaces);
    57795571                    if (RT_FAILURE(vrc))
    57805572                        throw setError(E_FAIL,
    57815573                                       tr("Could not open the hard disk storage unit '%s'%s"),
    5782                                        (*it)->m->strLocationFull.raw(),
     5574                                       pMedium->m->strLocationFull.raw(),
    57835575                                       vdError(vrc).raw());
    57845576                }
     
    58805672     * that we get a deadlock in Appliance::Import when Medium::Close
    58815673     * is called & the source chain is released at the same time. */
    5882     task.mSourceChain.reset();
     5674    task.mpSourceMediumLockList->Clear();
    58835675
    58845676    return rc;
     
    58955687 * @return
    58965688 */
    5897 HRESULT Medium::taskThreadDelete(Medium::DeleteTask &task)
     5689HRESULT Medium::taskDeleteHandler(Medium::DeleteTask &task)
    58985690{
    58995691    NOREF(task);
     
    59615753 * @return
    59625754 */
    5963 HRESULT Medium::taskThreadReset(Medium::ResetTask &task)
     5755HRESULT Medium::taskResetHandler(Medium::ResetTask &task)
    59645756{
    59655757    HRESULT rc = S_OK;
     
    60715863 * @return
    60725864 */
    6073 HRESULT Medium::taskThreadCompact(Medium::CompactTask &task)
     5865HRESULT Medium::taskCompactHandler(Medium::CompactTask &task)
    60745866{
    60755867    HRESULT rc = S_OK;
     
    60805872    AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
    60815873
    6082     ImageChain *imgChain = task.mImageChain.get();
    6083 
    60845874    try
    60855875    {
     
    60915881        {
    60925882            /* Open all hard disk images in the chain. */
    6093             MediaList::const_iterator last = imgChain->end();
    6094             last--;
    6095             for (MediaList::const_iterator it = imgChain->begin();
    6096                  it != imgChain->end();
     5883            MediumLockList::Base::const_iterator mediumListBegin =
     5884                task.mpMediumLockList->GetBegin();
     5885            MediumLockList::Base::const_iterator mediumListEnd =
     5886                task.mpMediumLockList->GetEnd();
     5887            MediumLockList::Base::const_iterator mediumListLast =
     5888                mediumListEnd;
     5889            mediumListLast--;
     5890            for (MediumLockList::Base::const_iterator it = mediumListBegin;
     5891                 it != mediumListEnd;
    60975892                 ++it)
    60985893            {
     5894                const MediumLock &mediumLock = *it;
     5895                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
     5896                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     5897
    60995898                /* sanity check */
    6100                 if (it == last)
    6101                     Assert((*it)->m->state == MediumState_LockedWrite);
     5899                if (it == mediumListLast)
     5900                    Assert(pMedium->m->state == MediumState_LockedWrite);
    61025901                else
    6103                     Assert((*it)->m->state == MediumState_LockedRead);
     5902                    Assert(pMedium->m->state == MediumState_LockedRead);
    61045903
    61055904                /** Open all images but last in read-only mode. */
    61065905                vrc = VDOpen(hdd,
    6107                              (*it)->m->strFormat.c_str(),
    6108                              (*it)->m->strLocationFull.c_str(),
    6109                              (it == last) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
    6110                              (*it)->m->vdDiskIfaces);
     5906                             pMedium->m->strFormat.c_str(),
     5907                             pMedium->m->strLocationFull.c_str(),
     5908                             (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
     5909                             pMedium->m->vdDiskIfaces);
    61115910                if (RT_FAILURE(vrc))
    61125911                    throw setError(E_FAIL,
    61135912                                   tr("Could not open the hard disk storage unit '%s'%s"),
    6114                                    (*it)->m->strLocationFull.raw(),
     5913                                   pMedium->m->strLocationFull.raw(),
    61155914                                   vdError(vrc).raw());
    61165915            }
  • trunk/src/VBox/Main/SnapshotImpl.cpp

    r28149 r28401  
     1/* $Id$ */
     2
    13/** @file
    24 *
    3  * COM class implementation for Snapshot and SnapshotMachine.
     5 * COM class implementation for Snapshot and SnapshotMachine in VBoxSVC.
    46 */
    57
    68/*
    7  * Copyright (C) 2006-2007 Sun Microsystems, Inc.
     9 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
    810 *
    911 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2022 */
    2123
     24#include "Logging.h"
    2225#include "SnapshotImpl.h"
    2326
     
    3437
    3538#include "AutoCaller.h"
    36 #include "Logging.h"
    3739
    3840#include <iprt/path.h>
     
    107109HRESULT Snapshot::FinalConstruct()
    108110{
    109     LogFlowMember(("Snapshot::FinalConstruct()\n"));
     111    LogFlowThisFunc(("\n"));
    110112    return S_OK;
    111113}
     
    113115void Snapshot::FinalRelease()
    114116{
    115     LogFlowMember(("Snapshot::FinalRelease()\n"));
     117    LogFlowThisFunc(("\n"));
    116118    uninit();
    117119}
     
    135137                       Snapshot *aParent)
    136138{
    137     LogFlowMember(("Snapshot::init(uuid: %s, aParent->uuid=%s)\n", aId.toString().c_str(), (aParent) ? aParent->m->uuid.toString().c_str() : ""));
     139    LogFlowThisFunc(("uuid=%s aParent->uuid=%s\n", aId.toString().c_str(), (aParent) ? aParent->m->uuid.toString().c_str() : ""));
    138140
    139141    ComAssertRet(!aId.isEmpty() && !aName.isEmpty() && aMachine, E_INVALIDARG);
     
    175177void Snapshot::uninit()
    176178{
    177     LogFlowMember(("Snapshot::uninit()\n"));
     179    LogFlowThisFunc(("\n"));
    178180
    179181    /* Enclose the state transition Ready->InUninit->NotReady */
     
    210212
    211213/**
    212  *  Discards the current snapshot by removing it from the tree of snapshots
     214 *  Delete the current snapshot by removing it from the tree of snapshots
    213215 *  and reparenting its children.
    214216 *
     
    221223 *  lock in write mode AND the machine state must be DeletingSnapshot.
    222224 */
    223 void Snapshot::beginDiscard()
     225void Snapshot::beginSnapshotDelete()
    224226{
    225227    AutoCaller autoCaller(this);
     
    231233    Assert(m->pMachine->isWriteLockOnCurrentThread());
    232234
    233     // the snapshot must have only one child when discarded or no children at all
     235    // the snapshot must have only one child when being deleted or no children at all
    234236    AssertReturnVoid(m->llChildren.size() <= 1);
    235237
     
    237239
    238240    /// @todo (dmik):
    239     //  when we introduce clones later, discarding the snapshot
    240     //  will affect the current and first snapshots of clones, if they are
    241     //  direct children of this snapshot. So we will need to lock machines
    242     //  associated with child snapshots as well and update mCurrentSnapshot
    243     //  and/or mFirstSnapshot fields.
     241    //  when we introduce clones later, deleting the snapshot will affect
     242    //  the current and first snapshots of clones, if they are direct children
     243    //  of this snapshot. So we will need to lock machines associated with
     244    //  child snapshots as well and update mCurrentSnapshot and/or
     245    //  mFirstSnapshot fields.
    244246
    245247    if (this == m->pMachine->mData->mCurrentSnapshot)
     
    11481150};
    11491151
    1150 /** Discard snapshot task */
     1152/** Delete snapshot task */
    11511153struct SessionMachine::DeleteSnapshotTask
    11521154    : public SessionMachine::SnapshotTask
     
    16851687    try
    16861688    {
    1687         /* discard the saved state file if the machine was Saved prior to this
     1689        /* Delete the saved state file if the machine was Saved prior to this
    16881690         * operation */
    16891691        if (aTask.machineStateBackup == MachineState_Saved)
     
    18111813        int saveFlags = 0;
    18121814
    1813         /* we have already discarded the current state, so set the execution
    1814          * state accordingly no matter of the discard snapshot result */
     1815        /* we have already deleted the current state, so set the execution
     1816         * state accordingly no matter of the delete snapshot result */
    18151817        if (!mSSData->mStateFilePath.isEmpty())
    18161818            setMachineState(MachineState_Saved);
     
    18721874            LogFlowThisFunc(("Deleting old current state in differencing image '%s'\n", pMedium->getName().raw()));
    18731875
    1874             HRESULT rc2 = pMedium->deleteStorageAndWait(NULL /*aProgress*/, &fNeedsSaveSettings);
     1876            HRESULT rc2 = pMedium->deleteStorage(NULL /* aProgress */,
     1877                                                 true /* aWait */,
     1878                                                 &fNeedsSaveSettings);
    18751879            // ignore errors here because we cannot roll back after saveSettings() above
    18761880            if (SUCCEEDED(rc2))
     
    19711975                        childrenCount);
    19721976
    1973     /* If the snapshot being discarded is the current one, ensure current
     1977    /* If the snapshot being deleted is the current one, ensure current
    19741978     * settings are committed and saved.
    19751979     */
     
    20172021            if (type != MediumType_Writethrough) // writethrough images are unaffected by snapshots, so do nothing for them
    20182022            {
    2019                 // normal or immutable: then this will need to be discarded
     2023                // normal or immutable media need attention
    20202024                ++ulOpCount;
    20212025                ulTotalWeight += (ULONG)(pHD->getSize() / _1M);
     
    20692073 * Helper struct for SessionMachine::deleteSnapshotHandler().
    20702074 */
    2071 struct MediumDiscardRec
    2072 {
    2073     MediumDiscardRec()
    2074         : chain(NULL)
     2075struct MediumDeleteRec
     2076{
     2077    MediumDeleteRec()
     2078        : mpMediumLockList(NULL)
    20752079    {}
    20762080
    2077     MediumDiscardRec(const ComObjPtr<Medium> &aHd,
    2078                      Medium::MergeChain *aChain = NULL)
    2079         : hd(aHd),
    2080           chain(aChain)
     2081    MediumDeleteRec(const ComObjPtr<Medium> &aHd,
     2082                    const ComObjPtr<Medium> &aSource,
     2083                    const ComObjPtr<Medium> &aTarget,
     2084                    bool fMergeForward,
     2085                    const ComObjPtr<Medium> &aParentForTarget,
     2086                    const MediaList &aChildrenToReparent,
     2087                    MediumLockList *aMediumLockList)
     2088        : mpHD(aHd),
     2089          mpSource(aSource),
     2090          mpTarget(aTarget),
     2091          mfMergeForward(fMergeForward),
     2092          mpParentForTarget(aParentForTarget),
     2093          mChildrenToReparent(aChildrenToReparent),
     2094          mpMediumLockList(aMediumLockList)
    20812095    {}
    20822096
    2083     MediumDiscardRec(const ComObjPtr<Medium> &aHd,
    2084                      Medium::MergeChain *aChain,
    2085                      const ComObjPtr<Medium> &aReplaceHd,
    2086                      const ComObjPtr<MediumAttachment> &aReplaceHda,
    2087                      const Guid &aSnapshotId)
    2088         : hd(aHd),
    2089           chain(aChain),
    2090           replaceHd(aReplaceHd),
    2091           replaceHda(aReplaceHda),
    2092           snapshotId(aSnapshotId)
     2097    MediumDeleteRec(const ComObjPtr<Medium> &aHd,
     2098                    const ComObjPtr<Medium> &aSource,
     2099                    const ComObjPtr<Medium> &aTarget,
     2100                    bool fMergeForward,
     2101                    const ComObjPtr<Medium> &aParentForTarget,
     2102                    const MediaList &aChildrenToReparent,
     2103                    MediumLockList *aMediumLockList,
     2104                    const ComObjPtr<MediumAttachment> &aReplaceHda,
     2105                    const Guid &aSnapshotId)
     2106        : mpHD(aHd),
     2107          mpSource(aSource),
     2108          mpTarget(aTarget),
     2109          mfMergeForward(fMergeForward),
     2110          mpParentForTarget(aParentForTarget),
     2111          mChildrenToReparent(aChildrenToReparent),
     2112          mpMediumLockList(aMediumLockList),
     2113          mpReplaceHda(aReplaceHda),
     2114          mSnapshotId(aSnapshotId)
    20932115    {}
    20942116
    2095     ComObjPtr<Medium> hd;
    2096     Medium::MergeChain *chain;
     2117    ComObjPtr<Medium> mpHD;
     2118    ComObjPtr<Medium> mpSource;
     2119    ComObjPtr<Medium> mpTarget;
     2120    bool mfMergeForward;
     2121    ComObjPtr<Medium> mpParentForTarget;
     2122    MediaList mChildrenToReparent;
     2123    MediumLockList *mpMediumLockList;
    20972124    /* these are for the replace hard disk case: */
    2098     ComObjPtr<Medium> replaceHd;
    2099     ComObjPtr<MediumAttachment> replaceHda;
    2100     Guid snapshotId;
     2125    ComObjPtr<MediumAttachment> mpReplaceHda;
     2126    Guid mSnapshotId;
    21012127};
    21022128
    2103 typedef std::list <MediumDiscardRec> MediumDiscardRecList;
     2129typedef std::list<MediumDeleteRec> MediumDeleteRecList;
    21042130
    21052131/**
     
    21382164    }
    21392165
    2140     MediumDiscardRecList toDiscard;
     2166    MediumDeleteRecList toDelete;
    21412167
    21422168    HRESULT rc = S_OK;
     
    21452171    bool fNeedsSaveSettings = false;            // VirtualBox.xml
    21462172
    2147     Guid snapshotId1;
     2173    Guid snapshotId;
    21482174
    21492175    try
     
    21592185        ComObjPtr<SnapshotMachine> pSnapMachine = aTask.pSnapshot->getSnapshotMachine();
    21602186        // no need to lock the snapshot machine since it is const by definiton
     2187        Guid machineId = pSnapMachine->getId();
    21612188
    21622189        // save the snapshot ID (for callbacks)
    2163         snapshotId1 = aTask.pSnapshot->getId();
     2190        snapshotId = aTask.pSnapshot->getId();
    21642191
    21652192        // first pass:
     
    21772204            ComObjPtr<MediumAttachment> &pAttach = *it;
    21782205            AutoReadLock attachLock(pAttach COMMA_LOCKVAL_SRC_POS);
    2179             if (pAttach->getType() == DeviceType_HardDisk)
     2206            if (pAttach->getType() != DeviceType_HardDisk)
     2207                continue;
     2208
     2209            ComObjPtr<Medium> pHD = pAttach->getMedium();
     2210            Assert(!pHD.isNull());
     2211
    21802212            {
    2181                 Assert(pAttach->getMedium());
    2182                 ComObjPtr<Medium> pHD = pAttach->getMedium();
    2183 
     2213                // writethrough images are unaffected by snapshots, skip them
     2214                AutoReadLock medlock(pHD COMMA_LOCKVAL_SRC_POS);
     2215                MediumType_T type = pHD->getType();
     2216                if (type == MediumType_Writethrough)
     2217                    continue;
     2218            }
     2219
     2220#ifdef DEBUG
     2221            pHD->dumpBackRefs();
     2222#endif
     2223
     2224            // needs to be merged with child or deleted, check prerequisites
     2225            ComObjPtr<Medium> pTarget;
     2226            ComObjPtr<Medium> pSource;
     2227            bool fMergeForward = false;
     2228            ComObjPtr<Medium> pParentForTarget;
     2229            MediaList childrenToReparent;
     2230            MediumLockList *pMediumLockList = NULL;
     2231            rc = prepareDeleteSnapshotMedium(pHD, machineId, snapshotId,
     2232                                             pSource, pTarget, fMergeForward,
     2233                                             pParentForTarget,
     2234                                             childrenToReparent,
     2235                                             pMediumLockList);
     2236            if (FAILED(rc)) throw rc;
     2237
     2238            // for simplicity, we merge pHd onto its child (forward merge), not
     2239            // the other way round, because that saves us from updating the
     2240            // attachments for the machine that follows the snapshot (next
     2241            // snapshot or real machine), unless it's a base image:
     2242
     2243            if (pMediumLockList != NULL && !fMergeForward)
     2244            {
     2245                // parent is null -> this disk is a base hard disk: we will
     2246                // then do a backward merge, i.e. merge its only child onto the
     2247                // base disk. Here we need then to update the attachment that
     2248                // refers to the child and have it point to the parent instead
     2249                Assert(pHD->getParent().isNull());
     2250                Assert(pHD->getChildren().size() == 1);
     2251
     2252                ComObjPtr<Medium> pReplaceHD = pHD->getChildren().front();
     2253
     2254                ComAssertThrow(pReplaceHD == pSource, E_FAIL);
     2255
     2256                const Guid *pReplaceMachineId = pSource->getFirstMachineBackrefId();
     2257                NOREF(pReplaceMachineId);
     2258                Assert(pReplaceMachineId);
     2259                Assert(*pReplaceMachineId == mData->mUuid);
     2260
     2261                Guid snapshotId2;
     2262                const Guid *pSnapshotId = pSource->getFirstMachineBackrefSnapshotId();
     2263                if (pSnapshotId)
     2264                    snapshotId2 = *pSnapshotId;
     2265
     2266                HRESULT rc2 = S_OK;
     2267
     2268                attachLock.release();
     2269
     2270                // First we must detach the child otherwise mergeTo() will
     2271                // will assert because it is going to delete the child.
     2272                // Adjust the backreferences:
     2273                // 1) detach the first child hard disk
     2274                rc2 = pSource->detachFrom(mData->mUuid, snapshotId2);
     2275                AssertComRC(rc2);
     2276                // 2) attach to machine and snapshot
     2277                rc2 = pHD->attachTo(mData->mUuid, snapshotId2);
     2278                AssertComRC(rc2);
     2279
     2280                /* replace the hard disk in the attachment object */
     2281                if (snapshotId2.isEmpty())
    21842282                {
    2185                     // writethrough images are unaffected by snapshots, so do nothing for them
    2186                     AutoReadLock medlock(pHD COMMA_LOCKVAL_SRC_POS);
    2187                     MediumType_T type = pHD->getType();
    2188                     if (type == MediumType_Writethrough)
    2189                         continue;
     2283                    /* in current state */
     2284                    AssertBreak(pAttach = findAttachment(mMediaData->mAttachments, pSource));
    21902285                }
    2191 
    2192                 // image is normal or immutable: then this will need to be discarded
    2193 
    2194                 // do not lock medium now, prepareDiscard() has a write lock which will hang otherwise
    2195 
    2196 #ifdef DEBUG
    2197                 pHD->dumpBackRefs();
    2198 #endif
    2199 
    2200                 Medium::MergeChain *chain = NULL;
    2201 
    2202                 // needs to be discarded (merged with the child if any), check prerequisites
    2203                 rc = pHD->prepareDiscard(chain);
    2204                 if (FAILED(rc)) throw rc;
    2205 
    2206                 // for simplicity, we merge pHd onto its child (forward merge), not the
    2207                 // other way round, because that saves us from updating the attachments
    2208                 // for the machine that follows the snapshot (next snapshot or real machine),
    2209                 // unless it's a base image:
    2210 
    2211                 if (    pHD->getParent().isNull()
    2212                      && chain != NULL
    2213                    )
     2286                else
    22142287                {
    2215                     // parent is null -> this disk is a base hard disk: we will
    2216                     // then do a backward merge, i.e. merge its only child onto
    2217                     // the base disk; prepareDiscard() does necessary checks.
    2218                     // So here we need then to update the attachment that refers
    2219                     // to the child and have it point to the parent instead
    2220 
    2221                     /* The below assert would be nice but I don't want to move
    2222                      * Medium::MergeChain to the header just for that
    2223                      * Assert(!chain->isForward()); */
    2224 
    2225                     // prepareDiscard() should have raised an error already
    2226                     // if there was more than one child
    2227                     Assert(pHD->getChildren().size() == 1);
    2228 
    2229                     ComObjPtr<Medium> pReplaceHD = pHD->getChildren().front();
    2230 
    2231                     const Guid *pReplaceMachineId = pReplaceHD->getFirstMachineBackrefId();
    2232                     NOREF(pReplaceMachineId);
    2233                     Assert(pReplaceMachineId);
    2234                     Assert(*pReplaceMachineId == mData->mUuid);
    2235 
    2236                     Guid snapshotId;
    2237                     const Guid *pSnapshotId = pReplaceHD->getFirstMachineBackrefSnapshotId();
    2238                     if (pSnapshotId)
    2239                         snapshotId = *pSnapshotId;
    2240 
    2241                     HRESULT rc2 = S_OK;
    2242 
    2243                     attachLock.release();
    2244 
    2245                     // First we must detach the child (otherwise mergeTo() called
    2246                     // by discard() will assert because it will be going to delete
    2247                     // the child), so adjust the backreferences:
    2248                     // 1) detach the first child hard disk
    2249                     rc2 = pReplaceHD->detachFrom(mData->mUuid, snapshotId);
     2288                    /* in snapshot */
     2289                    ComObjPtr<Snapshot> snapshot;
     2290                    rc2 = findSnapshot(snapshotId2, snapshot);
    22502291                    AssertComRC(rc2);
    2251                     // 2) attach to machine and snapshot
    2252                     rc2 = pHD->attachTo(mData->mUuid, snapshotId);
    2253                     AssertComRC(rc2);
    2254 
    2255                     /* replace the hard disk in the attachment object */
    2256                     if (snapshotId.isEmpty())
    2257                     {
    2258                         /* in current state */
    2259                         AssertBreak(pAttach = findAttachment(mMediaData->mAttachments, pReplaceHD));
    2260                     }
    2261                     else
    2262                     {
    2263                         /* in snapshot */
    2264                         ComObjPtr<Snapshot> snapshot;
    2265                         rc2 = findSnapshot(snapshotId, snapshot);
    2266                         AssertComRC(rc2);
    2267 
    2268                         /* don't lock the snapshot; cannot be modified outside */
    2269                         MediaData::AttachmentList &snapAtts = snapshot->getSnapshotMachine()->mMediaData->mAttachments;
    2270                         AssertBreak(pAttach = findAttachment(snapAtts, pReplaceHD));
    2271                     }
    2272 
    2273                     AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
    2274                     pAttach->updateMedium(pHD, false /* aImplicit */);
    2275 
    2276                     toDiscard.push_back(MediumDiscardRec(pHD,
    2277                                                          chain,
    2278                                                          pReplaceHD,
    2279                                                          pAttach,
    2280                                                          snapshotId));
    2281                     continue;
     2292
     2293                    /* don't lock the snapshot; cannot be modified outside */
     2294                    MediaData::AttachmentList &snapAtts = snapshot->getSnapshotMachine()->mMediaData->mAttachments;
     2295                    AssertBreak(pAttach = findAttachment(snapAtts, pSource));
    22822296                }
    22832297
    2284                 toDiscard.push_back(MediumDiscardRec(pHD, chain));
     2298                AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
     2299                pAttach->updateMedium(pHD, false /* aImplicit */);
     2300
     2301                toDelete.push_back(MediumDeleteRec(pHD, pSource, pTarget,
     2302                                                   fMergeForward,
     2303                                                   pParentForTarget,
     2304                                                   childrenToReparent,
     2305                                                   pMediumLockList,
     2306                                                   pAttach, snapshotId2));
    22852307            }
     2308            else
     2309                toDelete.push_back(MediumDeleteRec(pHD, pSource, pTarget,
     2310                                                   fMergeForward,
     2311                                                   pParentForTarget,
     2312                                                   childrenToReparent,
     2313                                                   pMediumLockList));
    22862314        }
    22872315
     
    22892317        multiLock.release();
    22902318
     2319        /** @todo r=klaus the comment below makes only limited sense to me.
     2320         * Deleting the snapshot early has some benefits, but also the drawback
     2321         * that a normal user surely has difficulties to perform the not
     2322         * completed merge operations later. Before IMedium::mergeTo is added
     2323         * to VBoxManage it actually is completely impossible. */
     2324
    22912325        /* Now we checked that we can successfully merge all normal hard disks
    22922326         * (unless a runtime error like end-of-disc happens). Prior to
    2293          * performing the actual merge, we want to discard the snapshot itself
     2327         * performing the actual merge, we want to delete the snapshot itself
    22942328         * and remove it from the XML file to make sure that a possible merge
    2295          * ruintime error will not make this snapshot inconsistent because of
     2329         * runtime error will not make this snapshot inconsistent because of
    22962330         * the partially merged or corrupted hard disks */
    22972331
    22982332        /* second pass: */
    2299         LogFlowThisFunc(("2: Discarding snapshot...\n"));
     2333        LogFlowThisFunc(("2: Deleting snapshot...\n"));
    23002334
    23012335        {
     
    23072341            Utf8Str stateFilePath = aTask.pSnapshot->stateFilePath();
    23082342
    2309             // Note that discarding the snapshot will deassociate it from the
     2343            // Note that deleting the snapshot will deassociate it from the
    23102344            // hard disks which will allow the merge+delete operation for them
    2311             aTask.pSnapshot->beginDiscard();
     2345            aTask.pSnapshot->beginSnapshotDelete();
    23122346            aTask.pSnapshot->uninit();
    23132347                    // this requests the machine lock in turn when deleting all the children
     
    23202354            if (!stateFilePath.isEmpty())
    23212355            {
    2322                 aTask.pProgress->SetNextOperation(Bstr(tr("Discarding the execution state")),
     2356                aTask.pProgress->SetNextOperation(Bstr(tr("Deleting the execution state")),
    23232357                                                  1);        // weight
    23242358
     
    23302364            /// saveSnapshotSettings fails. Actually, we may call
    23312365            /// #saveSnapshotSettings() with a special flag that will tell it to
    2332             /// skip the given snapshot as if it would have been discarded and
    2333             /// only actually discard it if the save operation succeeds.
     2366            /// skip the given snapshot as if it would have been deleted and
     2367            /// only actually delete it if the save operation succeeds.
    23342368        }
    23352369
    2336         /* here we come when we've irrevesibly discarded the snapshot which
     2370        /* here we come when we've irreversibly eleted the snapshot which
    23372371         * means that the VM settigns (our relevant changes to mData) need to be
    23382372         * saved too */
     
    23472381        /// snapshot itself has been already deleted (and interpret these
    23482382        /// warnings properly on the GUI side)
    2349         for (MediumDiscardRecList::iterator it = toDiscard.begin();
    2350              it != toDiscard.end();)
     2383        for (MediumDeleteRecList::iterator it = toDelete.begin();
     2384             it != toDelete.end();)
    23512385        {
    2352             rc = it->hd->discard(aTask.pProgress,
    2353                                  (ULONG)(it->hd->getSize() / _1M),          // weight
    2354                                  it->chain,
    2355                                  &fNeedsSaveSettings);
     2386            const ComObjPtr<Medium> &pMedium(it->mpHD);
     2387            ULONG ulWeight;
     2388
     2389            {
     2390                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     2391                ulWeight = (ULONG)(pMedium->getSize() / _1M);
     2392            }
     2393
     2394            aTask.pProgress->SetNextOperation(BstrFmt(tr("Merging differencing image '%s'"),
     2395                                              pMedium->getName().raw()),
     2396                                              ulWeight);
     2397
     2398            if (it->mpMediumLockList == NULL)
     2399            {
     2400                /* no real merge needed, just updating state and delete
     2401                 * diff files if necessary */
     2402                AutoMultiWriteLock2 mLock(&mParent->getMediaTreeLockHandle(), pMedium->lockHandle() COMMA_LOCKVAL_SRC_POS);
     2403
     2404                Assert(   !it->mfMergeForward
     2405                       || pMedium->getChildren().size() == 0);
     2406
     2407                /* Delete the differencing hard disk (has no children). Two
     2408                 * exceptions: if it's the last medium in the chain or if it's
     2409                 * a backward merge we don't want to handle due to complextity.
     2410                 * In both cases leave the image in place. If it's the first
     2411                 * exception the user can delete it later if he wants. */
     2412                if (!pMedium->getParent().isNull())
     2413                {
     2414                    Assert(pMedium->getState() == MediumState_Deleting);
     2415                    rc = pMedium->deleteStorage(&aTask.pProgress,
     2416                                                true /* aWait */,
     2417                                                &fNeedsSaveSettings);
     2418                    if (SUCCEEDED(rc))
     2419                        pMedium->uninit();
     2420                }
     2421            }
     2422            else
     2423            {
     2424                /* Normal merge operation, in the direction decided earlier. */
     2425                rc = it->mpSource->mergeTo(it->mpTarget, it->mfMergeForward,
     2426                                           it->mpParentForTarget,
     2427                                           it->mChildrenToReparent,
     2428                                           it->mpMediumLockList,
     2429                                           &aTask.pProgress, true /* aWait */,
     2430                                           &fNeedsSaveSettings);
     2431                if (SUCCEEDED(rc))
     2432                {
     2433                    /* On success delete the no longer needed medium lock
     2434                     * list. This unlocks the media as well. */
     2435                    delete it->mpMediumLockList;
     2436                    it->mpMediumLockList = NULL;
     2437                    it->mpSource->uninit();
     2438                }
     2439            }
     2440
    23562441            if (FAILED(rc)) throw rc;
    23572442
    2358             /* prevent from calling cancelDiscard() */
    2359             it = toDiscard.erase(it);
     2443            /* prevent from calling cancelDeleteSnapshotMedium() */
     2444            it = toDelete.erase(it);
    23602445        }
    23612446    }
     
    23642449    if (FAILED(rc))
    23652450    {
    2366         HRESULT rc2 = S_OK;
    2367 
    23682451        AutoMultiWriteLock2 multiLock(this->lockHandle(),                   // machine
    23692452                                      &mParent->getMediaTreeLockHandle()    // media tree
     
    23712454
    23722455        // un-prepare the remaining hard disks
    2373         for (MediumDiscardRecList::const_iterator it = toDiscard.begin();
    2374              it != toDiscard.end();
     2456        for (MediumDeleteRecList::const_iterator it = toDelete.begin();
     2457             it != toDelete.end();
    23752458             ++it)
    23762459        {
    2377             it->hd->cancelDiscard(it->chain);
    2378 
    2379             if (!it->replaceHd.isNull())
    2380             {
    2381                 rc2 = it->replaceHd->attachTo(mData->mUuid, it->snapshotId);
    2382                 AssertComRC(rc2);
    2383 
    2384                 rc2 = it->hd->detachFrom(mData->mUuid, it->snapshotId);
    2385                 AssertComRC(rc2);
    2386 
    2387                 AutoWriteLock attLock(it->replaceHda COMMA_LOCKVAL_SRC_POS);
    2388                 it->replaceHda->updateMedium(it->replaceHd, false /* aImplicit */);
    2389             }
     2460            cancelDeleteSnapshotMedium(it->mpHD, it->mpSource, it->mpTarget,
     2461                                       it->mChildrenToReparent,
     2462                                       it->mpMediumLockList,
     2463                                       it->mpReplaceHda, it->mSnapshotId);
    23902464        }
    23912465    }
     
    24232497
    24242498    if (SUCCEEDED(rc))
    2425         mParent->onSnapshotDeleted(mData->mUuid, snapshotId1);
     2499        mParent->onSnapshotDeleted(mData->mUuid, snapshotId);
    24262500
    24272501    LogFlowThisFunc(("Done deleting snapshot (rc=%08X)\n", rc));
     
    24292503}
    24302504
     2505/**
     2506 * Checks that this hard disk (part of a snapshot) may be deleted/merged and
     2507 * performs necessary state changes. Must not be called for writethrough disks
     2508 * because there is nothing to delete/merge then.
     2509 *
     2510 * This method is to be called prior to calling #deleteSnapshotMedium().
     2511 * If #deleteSnapshotMedium() is not called or fails, the state modifications
     2512 * performed by this method must be undone by #cancelDeleteSnapshotMedium().
     2513 *
     2514 * @param aHD           Hard disk which is connected to the snapshot.
     2515 * @param aMachineId    UUID of machine this hard disk is attached to.
     2516 * @param aSnapshotId   UUID of snapshot this hard disk is attached to. May
     2517 *                      be a zero UUID if no snapshot is applicable.
     2518 * @param aSource       Source hard disk for merge (out).
     2519 * @param aTarget       Target hard disk for merge (out).
     2520 * @param aMergeForward Merge direction decision (out).
     2521 * @param aParentForTarget New parent if target needs to be reparented (out).
     2522 * @param aChildrenToReparent Children which have to be reparented to the
     2523 *                      target (out).
     2524 * @param aMediumLockList Where to store the created medium lock list (may
     2525 *                      return NULL if no real merge is necessary).
     2526 *
     2527 * @note Caller must hold media tree lock for writing. This locks this object
     2528 *       and every medium object on the merge chain for writing.
     2529 */
     2530HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,
     2531                                                    const Guid &aMachineId,
     2532                                                    const Guid &aSnapshotId,
     2533                                                    ComObjPtr<Medium> &aSource,
     2534                                                    ComObjPtr<Medium> &aTarget,
     2535                                                    bool &aMergeForward,
     2536                                                    ComObjPtr<Medium> &aParentForTarget,
     2537                                                    MediaList &aChildrenToReparent,
     2538                                                    MediumLockList * &aMediumLockList)
     2539{
     2540    Assert(mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
     2541
     2542    AutoWriteLock alock(aHD COMMA_LOCKVAL_SRC_POS);
     2543
     2544    // Medium must not be writethrough at this point
     2545    AssertReturn(aHD->getType() != MediumType_Writethrough, E_FAIL);
     2546
     2547    aMediumLockList = NULL;
     2548
     2549    if (aHD->getChildren().size() == 0)
     2550    {
     2551        /* special treatment of the last hard disk in the chain: */
     2552        if (aHD->getParent().isNull())
     2553        {
     2554            /* lock only, to prevent any usage until the snapshot deletion
     2555             * is completed */
     2556            return aHD->LockWrite(NULL);
     2557        }
     2558
     2559        /* the differencing hard disk w/o children will be deleted, protect it
     2560         * from attaching to other VMs (this is why Deleting) */
     2561        return aHD->markForDeletion();
     2562   }
     2563
     2564    /* not going multi-merge as it's too expensive */
     2565    if (aHD->getChildren().size() > 1)
     2566        return setError(E_FAIL,
     2567                        tr ("Hard disk '%s' has more than one child hard disk (%d)"),
     2568                        aHD->getLocationFull().raw(),
     2569                        aHD->getChildren().size());
     2570
     2571    ComObjPtr<Medium> pChild = aHD->getChildren().front();
     2572
     2573    /* we keep this locked, so lock the affected child to make sure the lock
     2574     * order is correct when calling prepareMergeTo() */
     2575    AutoWriteLock childLock(pChild COMMA_LOCKVAL_SRC_POS);
     2576
     2577    /* the rest is a normal merge setup */
     2578    if (aHD->getParent().isNull())
     2579    {
     2580        /* base hard disk, backward merge */
     2581        const Guid *pMachineId1 = pChild->getFirstMachineBackrefId();
     2582        const Guid *pMachineId2 = aHD->getFirstMachineBackrefId();
     2583        if (pMachineId1 && pMachineId2 && *pMachineId1 != *pMachineId2)
     2584        {
     2585            /* backward merge is too tricky, we'll just detach on snapshot
     2586             * deletion, so lock only, to prevent any usage */
     2587            return aHD->LockWrite(NULL);
     2588        }
     2589
     2590        aSource = pChild;
     2591        aTarget = aHD;
     2592    }
     2593    else
     2594    {
     2595        /* forward merge */
     2596        aSource = aHD;
     2597        aTarget = pChild;
     2598    }
     2599
     2600    return aSource->prepareMergeTo(aTarget, &aMachineId, &aSnapshotId,
     2601                                   aMergeForward, aParentForTarget,
     2602                                   aChildrenToReparent, aMediumLockList);
     2603}
     2604
     2605/**
     2606 * Cancels the deletion/merging of this hard disk (part of a snapshot). Undoes
     2607 * what #prepareDeleteSnapshotMedium() did. Must be called if
     2608 * #deleteSnapshotMedium() is not called or fails.
     2609 *
     2610 * @param aHD           Hard disk which is connected to the snapshot.
     2611 * @param aSource       Source hard disk for merge.
     2612 * @param aTarget       Source hard disk for merge.
     2613 * @param aChildrenToReparent Children to unlock.
     2614 * @param aMediumLockList Medium locks to cancel.
     2615 * @param aReplaceHda   Hard disk attachment to restore.
     2616 * @param aSnapshotId   Snapshot id to attach the medium to.
     2617 *
     2618 * @note Locks the medium tree and the hard disks in the chain for writing.
     2619 */
     2620void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,
     2621                                                const ComObjPtr<Medium> &aSource,
     2622                                                const ComObjPtr<Medium> &aTarget,
     2623                                                const MediaList &aChildrenToReparent,
     2624                                                MediumLockList *aMediumLockList,
     2625                                                const ComObjPtr<MediumAttachment> &aReplaceHda,
     2626                                                const Guid &aSnapshotId)
     2627{
     2628    if (aMediumLockList == NULL)
     2629    {
     2630        AutoMultiWriteLock2 mLock(&mParent->getMediaTreeLockHandle(), aHD->lockHandle() COMMA_LOCKVAL_SRC_POS);
     2631
     2632        Assert(aHD->getChildren().size() == 0);
     2633
     2634        if (aHD->getParent().isNull())
     2635        {
     2636            HRESULT rc = aHD->UnlockWrite(NULL);;
     2637            AssertComRC(rc);
     2638        }
     2639        else
     2640        {
     2641            HRESULT rc = aHD->unmarkForDeletion();
     2642            AssertComRC(rc);
     2643        }
     2644    }
     2645    else
     2646        aSource->cancelMergeTo(aChildrenToReparent, aMediumLockList);
     2647
     2648    if (!aReplaceHda.isNull())
     2649    {
     2650        HRESULT rc = aTarget->attachTo(mData->mUuid, aSnapshotId);
     2651        AssertComRC(rc);
     2652
     2653        rc = aHD->detachFrom(mData->mUuid, aSnapshotId);
     2654        AssertComRC(rc);
     2655
     2656        AutoWriteLock attLock(aReplaceHda COMMA_LOCKVAL_SRC_POS);
     2657        aReplaceHda->updateMedium(aTarget, false /* aImplicit */);
     2658    }
     2659}
  • trunk/src/VBox/Main/idl/VirtualBox.xidl

    r28377 r28401  
    91599159  <interface
    91609160    name="IMedium" extends="$unknown"
    9161     uuid="aa8167ba-df72-4738-b740-9b84377ba9f1"
     9161    uuid="d709160c-303f-4ead-b7ef-53ffa26aa861"
    91629162    wsmap="managed"
    91639163  >
     
    1035110351        </note>
    1035210352      </desc>
    10353       <param name="targetId" type="uuid" mod="string" dir="in">
    10354         <desc>UUID of the target ancestor or descendant medium.</desc>
     10353      <param name="target" type="IMedium" dir="in">
     10354        <desc>Target medium.</desc>
    1035510355      </param>
    1035610356      <param name="progress" type="IProgress" dir="return">
  • trunk/src/VBox/Main/include/Logging.h

    r14949 r28401  
     1/* $Id$ */
     2
    13/** @file
    24 *
     
    57
    68/*
    7  * Copyright (C) 2006-2007 Sun Microsystems, Inc.
     9 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
    810 *
    911 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    5456#include <iprt/assert.h>
    5557
    56 /** @deprecated Please use LogFlowThisFunc instead! */
    57 #define LogFlowMember(m)     \
    58     do { LogFlow (("{%p} ", this)); LogFlow (m); } while (0)
    59 
    6058/** @def MyLogIt
    6159 * Copy of LogIt that works even when logging is completely disabled (e.g. in
  • trunk/src/VBox/Main/include/MachineImpl.h

    r28343 r28401  
    33/** @file
    44 *
    5  * VirtualBox COM class declaration
     5 * VirtualBox COM class implementation
    66 */
    77
     
    2929#include "VRDPServerImpl.h"
    3030#include "MediumAttachmentImpl.h"
     31#include "MediumLock.h"
    3132#include "NetworkAdapterImpl.h"
    3233#include "AudioAdapterImpl.h"
     
    148149            ComObjPtr<SessionMachine> mMachine;
    149150
    150             /**
    151              * Successfully locked media list. The 2nd value in the pair is true
    152              * if the medium is locked for writing and false if locked for
    153              * reading.
    154              */
    155             typedef std::list<std::pair<ComPtr<IMedium>, bool > > LockedMedia;
    156             LockedMedia mLockedMedia;
     151            /** Medium object lock collection. */
     152            MediumLockListMap mLockedMedia;
    157153        };
    158154
     
    316312     *  The usage policy is the same as for HWData, but a separate structure
    317313     *  is necessary because hard disk data requires different procedures when
    318      *  taking or discarding snapshots, etc.
     314     *  taking or deleting snapshots, etc.
    319315     *
    320316     *  The data variable is |mMediaData|.
     
    997993    void restoreSnapshotHandler(RestoreSnapshotTask &aTask);
    998994
     995    HRESULT prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,
     996                                        const Guid &machineId,
     997                                        const Guid &snapshotId,
     998                                        ComObjPtr<Medium> &aSource,
     999                                        ComObjPtr<Medium> &aTarget,
     1000                                        bool &fMergeForward,
     1001                                        ComObjPtr<Medium> &pParentForTarget,
     1002                                        MediaList &aChildrenToReparent,
     1003                                        MediumLockList * &aMediumLockList);
     1004    void cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,
     1005                                    const ComObjPtr<Medium> &aSource,
     1006                                    const ComObjPtr<Medium> &aTarget,
     1007                                    const MediaList &aChildrenToReparent,
     1008                                    MediumLockList *aMediumLockList,
     1009                                    const ComObjPtr<MediumAttachment> &aReplaceHda,
     1010                                    const Guid &aSnapshotId);
     1011
    9991012    HRESULT lockMedia();
    10001013    void unlockMedia();
  • trunk/src/VBox/Main/include/MediumImpl.h

    • Property svn:keywords changed from Date Revision Author Id to Author Date Id Revision
    r27831 r28401  
    11/* $Id$ */
     2
    23/** @file
    34 *
     
    2526
    2627#include "VirtualBoxBase.h"
     28#include "MediumLock.h"
    2729
    2830class Progress;
     
    4547{
    4648public:
    47     class MergeChain;
    48     class ImageChain;
    49 
    5049    VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(Medium)
    5150
     
    144143                                 MediumVariant_T aVariant,
    145144                                 IProgress **aProgress);
    146     STDMETHOD(MergeTo)(IN_BSTR aTargetId, IProgress **aProgress);
     145    STDMETHOD(MergeTo)(IMedium *aTarget, IProgress **aProgress);
    147146    STDMETHOD(CloneTo)(IMedium *aTarget, MediumVariant_T aVariant,
    148147                        IMedium *aParent, IProgress **aProgress);
     
    155154    const MediaList& getChildren() const;
    156155
     156    // unsafe methods for internal purposes only (ensure there is
     157    // a caller and a read lock before calling them!)
    157158    const Guid& getId() const;
    158159    MediumState_T getState() const;
     
    160161    const Utf8Str& getLocationFull() const;
    161162    uint64_t getSize() const;
     163    MediumType_T getType() const;
     164    Utf8Str getName();
    162165
    163166    HRESULT attachTo(const Guid &aMachineId,
     
    184187    HRESULT compareLocationTo(const char *aLocation, int &aResult);
    185188
    186     /**
    187      * Shortcut to #deleteStorage() that doesn't wait for operation completion
    188      * and implies the progress object will be used for waiting.
    189      */
    190     HRESULT deleteStorageNoWait(ComObjPtr<Progress> &aProgress)
    191     { return deleteStorage(&aProgress, false /* aWait */, NULL /* pfNeedsSaveSettings */); }
    192 
    193     /**
    194      * Shortcut to #deleteStorage() that wait for operation completion by
    195      * blocking the current thread.
    196      */
    197     HRESULT deleteStorageAndWait(ComObjPtr<Progress> *aProgress, bool *pfNeedsSaveSettings)
    198     { return deleteStorage(aProgress, true /* aWait */, pfNeedsSaveSettings); }
    199 
    200     /**
    201      * Shortcut to #createDiffStorage() that doesn't wait for operation
    202      * completion and implies the progress object will be used for waiting.
    203      */
    204     HRESULT createDiffStorageNoWait(ComObjPtr<Medium> &aTarget,
    205                                     MediumVariant_T aVariant,
    206                                     ComObjPtr<Progress> &aProgress)
    207     { return createDiffStorage(aTarget, aVariant, &aProgress, false /* aWait */, NULL /* pfNeedsSaveSettings*/ ); }
    208 
    209     /**
    210      * Shortcut to #createDiffStorage() that wait for operation completion by
    211      * blocking the current thread.
    212      */
    213     HRESULT createDiffStorageAndWait(ComObjPtr<Medium> &aTarget,
    214                                      MediumVariant_T aVariant,
    215                                      bool *pfNeedsSaveSettings)
    216     { return createDiffStorage(aTarget, aVariant, NULL /*aProgress*/, true /* aWait */, pfNeedsSaveSettings); }
    217 
    218     HRESULT prepareMergeTo(Medium *aTarget, MergeChain * &aChain,
    219                             bool aIgnoreAttachments = false);
    220 
    221     /**
    222      * Shortcut to #mergeTo() that doesn't wait for operation completion and
    223      * implies the progress object will be used for waiting.
    224      */
    225     HRESULT mergeToNoWait(MergeChain *aChain,
    226                           ComObjPtr<Progress> &aProgress)
    227     { return mergeTo(aChain, &aProgress, false /* aWait */, NULL /*pfNeedsSaveSettings*/); }
    228 
    229     /**
    230      * Shortcut to #mergeTo() that wait for operation completion by
    231      * blocking the current thread.
    232      */
    233     HRESULT mergeToAndWait(MergeChain *aChain,
    234                            ComObjPtr<Progress> *aProgress,
    235                            bool *pfNeedsSaveSettings)
    236     { return mergeTo(aChain, aProgress, true /* aWait */, pfNeedsSaveSettings); }
    237 
    238     void cancelMergeTo(MergeChain *aChain);
    239 
    240     Utf8Str getName();
    241 
    242     HRESULT prepareDiscard(MergeChain * &aChain);
    243     HRESULT discard(ComObjPtr<Progress> &aProgress, ULONG ulWeight, MergeChain *aChain, bool *pfNeedsSaveSettings);
    244     void cancelDiscard(MergeChain *aChain);
     189    HRESULT createMediumLockList(bool fMediumWritable, Medium *pToBeParent, MediumLockList &mediumLockList);
     190
     191    HRESULT createDiffStorage(ComObjPtr<Medium> &aTarget,
     192                              MediumVariant_T aVariant,
     193                              MediumLockList *pMediumLockList,
     194                              ComObjPtr<Progress> *aProgress,
     195                              bool aWait,
     196                              bool *pfNeedsSaveSettings);
     197
     198    HRESULT deleteStorage(ComObjPtr<Progress> *aProgress, bool aWait, bool *pfNeedsSaveSettings);
     199    HRESULT markForDeletion();
     200    HRESULT unmarkForDeletion();
     201
     202    HRESULT prepareMergeTo(const ComObjPtr<Medium> &pTarget,
     203                           const Guid *aMachineId,
     204                           const Guid *aSnapshotId,
     205                           bool &fMergeForward,
     206                           ComObjPtr<Medium> &pParentForTarget,
     207                           MediaList &aChildrenToReparent,
     208                           MediumLockList * &aMediumLockList);
     209    HRESULT mergeTo(const ComObjPtr<Medium> &pTarget,
     210                    bool &fMergeForward,
     211                    ComObjPtr<Medium> pParentForTarget,
     212                    const MediaList &aChildrenToReparent,
     213                    MediumLockList *aMediumLockList,
     214                    ComObjPtr<Progress> *aProgress,
     215                    bool aWait,
     216                    bool *pfNeedsSaveSettings);
     217    void cancelMergeTo(const MediaList &aChildrenToReparent,
     218                       MediumLockList *aMediumLockList);
    245219
    246220    /** Returns a preferred format for a differencing hard disk. */
    247221    Bstr preferredDiffFormat();
    248 
    249     // unsafe inline public methods for internal purposes only (ensure there is
    250     // a caller and a read lock before calling them!)
    251     MediumType_T getType() const;
    252222
    253223    /** For com::SupportErrorInfoImpl. */
     
    272242
    273243    HRESULT setStateError();
    274 
    275     HRESULT deleteStorage(ComObjPtr<Progress> *aProgress, bool aWait, bool *pfNeedsSaveSettings);
    276 
    277     HRESULT createDiffStorage(ComObjPtr<Medium> &aTarget,
    278                               MediumVariant_T aVariant,
    279                               ComObjPtr<Progress> *aProgress,
    280                               bool aWait,
    281                               bool *pfNeedsSaveSettings);
    282 
    283     HRESULT mergeTo(MergeChain *aChain,
    284                     ComObjPtr<Progress> *aProgress,
    285                     bool aWait,
    286                     bool *pfNeedsSaveSettings);
    287244
    288245    HRESULT setLocation(const Utf8Str &aLocation, const Utf8Str &aFormat = Utf8Str());
     
    321278    HRESULT runNow(Medium::Task *pTask, bool *pfNeedsSaveSettings);
    322279
    323     HRESULT taskThreadCreateBase(Medium::CreateBaseTask &task);
    324     HRESULT taskThreadCreateDiff(Medium::CreateDiffTask &task);
    325     HRESULT taskThreadMerge(Medium::MergeTask &task);
    326     HRESULT taskThreadClone(Medium::CloneTask &task);
    327     HRESULT taskThreadDelete(Medium::DeleteTask &task);
    328     HRESULT taskThreadReset(Medium::ResetTask &task);
    329     HRESULT taskThreadCompact(Medium::CompactTask &task);
     280    HRESULT taskCreateBaseHandler(Medium::CreateBaseTask &task);
     281    HRESULT taskCreateDiffHandler(Medium::CreateDiffTask &task);
     282    HRESULT taskMergeHandler(Medium::MergeTask &task);
     283    HRESULT taskCloneHandler(Medium::CloneTask &task);
     284    HRESULT taskDeleteHandler(Medium::DeleteTask &task);
     285    HRESULT taskResetHandler(Medium::ResetTask &task);
     286    HRESULT taskCompactHandler(Medium::CompactTask &task);
    330287
    331288    struct Data;            // opaque data struct, defined in MediumImpl.cpp
  • trunk/src/VBox/Main/include/SnapshotImpl.h

    r26044 r28401  
     1/* $Id$ */
     2
    13/** @file
    24 *
     
    57
    68/*
    7  * Copyright (C) 2006-2009 Sun Microsystems, Inc.
     9 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
    810 *
    911 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    7072    void uninit();
    7173
    72     void beginDiscard();
     74    void beginSnapshotDelete();
    7375
    7476    void deparent();
Note: See TracChangeset for help on using the changeset viewer.

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