VirtualBox

Changeset 33908 in vbox


Ignore:
Timestamp:
Nov 9, 2010 3:37:30 PM (15 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
67564
Message:

Main: sort Medium methods to have the same order in .h und .cpp

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

Legend:

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

    r33760 r33908  
    29432943
    29442944/**
     2945 * Returns the medium device type. Must have caller + locking!
     2946 * @return
     2947 */
     2948DeviceType_T Medium::getDeviceType() const
     2949{
     2950    return m->devType;
     2951}
     2952
     2953/**
     2954 * Returns the medium type. Must have caller + locking!
     2955 * @return
     2956 */
     2957MediumType_T Medium::getType() const
     2958{
     2959    return m->type;
     2960}
     2961
     2962/**
     2963 * Returns a short version of the location attribute.
     2964 *
     2965 * @note Must be called from under this object's read or write lock.
     2966 */
     2967Utf8Str Medium::getName()
     2968{
     2969    Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
     2970    return name;
     2971}
     2972
     2973/**
     2974 * This adds the given UUID to the list of media registries in which this
     2975 * medium should be registered. The UUID can either be a machine UUID,
     2976 * to add a machine registry, or the global registry UUID as returned by
     2977 * VirtualBox::getGlobalRegistryId().
     2978 *
     2979 * Note that for hard disks, this method does nothing if the medium is
     2980 * already in another registry to avoid having hard disks in more than
     2981 * one registry, which causes trouble with keeping diff images in sync.
     2982 * See getFirstRegistryMachineId() for details.
     2983 *
     2984 * @param id
     2985 * @param pfNeedsSaveSettings If != NULL, is set to true if a new reference was added and saveSettings for either the machine or global XML is needed.
     2986 * @return true if the registry was added.
     2987 */
     2988bool Medium::addRegistry(const Guid& id,
     2989                         bool *pfNeedsSaveSettings)
     2990{
     2991    AutoCaller autoCaller(this);
     2992    if (FAILED(autoCaller.rc())) return false;
     2993
     2994    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     2995
     2996    if (    m->devType == DeviceType_HardDisk
     2997         && m->llRegistryIDs.size() > 0
     2998       )
     2999        return false;
     3000
     3001    // no need to add the UUID twice
     3002    for (GuidList::const_iterator it = m->llRegistryIDs.begin();
     3003         it != m->llRegistryIDs.end();
     3004         ++it)
     3005    {
     3006        if ((*it) == id)
     3007            return false;
     3008    }
     3009
     3010    m->llRegistryIDs.push_back(id);
     3011    if (pfNeedsSaveSettings)
     3012        *pfNeedsSaveSettings = true;
     3013    return true;
     3014}
     3015
     3016/**
     3017 * Returns true if id is in the list of media registries for this medium.
     3018 * @param id
     3019 * @return
     3020 */
     3021bool Medium::isInRegistry(const Guid& id)
     3022{
     3023    AutoCaller autoCaller(this);
     3024    if (FAILED(autoCaller.rc())) return false;
     3025
     3026    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     3027
     3028    for (GuidList::const_iterator it = m->llRegistryIDs.begin();
     3029         it != m->llRegistryIDs.end();
     3030         ++it)
     3031    {
     3032        if (*it == id)
     3033            return true;
     3034    }
     3035
     3036    return false;
     3037}
     3038
     3039/**
     3040 * Internal method to return the medium's first registry machine (i.e. the machine in whose
     3041 * machine XML this medium is listed).
     3042 *
     3043 * Every medium must now (4.0) reside in at least one media registry, which is identified by
     3044 * a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
     3045 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
     3046 * object if the machine is old and still needs the global registry in VirtualBox.xml.
     3047 *
     3048 * By definition, hard disks may only be in one media registry, in which all its children
     3049 * will be stored as well. Otherwise we run into problems with having keep multiple registries
     3050 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
     3051 * case, only VM2's registry is used for the disk in question.)
     3052 *
     3053 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
     3054 * the user.
     3055 *
     3056 * Must have caller + locking!
     3057 *
     3058 * @return
     3059 */
     3060const Guid& Medium::getFirstRegistryMachineId() const
     3061{
     3062    return m->llRegistryIDs.front();
     3063}
     3064
     3065/**
    29453066 * Adds the given machine and optionally the snapshot to the list of the objects
    29463067 * this medium is attached to.
     
    34253546
    34263547/**
     3548 * Creates a new differencing storage unit using the format of the given target
     3549 * medium and the location. Note that @c aTarget must be NotCreated.
     3550 *
     3551 * The @a aMediumLockList parameter contains the associated medium lock list,
     3552 * which must be in locked state. If @a aWait is @c true then the caller is
     3553 * responsible for unlocking.
     3554 *
     3555 * If @a aProgress is not NULL but the object it points to is @c null then a
     3556 * new progress object will be created and assigned to @a *aProgress on
     3557 * success, otherwise the existing progress object is used. If @a aProgress is
     3558 * NULL, then no progress object is created/used at all.
     3559 *
     3560 * When @a aWait is @c false, this method will create a thread to perform the
     3561 * create operation asynchronously and will return immediately. Otherwise, it
     3562 * will perform the operation on the calling thread and will not return to the
     3563 * caller until the operation is completed. Note that @a aProgress cannot be
     3564 * NULL when @a aWait is @c false (this method will assert in this case).
     3565 *
     3566 * @param aTarget           Target medium.
     3567 * @param aVariant          Precise medium variant to create.
     3568 * @param aMediumLockList   List of media which should be locked.
     3569 * @param aProgress         Where to find/store a Progress object to track
     3570 *                          operation completion.
     3571 * @param aWait             @c true if this method should block instead of
     3572 *                          creating an asynchronous thread.
     3573 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
     3574 *                          initialized to false and that will be set to true
     3575 *                          by this function if the caller should invoke
     3576 *                          VirtualBox::saveSettings() because the global
     3577 *                          settings have changed. This only works in "wait"
     3578 *                          mode; otherwise saveSettings is called
     3579 *                          automatically by the thread that was created,
     3580 *                          and this parameter is ignored.
     3581 *
     3582 * @note Locks this object and @a aTarget for writing.
     3583 */
     3584HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
     3585                                  MediumVariant_T aVariant,
     3586                                  MediumLockList *aMediumLockList,
     3587                                  ComObjPtr<Progress> *aProgress,
     3588                                  bool aWait,
     3589                                  bool *pfNeedsGlobalSaveSettings)
     3590{
     3591    AssertReturn(!aTarget.isNull(), E_FAIL);
     3592    AssertReturn(aMediumLockList, E_FAIL);
     3593    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
     3594
     3595    AutoCaller autoCaller(this);
     3596    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     3597
     3598    AutoCaller targetCaller(aTarget);
     3599    if (FAILED(targetCaller.rc())) return targetCaller.rc();
     3600
     3601    HRESULT rc = S_OK;
     3602    ComObjPtr<Progress> pProgress;
     3603    Medium::Task *pTask = NULL;
     3604
     3605    try
     3606    {
     3607        AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
     3608
     3609        ComAssertThrow(   m->type != MediumType_Writethrough
     3610                       && m->type != MediumType_Shareable
     3611                       && m->type != MediumType_Readonly, E_FAIL);
     3612        ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
     3613
     3614        if (aTarget->m->state != MediumState_NotCreated)
     3615            throw aTarget->setStateError();
     3616
     3617        /* Check that the medium is not attached to the current state of
     3618         * any VM referring to it. */
     3619        for (BackRefList::const_iterator it = m->backRefs.begin();
     3620             it != m->backRefs.end();
     3621             ++it)
     3622        {
     3623            if (it->fInCurState)
     3624            {
     3625                /* Note: when a VM snapshot is being taken, all normal media
     3626                 * attached to the VM in the current state will be, as an
     3627                 * exception, also associated with the snapshot which is about
     3628                 * to create (see SnapshotMachine::init()) before deassociating
     3629                 * them from the current state (which takes place only on
     3630                 * success in Machine::fixupHardDisks()), so that the size of
     3631                 * snapshotIds will be 1 in this case. The extra condition is
     3632                 * used to filter out this legal situation. */
     3633                if (it->llSnapshotIds.size() == 0)
     3634                    throw setError(VBOX_E_INVALID_OBJECT_STATE,
     3635                                   tr("Medium '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing media based on it may be created until it is detached"),
     3636                                   m->strLocationFull.c_str(), it->machineId.raw());
     3637
     3638                Assert(it->llSnapshotIds.size() == 1);
     3639            }
     3640        }
     3641
     3642        if (aProgress != NULL)
     3643        {
     3644            /* use the existing progress object... */
     3645            pProgress = *aProgress;
     3646
     3647            /* ...but create a new one if it is null */
     3648            if (pProgress.isNull())
     3649            {
     3650                pProgress.createObject();
     3651                rc = pProgress->init(m->pVirtualBox,
     3652                                     static_cast<IMedium*>(this),
     3653                                     BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
     3654                                     TRUE /* aCancelable */);
     3655                if (FAILED(rc))
     3656                    throw rc;
     3657            }
     3658        }
     3659
     3660        /* setup task object to carry out the operation sync/async */
     3661        pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
     3662                                           aMediumLockList,
     3663                                           aWait /* fKeepMediumLockList */);
     3664        rc = pTask->rc();
     3665        AssertComRC(rc);
     3666        if (FAILED(rc))
     3667             throw rc;
     3668
     3669        /* register a task (it will deregister itself when done) */
     3670        ++m->numCreateDiffTasks;
     3671        Assert(m->numCreateDiffTasks != 0); /* overflow? */
     3672
     3673        aTarget->m->state = MediumState_Creating;
     3674    }
     3675    catch (HRESULT aRC) { rc = aRC; }
     3676
     3677    if (SUCCEEDED(rc))
     3678    {
     3679        if (aWait)
     3680            rc = runNow(pTask, pfNeedsGlobalSaveSettings);
     3681        else
     3682            rc = startThread(pTask);
     3683
     3684        if (SUCCEEDED(rc) && aProgress != NULL)
     3685            *aProgress = pProgress;
     3686    }
     3687    else if (pTask != NULL)
     3688        delete pTask;
     3689
     3690    return rc;
     3691}
     3692
     3693/**
    34273694 * Returns a preferred format for differencing media.
    34283695 */
     
    34463713
    34473714/**
    3448  * Returns the medium device type. Must have caller + locking!
     3715 * Implementation for the public Medium::Close() with the exception of calling
     3716 * VirtualBox::saveSettings(), in case someone wants to call this for several
     3717 * media.
     3718 *
     3719 * After this returns with success, uninit() has been called on the medium, and
     3720 * the object is no longer usable ("not ready" state).
     3721 *
     3722 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
     3723 *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
     3724 *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
     3725 *                and this parameter is ignored.
     3726 * @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here
     3727 *                   upon which the Medium instance gets uninitialized.
    34493728 * @return
    34503729 */
    3451 DeviceType_T Medium::getDeviceType() const
    3452 {
    3453     return m->devType;
    3454 }
    3455 
    3456 /**
    3457  * Returns the medium type. Must have caller + locking!
     3730HRESULT Medium::close(bool *pfNeedsGlobalSaveSettings, AutoCaller &autoCaller)
     3731{
     3732    // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
     3733    AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
     3734                                  this->lockHandle()
     3735                                  COMMA_LOCKVAL_SRC_POS);
     3736
     3737    LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str()));
     3738
     3739    bool wasCreated = true;
     3740
     3741    switch (m->state)
     3742    {
     3743        case MediumState_NotCreated:
     3744            wasCreated = false;
     3745            break;
     3746        case MediumState_Created:
     3747        case MediumState_Inaccessible:
     3748            break;
     3749        default:
     3750            return setStateError();
     3751    }
     3752
     3753    if (m->backRefs.size() != 0)
     3754        return setError(VBOX_E_OBJECT_IN_USE,
     3755                        tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
     3756                        m->strLocationFull.c_str(), m->backRefs.size());
     3757
     3758    // perform extra media-dependent close checks
     3759    HRESULT rc = canClose();
     3760    if (FAILED(rc)) return rc;
     3761
     3762    if (wasCreated)
     3763    {
     3764        // remove from the list of known media before performing actual
     3765        // uninitialization (to keep the media registry consistent on
     3766        // failure to do so)
     3767        rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings);
     3768        if (FAILED(rc)) return rc;
     3769    }
     3770
     3771    // leave the AutoCaller, as otherwise uninit() will simply hang
     3772    autoCaller.release();
     3773
     3774    // Keep the locks held until after uninit, as otherwise the consistency
     3775    // of the medium tree cannot be guaranteed.
     3776    uninit();
     3777
     3778    LogFlowFuncLeave();
     3779
     3780    return rc;
     3781}
     3782
     3783/**
     3784 * Deletes the medium storage unit.
     3785 *
     3786 * If @a aProgress is not NULL but the object it points to is @c null then a new
     3787 * progress object will be created and assigned to @a *aProgress on success,
     3788 * otherwise the existing progress object is used. If Progress is NULL, then no
     3789 * progress object is created/used at all.
     3790 *
     3791 * When @a aWait is @c false, this method will create a thread to perform the
     3792 * delete operation asynchronously and will return immediately. Otherwise, it
     3793 * will perform the operation on the calling thread and will not return to the
     3794 * caller until the operation is completed. Note that @a aProgress cannot be
     3795 * NULL when @a aWait is @c false (this method will assert in this case).
     3796 *
     3797 * @param aProgress     Where to find/store a Progress object to track operation
     3798 *                      completion.
     3799 * @param aWait         @c true if this method should block instead of creating
     3800 *                      an asynchronous thread.
     3801 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
     3802 *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
     3803 *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
     3804 *                and this parameter is ignored.
     3805 *
     3806 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
     3807 *       writing.
     3808 */
     3809HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
     3810                              bool aWait,
     3811                              bool *pfNeedsGlobalSaveSettings)
     3812{
     3813    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
     3814
     3815    AutoCaller autoCaller(this);
     3816    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     3817
     3818    HRESULT rc = S_OK;
     3819    ComObjPtr<Progress> pProgress;
     3820    Medium::Task *pTask = NULL;
     3821
     3822    try
     3823    {
     3824        /* we're accessing the media tree, and canClose() needs it too */
     3825        AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
     3826                                      this->lockHandle()
     3827                                      COMMA_LOCKVAL_SRC_POS);
     3828        LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
     3829
     3830        if (    !(m->formatObj->getCapabilities() & (   MediumFormatCapabilities_CreateDynamic
     3831                                                      | MediumFormatCapabilities_CreateFixed)))
     3832            throw setError(VBOX_E_NOT_SUPPORTED,
     3833                           tr("Medium format '%s' does not support storage deletion"),
     3834                           m->strFormat.c_str());
     3835
     3836        /* Note that we are fine with Inaccessible state too: a) for symmetry
     3837         * with create calls and b) because it doesn't really harm to try, if
     3838         * it is really inaccessible, the delete operation will fail anyway.
     3839         * Accepting Inaccessible state is especially important because all
     3840         * registered media are initially Inaccessible upon VBoxSVC startup
     3841         * until COMGETTER(RefreshState) is called. Accept Deleting state
     3842         * because some callers need to put the medium in this state early
     3843         * to prevent races. */
     3844        switch (m->state)
     3845        {
     3846            case MediumState_Created:
     3847            case MediumState_Deleting:
     3848            case MediumState_Inaccessible:
     3849                break;
     3850            default:
     3851                throw setStateError();
     3852        }
     3853
     3854        if (m->backRefs.size() != 0)
     3855        {
     3856            Utf8Str strMachines;
     3857            for (BackRefList::const_iterator it = m->backRefs.begin();
     3858                it != m->backRefs.end();
     3859                ++it)
     3860            {
     3861                const BackRef &b = *it;
     3862                if (strMachines.length())
     3863                    strMachines.append(", ");
     3864                strMachines.append(b.machineId.toString().c_str());
     3865            }
     3866#ifdef DEBUG
     3867            dumpBackRefs();
     3868#endif
     3869            throw setError(VBOX_E_OBJECT_IN_USE,
     3870                           tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
     3871                           m->strLocationFull.c_str(),
     3872                           m->backRefs.size(),
     3873                           strMachines.c_str());
     3874        }
     3875
     3876        rc = canClose();
     3877        if (FAILED(rc))
     3878            throw rc;
     3879
     3880        /* go to Deleting state, so that the medium is not actually locked */
     3881        if (m->state != MediumState_Deleting)
     3882        {
     3883            rc = markForDeletion();
     3884            if (FAILED(rc))
     3885                throw rc;
     3886        }
     3887
     3888        /* Build the medium lock list. */
     3889        MediumLockList *pMediumLockList(new MediumLockList());
     3890        rc = createMediumLockList(true /* fFailIfInaccessible */,
     3891                                  true /* fMediumLockWrite */,
     3892                                  NULL,
     3893                                  *pMediumLockList);
     3894        if (FAILED(rc))
     3895        {
     3896            delete pMediumLockList;
     3897            throw rc;
     3898        }
     3899
     3900        rc = pMediumLockList->Lock();
     3901        if (FAILED(rc))
     3902        {
     3903            delete pMediumLockList;
     3904            throw setError(rc,
     3905                           tr("Failed to lock media when deleting '%s'"),
     3906                           getLocationFull().c_str());
     3907        }
     3908
     3909        /* try to remove from the list of known media before performing
     3910         * actual deletion (we favor the consistency of the media registry
     3911         * which would have been broken if unregisterWithVirtualBox() failed
     3912         * after we successfully deleted the storage) */
     3913        rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings);
     3914        if (FAILED(rc))
     3915            throw rc;
     3916        // no longer need lock
     3917        multilock.release();
     3918
     3919        if (aProgress != NULL)
     3920        {
     3921            /* use the existing progress object... */
     3922            pProgress = *aProgress;
     3923
     3924            /* ...but create a new one if it is null */
     3925            if (pProgress.isNull())
     3926            {
     3927                pProgress.createObject();
     3928                rc = pProgress->init(m->pVirtualBox,
     3929                                     static_cast<IMedium*>(this),
     3930                                     BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
     3931                                     FALSE /* aCancelable */);
     3932                if (FAILED(rc))
     3933                    throw rc;
     3934            }
     3935        }
     3936
     3937        /* setup task object to carry out the operation sync/async */
     3938        pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
     3939        rc = pTask->rc();
     3940        AssertComRC(rc);
     3941        if (FAILED(rc))
     3942            throw rc;
     3943    }
     3944    catch (HRESULT aRC) { rc = aRC; }
     3945
     3946    if (SUCCEEDED(rc))
     3947    {
     3948        if (aWait)
     3949            rc = runNow(pTask, NULL /* pfNeedsGlobalSaveSettings*/);
     3950        else
     3951            rc = startThread(pTask);
     3952
     3953        if (SUCCEEDED(rc) && aProgress != NULL)
     3954            *aProgress = pProgress;
     3955
     3956    }
     3957    else
     3958    {
     3959        if (pTask)
     3960            delete pTask;
     3961
     3962        /* Undo deleting state if necessary. */
     3963        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     3964        unmarkForDeletion();
     3965    }
     3966
     3967    return rc;
     3968}
     3969
     3970/**
     3971 * Mark a medium for deletion.
     3972 *
     3973 * @note Caller must hold the write lock on this medium!
     3974 */
     3975HRESULT Medium::markForDeletion()
     3976{
     3977    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
     3978    switch (m->state)
     3979    {
     3980        case MediumState_Created:
     3981        case MediumState_Inaccessible:
     3982            m->preLockState = m->state;
     3983            m->state = MediumState_Deleting;
     3984            return S_OK;
     3985        default:
     3986            return setStateError();
     3987    }
     3988}
     3989
     3990/**
     3991 * Removes the "mark for deletion".
     3992 *
     3993 * @note Caller must hold the write lock on this medium!
     3994 */
     3995HRESULT Medium::unmarkForDeletion()
     3996{
     3997    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
     3998    switch (m->state)
     3999    {
     4000        case MediumState_Deleting:
     4001            m->state = m->preLockState;
     4002            return S_OK;
     4003        default:
     4004            return setStateError();
     4005    }
     4006}
     4007
     4008/**
     4009 * Mark a medium for deletion which is in locked state.
     4010 *
     4011 * @note Caller must hold the write lock on this medium!
     4012 */
     4013HRESULT Medium::markLockedForDeletion()
     4014{
     4015    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
     4016    if (   (   m->state == MediumState_LockedRead
     4017            || m->state == MediumState_LockedWrite)
     4018        && m->preLockState == MediumState_Created)
     4019    {
     4020        m->preLockState = MediumState_Deleting;
     4021        return S_OK;
     4022    }
     4023    else
     4024        return setStateError();
     4025}
     4026
     4027/**
     4028 * Removes the "mark for deletion" for a medium in locked state.
     4029 *
     4030 * @note Caller must hold the write lock on this medium!
     4031 */
     4032HRESULT Medium::unmarkLockedForDeletion()
     4033{
     4034    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
     4035    if (   (   m->state == MediumState_LockedRead
     4036            || m->state == MediumState_LockedWrite)
     4037        && m->preLockState == MediumState_Deleting)
     4038    {
     4039        m->preLockState = MediumState_Created;
     4040        return S_OK;
     4041    }
     4042    else
     4043        return setStateError();
     4044}
     4045
     4046/**
     4047 * Prepares this (source) medium, target medium and all intermediate media
     4048 * for the merge operation.
     4049 *
     4050 * This method is to be called prior to calling the #mergeTo() to perform
     4051 * necessary consistency checks and place involved media to appropriate
     4052 * states. If #mergeTo() is not called or fails, the state modifications
     4053 * performed by this method must be undone by #cancelMergeTo().
     4054 *
     4055 * See #mergeTo() for more information about merging.
     4056 *
     4057 * @param pTarget       Target medium.
     4058 * @param aMachineId    Allowed machine attachment. NULL means do not check.
     4059 * @param aSnapshotId   Allowed snapshot attachment. NULL or empty UUID means
     4060 *                      do not check.
     4061 * @param fLockMedia    Flag whether to lock the medium lock list or not.
     4062 *                      If set to false and the medium lock list locking fails
     4063 *                      later you must call #cancelMergeTo().
     4064 * @param fMergeForward Resulting merge direction (out).
     4065 * @param pParentForTarget New parent for target medium after merge (out).
     4066 * @param aChildrenToReparent List of children of the source which will have
     4067 *                      to be reparented to the target after merge (out).
     4068 * @param aMediumLockList Medium locking information (out).
     4069 *
     4070 * @note Locks medium tree for reading. Locks this object, aTarget and all
     4071 *       intermediate media for writing.
     4072 */
     4073HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
     4074                               const Guid *aMachineId,
     4075                               const Guid *aSnapshotId,
     4076                               bool fLockMedia,
     4077                               bool &fMergeForward,
     4078                               ComObjPtr<Medium> &pParentForTarget,
     4079                               MediaList &aChildrenToReparent,
     4080                               MediumLockList * &aMediumLockList)
     4081{
     4082    AssertReturn(pTarget != NULL, E_FAIL);
     4083    AssertReturn(pTarget != this, E_FAIL);
     4084
     4085    AutoCaller autoCaller(this);
     4086    AssertComRCReturnRC(autoCaller.rc());
     4087
     4088    AutoCaller targetCaller(pTarget);
     4089    AssertComRCReturnRC(targetCaller.rc());
     4090
     4091    HRESULT rc = S_OK;
     4092    fMergeForward = false;
     4093    pParentForTarget.setNull();
     4094    aChildrenToReparent.clear();
     4095    Assert(aMediumLockList == NULL);
     4096    aMediumLockList = NULL;
     4097
     4098    try
     4099    {
     4100        // locking: we need the tree lock first because we access parent pointers
     4101        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     4102
     4103        /* more sanity checking and figuring out the merge direction */
     4104        ComObjPtr<Medium> pMedium = getParent();
     4105        while (!pMedium.isNull() && pMedium != pTarget)
     4106            pMedium = pMedium->getParent();
     4107        if (pMedium == pTarget)
     4108            fMergeForward = false;
     4109        else
     4110        {
     4111            pMedium = pTarget->getParent();
     4112            while (!pMedium.isNull() && pMedium != this)
     4113                pMedium = pMedium->getParent();
     4114            if (pMedium == this)
     4115                fMergeForward = true;
     4116            else
     4117            {
     4118                Utf8Str tgtLoc;
     4119                {
     4120                    AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
     4121                    tgtLoc = pTarget->getLocationFull();
     4122                }
     4123
     4124                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     4125                throw setError(VBOX_E_INVALID_OBJECT_STATE,
     4126                               tr("Media '%s' and '%s' are unrelated"),
     4127                               m->strLocationFull.c_str(), tgtLoc.c_str());
     4128            }
     4129        }
     4130
     4131        /* Build the lock list. */
     4132        aMediumLockList = new MediumLockList();
     4133        if (fMergeForward)
     4134            rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
     4135                                               true /* fMediumLockWrite */,
     4136                                               NULL,
     4137                                               *aMediumLockList);
     4138        else
     4139            rc = createMediumLockList(true /* fFailIfInaccessible */,
     4140                                      false /* fMediumLockWrite */,
     4141                                      NULL,
     4142                                      *aMediumLockList);
     4143        if (FAILED(rc))
     4144            throw rc;
     4145
     4146        /* Sanity checking, must be after lock list creation as it depends on
     4147         * valid medium states. The medium objects must be accessible. Only
     4148         * do this if immediate locking is requested, otherwise it fails when
     4149         * we construct a medium lock list for an already running VM. Snapshot
     4150         * deletion uses this to simplify its life. */
     4151        if (fLockMedia)
     4152        {
     4153            {
     4154                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     4155                if (m->state != MediumState_Created)
     4156                    throw setStateError();
     4157            }
     4158            {
     4159                AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
     4160                if (pTarget->m->state != MediumState_Created)
     4161                    throw pTarget->setStateError();
     4162            }
     4163        }
     4164
     4165        /* check medium attachment and other sanity conditions */
     4166        if (fMergeForward)
     4167        {
     4168            AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     4169            if (getChildren().size() > 1)
     4170            {
     4171                throw setError(VBOX_E_INVALID_OBJECT_STATE,
     4172                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
     4173                               m->strLocationFull.c_str(), getChildren().size());
     4174            }
     4175            /* One backreference is only allowed if the machine ID is not empty
     4176             * and it matches the machine the medium is attached to (including
     4177             * the snapshot ID if not empty). */
     4178            if (   m->backRefs.size() != 0
     4179                && (   !aMachineId
     4180                    || m->backRefs.size() != 1
     4181                    || aMachineId->isEmpty()
     4182                    || *getFirstMachineBackrefId() != *aMachineId
     4183                    || (   (!aSnapshotId || !aSnapshotId->isEmpty())
     4184                        && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
     4185                throw setError(VBOX_E_OBJECT_IN_USE,
     4186                               tr("Medium '%s' is attached to %d virtual machines"),
     4187                               m->strLocationFull.c_str(), m->backRefs.size());
     4188            if (m->type == MediumType_Immutable)
     4189                throw setError(VBOX_E_INVALID_OBJECT_STATE,
     4190                               tr("Medium '%s' is immutable"),
     4191                               m->strLocationFull.c_str());
     4192        }
     4193        else
     4194        {
     4195            AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
     4196            if (pTarget->getChildren().size() > 1)
     4197            {
     4198                throw setError(VBOX_E_OBJECT_IN_USE,
     4199                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
     4200                               pTarget->m->strLocationFull.c_str(),
     4201                               pTarget->getChildren().size());
     4202            }
     4203            if (pTarget->m->type == MediumType_Immutable)
     4204                throw setError(VBOX_E_INVALID_OBJECT_STATE,
     4205                               tr("Medium '%s' is immutable"),
     4206                               pTarget->m->strLocationFull.c_str());
     4207        }
     4208        ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
     4209        ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
     4210        for (pLast = pLastIntermediate;
     4211             !pLast.isNull() && pLast != pTarget && pLast != this;
     4212             pLast = pLast->getParent())
     4213        {
     4214            AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
     4215            if (pLast->getChildren().size() > 1)
     4216            {
     4217                throw setError(VBOX_E_OBJECT_IN_USE,
     4218                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
     4219                               pLast->m->strLocationFull.c_str(),
     4220                               pLast->getChildren().size());
     4221            }
     4222            if (pLast->m->backRefs.size() != 0)
     4223                throw setError(VBOX_E_OBJECT_IN_USE,
     4224                               tr("Medium '%s' is attached to %d virtual machines"),
     4225                               pLast->m->strLocationFull.c_str(),
     4226                               pLast->m->backRefs.size());
     4227
     4228        }
     4229
     4230        /* Update medium states appropriately */
     4231        if (m->state == MediumState_Created)
     4232        {
     4233            rc = markForDeletion();
     4234            if (FAILED(rc))
     4235                throw rc;
     4236        }
     4237        else
     4238        {
     4239            if (fLockMedia)
     4240                throw setStateError();
     4241            else if (   m->state == MediumState_LockedWrite
     4242                     || m->state == MediumState_LockedRead)
     4243            {
     4244                /* Either mark it for deletion in locked state or allow
     4245                 * others to have done so. */
     4246                if (m->preLockState == MediumState_Created)
     4247                    markLockedForDeletion();
     4248                else if (m->preLockState != MediumState_Deleting)
     4249                    throw setStateError();
     4250            }
     4251            else
     4252                throw setStateError();
     4253        }
     4254
     4255        if (fMergeForward)
     4256        {
     4257            /* we will need parent to reparent target */
     4258            pParentForTarget = m->pParent;
     4259        }
     4260        else
     4261        {
     4262            /* we will need to reparent children of the source */
     4263            for (MediaList::const_iterator it = getChildren().begin();
     4264                 it != getChildren().end();
     4265                 ++it)
     4266            {
     4267                pMedium = *it;
     4268                if (fLockMedia)
     4269                {
     4270                    rc = pMedium->LockWrite(NULL);
     4271                    if (FAILED(rc))
     4272                        throw rc;
     4273                }
     4274
     4275                aChildrenToReparent.push_back(pMedium);
     4276            }
     4277        }
     4278        for (pLast = pLastIntermediate;
     4279             !pLast.isNull() && pLast != pTarget && pLast != this;
     4280             pLast = pLast->getParent())
     4281        {
     4282            AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
     4283            if (pLast->m->state == MediumState_Created)
     4284            {
     4285                rc = pLast->markForDeletion();
     4286                if (FAILED(rc))
     4287                    throw rc;
     4288            }
     4289            else
     4290                throw pLast->setStateError();
     4291        }
     4292
     4293        /* Tweak the lock list in the backward merge case, as the target
     4294         * isn't marked to be locked for writing yet. */
     4295        if (!fMergeForward)
     4296        {
     4297            MediumLockList::Base::iterator lockListBegin =
     4298                aMediumLockList->GetBegin();
     4299            MediumLockList::Base::iterator lockListEnd =
     4300                aMediumLockList->GetEnd();
     4301            lockListEnd--;
     4302            for (MediumLockList::Base::iterator it = lockListBegin;
     4303                 it != lockListEnd;
     4304                 ++it)
     4305            {
     4306                MediumLock &mediumLock = *it;
     4307                if (mediumLock.GetMedium() == pTarget)
     4308                {
     4309                    HRESULT rc2 = mediumLock.UpdateLock(true);
     4310                    AssertComRC(rc2);
     4311                    break;
     4312                }
     4313            }
     4314        }
     4315
     4316        if (fLockMedia)
     4317        {
     4318            rc = aMediumLockList->Lock();
     4319            if (FAILED(rc))
     4320            {
     4321                AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
     4322                throw setError(rc,
     4323                               tr("Failed to lock media when merging to '%s'"),
     4324                               pTarget->getLocationFull().c_str());
     4325            }
     4326        }
     4327    }
     4328    catch (HRESULT aRC) { rc = aRC; }
     4329
     4330    if (FAILED(rc))
     4331    {
     4332        delete aMediumLockList;
     4333        aMediumLockList = NULL;
     4334    }
     4335
     4336    return rc;
     4337}
     4338
     4339/**
     4340 * Merges this medium to the specified medium which must be either its
     4341 * direct ancestor or descendant.
     4342 *
     4343 * Given this medium is SOURCE and the specified medium is TARGET, we will
     4344 * get two variants of the merge operation:
     4345 *
     4346 *                forward merge
     4347 *                ------------------------->
     4348 *  [Extra] <- SOURCE <- Intermediate <- TARGET
     4349 *  Any        Del       Del             LockWr
     4350 *
     4351 *
     4352 *                            backward merge
     4353 *                <-------------------------
     4354 *             TARGET <- Intermediate <- SOURCE <- [Extra]
     4355 *             LockWr    Del             Del       LockWr
     4356 *
     4357 * Each diagram shows the involved media on the media chain where
     4358 * SOURCE and TARGET belong. Under each medium there is a state value which
     4359 * the medium must have at a time of the mergeTo() call.
     4360 *
     4361 * The media in the square braces may be absent (e.g. when the forward
     4362 * operation takes place and SOURCE is the base medium, or when the backward
     4363 * merge operation takes place and TARGET is the last child in the chain) but if
     4364 * they present they are involved too as shown.
     4365 *
     4366 * Neither the source medium nor intermediate media may be attached to
     4367 * any VM directly or in the snapshot, otherwise this method will assert.
     4368 *
     4369 * The #prepareMergeTo() method must be called prior to this method to place all
     4370 * involved to necessary states and perform other consistency checks.
     4371 *
     4372 * If @a aWait is @c true then this method will perform the operation on the
     4373 * calling thread and will not return to the caller until the operation is
     4374 * completed. When this method succeeds, all intermediate medium objects in
     4375 * the chain will be uninitialized, the state of the target medium (and all
     4376 * involved extra media) will be restored. @a aMediumLockList will not be
     4377 * deleted, whether the operation is successful or not. The caller has to do
     4378 * this if appropriate. Note that this (source) medium is not uninitialized
     4379 * because of possible AutoCaller instances held by the caller of this method
     4380 * on the current thread. It's therefore the responsibility of the caller to
     4381 * call Medium::uninit() after releasing all callers.
     4382 *
     4383 * If @a aWait is @c false then this method will create a thread to perform the
     4384 * operation asynchronously and will return immediately. If the operation
     4385 * succeeds, the thread will uninitialize the source medium object and all
     4386 * intermediate medium objects in the chain, reset the state of the target
     4387 * medium (and all involved extra media) and delete @a aMediumLockList.
     4388 * If the operation fails, the thread will only reset the states of all
     4389 * involved media and delete @a aMediumLockList.
     4390 *
     4391 * When this method fails (regardless of the @a aWait mode), it is a caller's
     4392 * responsibility to undo state changes and delete @a aMediumLockList using
     4393 * #cancelMergeTo().
     4394 *
     4395 * If @a aProgress is not NULL but the object it points to is @c null then a new
     4396 * progress object will be created and assigned to @a *aProgress on success,
     4397 * otherwise the existing progress object is used. If Progress is NULL, then no
     4398 * progress object is created/used at all. Note that @a aProgress cannot be
     4399 * NULL when @a aWait is @c false (this method will assert in this case).
     4400 *
     4401 * @param pTarget       Target medium.
     4402 * @param fMergeForward Merge direction.
     4403 * @param pParentForTarget New parent for target medium after merge.
     4404 * @param aChildrenToReparent List of children of the source which will have
     4405 *                      to be reparented to the target after merge.
     4406 * @param aMediumLockList Medium locking information.
     4407 * @param aProgress     Where to find/store a Progress object to track operation
     4408 *                      completion.
     4409 * @param aWait         @c true if this method should block instead of creating
     4410 *                      an asynchronous thread.
     4411 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
     4412 *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
     4413 *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
     4414 *                and this parameter is ignored.
     4415 *
     4416 * @note Locks the tree lock for writing. Locks the media from the chain
     4417 *       for writing.
     4418 */
     4419HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
     4420                        bool fMergeForward,
     4421                        const ComObjPtr<Medium> &pParentForTarget,
     4422                        const MediaList &aChildrenToReparent,
     4423                        MediumLockList *aMediumLockList,
     4424                        ComObjPtr <Progress> *aProgress,
     4425                        bool aWait,
     4426                        bool *pfNeedsGlobalSaveSettings)
     4427{
     4428    AssertReturn(pTarget != NULL, E_FAIL);
     4429    AssertReturn(pTarget != this, E_FAIL);
     4430    AssertReturn(aMediumLockList != NULL, E_FAIL);
     4431    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
     4432
     4433    AutoCaller autoCaller(this);
     4434    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     4435
     4436    AutoCaller targetCaller(pTarget);
     4437    AssertComRCReturnRC(targetCaller.rc());
     4438
     4439    HRESULT rc = S_OK;
     4440    ComObjPtr <Progress> pProgress;
     4441    Medium::Task *pTask = NULL;
     4442
     4443    try
     4444    {
     4445        if (aProgress != NULL)
     4446        {
     4447            /* use the existing progress object... */
     4448            pProgress = *aProgress;
     4449
     4450            /* ...but create a new one if it is null */
     4451            if (pProgress.isNull())
     4452            {
     4453                Utf8Str tgtName;
     4454                {
     4455                    AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
     4456                    tgtName = pTarget->getName();
     4457                }
     4458
     4459                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     4460
     4461                pProgress.createObject();
     4462                rc = pProgress->init(m->pVirtualBox,
     4463                                     static_cast<IMedium*>(this),
     4464                                     BstrFmt(tr("Merging medium '%s' to '%s'"),
     4465                                             getName().c_str(),
     4466                                             tgtName.c_str()).raw(),
     4467                                     TRUE /* aCancelable */);
     4468                if (FAILED(rc))
     4469                    throw rc;
     4470            }
     4471        }
     4472
     4473        /* setup task object to carry out the operation sync/async */
     4474        pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
     4475                                      pParentForTarget, aChildrenToReparent,
     4476                                      pProgress, aMediumLockList,
     4477                                      aWait /* fKeepMediumLockList */);
     4478        rc = pTask->rc();
     4479        AssertComRC(rc);
     4480        if (FAILED(rc))
     4481            throw rc;
     4482    }
     4483    catch (HRESULT aRC) { rc = aRC; }
     4484
     4485    if (SUCCEEDED(rc))
     4486    {
     4487        if (aWait)
     4488            rc = runNow(pTask, pfNeedsGlobalSaveSettings);
     4489        else
     4490            rc = startThread(pTask);
     4491
     4492        if (SUCCEEDED(rc) && aProgress != NULL)
     4493            *aProgress = pProgress;
     4494    }
     4495    else if (pTask != NULL)
     4496        delete pTask;
     4497
     4498    return rc;
     4499}
     4500
     4501/**
     4502 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
     4503 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
     4504 * the medium objects in @a aChildrenToReparent.
     4505 *
     4506 * @param aChildrenToReparent List of children of the source which will have
     4507 *                      to be reparented to the target after merge.
     4508 * @param aMediumLockList Medium locking information.
     4509 *
     4510 * @note Locks the media from the chain for writing.
     4511 */
     4512void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
     4513                           MediumLockList *aMediumLockList)
     4514{
     4515    AutoCaller autoCaller(this);
     4516    AssertComRCReturnVoid(autoCaller.rc());
     4517
     4518    AssertReturnVoid(aMediumLockList != NULL);
     4519
     4520    /* Revert media marked for deletion to previous state. */
     4521    HRESULT rc;
     4522    MediumLockList::Base::const_iterator mediumListBegin =
     4523        aMediumLockList->GetBegin();
     4524    MediumLockList::Base::const_iterator mediumListEnd =
     4525        aMediumLockList->GetEnd();
     4526    for (MediumLockList::Base::const_iterator it = mediumListBegin;
     4527         it != mediumListEnd;
     4528         ++it)
     4529    {
     4530        const MediumLock &mediumLock = *it;
     4531        const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
     4532        AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     4533
     4534        if (pMedium->m->state == MediumState_Deleting)
     4535        {
     4536            rc = pMedium->unmarkForDeletion();
     4537            AssertComRC(rc);
     4538        }
     4539    }
     4540
     4541    /* the destructor will do the work */
     4542    delete aMediumLockList;
     4543
     4544    /* unlock the children which had to be reparented */
     4545    for (MediaList::const_iterator it = aChildrenToReparent.begin();
     4546         it != aChildrenToReparent.end();
     4547         ++it)
     4548    {
     4549        const ComObjPtr<Medium> &pMedium = *it;
     4550
     4551        AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     4552        pMedium->UnlockWrite(NULL);
     4553    }
     4554}
     4555
     4556/**
     4557 * Fix the parent UUID of all children to point to this medium as their
     4558 * parent.
     4559 */
     4560HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
     4561{
     4562    MediumLockList mediumLockList;
     4563    HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
     4564                                      false /* fMediumLockWrite */,
     4565                                      this,
     4566                                      mediumLockList);
     4567    AssertComRCReturnRC(rc);
     4568
     4569    try
     4570    {
     4571        PVBOXHDD hdd;
     4572        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
     4573        ComAssertRCThrow(vrc, E_FAIL);
     4574
     4575        try
     4576        {
     4577            MediumLockList::Base::iterator lockListBegin =
     4578                mediumLockList.GetBegin();
     4579            MediumLockList::Base::iterator lockListEnd =
     4580                mediumLockList.GetEnd();
     4581            for (MediumLockList::Base::iterator it = lockListBegin;
     4582                 it != lockListEnd;
     4583                 ++it)
     4584            {
     4585                MediumLock &mediumLock = *it;
     4586                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
     4587                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
     4588
     4589                // open the medium
     4590                vrc = VDOpen(hdd,
     4591                             pMedium->m->strFormat.c_str(),
     4592                             pMedium->m->strLocationFull.c_str(),
     4593                             VD_OPEN_FLAGS_READONLY,
     4594                             pMedium->m->vdImageIfaces);
     4595                if (RT_FAILURE(vrc))
     4596                    throw vrc;
     4597            }
     4598
     4599            for (MediaList::const_iterator it = childrenToReparent.begin();
     4600                 it != childrenToReparent.end();
     4601                 ++it)
     4602            {
     4603                /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
     4604                vrc = VDOpen(hdd,
     4605                             (*it)->m->strFormat.c_str(),
     4606                             (*it)->m->strLocationFull.c_str(),
     4607                             VD_OPEN_FLAGS_INFO,
     4608                             (*it)->m->vdImageIfaces);
     4609                if (RT_FAILURE(vrc))
     4610                    throw vrc;
     4611
     4612                vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
     4613                if (RT_FAILURE(vrc))
     4614                    throw vrc;
     4615
     4616                vrc = VDClose(hdd, false /* fDelete */);
     4617                if (RT_FAILURE(vrc))
     4618                    throw vrc;
     4619
     4620                (*it)->UnlockWrite(NULL);
     4621            }
     4622        }
     4623        catch (HRESULT aRC) { rc = aRC; }
     4624        catch (int aVRC)
     4625        {
     4626            throw setError(E_FAIL,
     4627                            tr("Could not update medium UUID references to parent '%s' (%s)"),
     4628                            m->strLocationFull.c_str(),
     4629                            vdError(aVRC).c_str());
     4630        }
     4631
     4632        VDDestroy(hdd);
     4633    }
     4634    catch (HRESULT aRC) { rc = aRC; }
     4635
     4636    return rc;
     4637}
     4638
     4639/**
     4640 * Used by IAppliance to export disk images.
     4641 *
     4642 * @param aFilename             Filename to create (UTF8).
     4643 * @param aFormat               Medium format for creating @a aFilename.
     4644 * @param aVariant              Which exact image format variant to use
     4645 *                              for the destination image.
     4646 * @param aVDImageIOCallbacks   Pointer to the callback table for a
     4647 *                              VDINTERFACEIO interface. May be NULL.
     4648 * @param aVDImageIOUser        Opaque data for the callbacks.
     4649 * @param aProgress             Progress object to use.
    34584650 * @return
    3459  */
    3460 MediumType_T Medium::getType() const
    3461 {
    3462     return m->type;
    3463 }
    3464 
    3465 /**
    3466  * Returns a short version of the location attribute.
    3467  *
    3468  * @note Must be called from under this object's read or write lock.
    3469  */
    3470 Utf8Str Medium::getName()
    3471 {
    3472     Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
    3473     return name;
    3474 }
    3475 
    3476 /**
    3477  * This adds the given UUID to the list of media registries in which this
    3478  * medium should be registered. The UUID can either be a machine UUID,
    3479  * to add a machine registry, or the global registry UUID as returned by
    3480  * VirtualBox::getGlobalRegistryId().
    3481  *
    3482  * Note that for hard disks, this method does nothing if the medium is
    3483  * already in another registry to avoid having hard disks in more than
    3484  * one registry, which causes trouble with keeping diff images in sync.
    3485  * See getFirstRegistryMachineId() for details.
    3486  *
    3487  * @param id
    3488  * @param pfNeedsSaveSettings If != NULL, is set to true if a new reference was added and saveSettings for either the machine or global XML is needed.
    3489  * @return true if the registry was added.
    3490  */
    3491 bool Medium::addRegistry(const Guid& id,
    3492                          bool *pfNeedsSaveSettings)
    3493 {
     4651 * @note The source format is defined by the Medium instance.
     4652 */
     4653HRESULT Medium::exportFile(const char *aFilename,
     4654                           const ComObjPtr<MediumFormat> &aFormat,
     4655                           MediumVariant_T aVariant,
     4656                           void *aVDImageIOCallbacks, void *aVDImageIOUser,
     4657                           const ComObjPtr<Progress> &aProgress)
     4658{
     4659    AssertPtrReturn(aFilename, E_INVALIDARG);
     4660    AssertReturn(!aFormat.isNull(), E_INVALIDARG);
     4661    AssertReturn(!aProgress.isNull(), E_INVALIDARG);
     4662
    34944663    AutoCaller autoCaller(this);
    3495     if (FAILED(autoCaller.rc())) return false;
    3496 
     4664    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     4665
     4666    HRESULT rc = S_OK;
     4667    Medium::Task *pTask = NULL;
     4668
     4669    try
     4670    {
     4671        // locking: we need the tree lock first because we access parent pointers
     4672        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     4673        // and we need to write-lock the media involved
     4674        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     4675
     4676        /* Build the source lock list. */
     4677        MediumLockList *pSourceMediumLockList(new MediumLockList());
     4678        rc = createMediumLockList(true /* fFailIfInaccessible */,
     4679                                  false /* fMediumLockWrite */,
     4680                                  NULL,
     4681                                  *pSourceMediumLockList);
     4682        if (FAILED(rc))
     4683        {
     4684            delete pSourceMediumLockList;
     4685            throw rc;
     4686        }
     4687
     4688        rc = pSourceMediumLockList->Lock();
     4689        if (FAILED(rc))
     4690        {
     4691            delete pSourceMediumLockList;
     4692            throw setError(rc,
     4693                           tr("Failed to lock source media '%s'"),
     4694                           getLocationFull().c_str());
     4695        }
     4696
     4697        /* setup task object to carry out the operation asynchronously */
     4698        pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
     4699                                       aVariant, aVDImageIOCallbacks,
     4700                                       aVDImageIOUser, pSourceMediumLockList);
     4701        rc = pTask->rc();
     4702        AssertComRC(rc);
     4703        if (FAILED(rc))
     4704            throw rc;
     4705    }
     4706    catch (HRESULT aRC) { rc = aRC; }
     4707
     4708    if (SUCCEEDED(rc))
     4709        rc = startThread(pTask);
     4710    else if (pTask != NULL)
     4711        delete pTask;
     4712
     4713    return rc;
     4714}
     4715
     4716/**
     4717 * Used by IAppliance to import disk images.
     4718 *
     4719 * @param aFilename             Filename to read (UTF8).
     4720 * @param aFormat               Medium format for reading @a aFilename.
     4721 * @param aVariant              Which exact image format variant to use
     4722 *                              for the destination image.
     4723 * @param aVDImageIOCallbacks   Pointer to the callback table for a
     4724 *                              VDINTERFACEIO interface. May be NULL.
     4725 * @param aVDImageIOUser        Opaque data for the callbacks.
     4726 * @param aParent               Parent medium. May be NULL.
     4727 * @param aProgress             Progress object to use.
     4728 * @return
     4729 * @note The destination format is defined by the Medium instance.
     4730 */
     4731HRESULT Medium::importFile(const char *aFilename,
     4732                           const ComObjPtr<MediumFormat> &aFormat,
     4733                           MediumVariant_T aVariant,
     4734                           void *aVDImageIOCallbacks, void *aVDImageIOUser,
     4735                           const ComObjPtr<Medium> &aParent,
     4736                           const ComObjPtr<Progress> &aProgress)
     4737{
     4738    AssertPtrReturn(aFilename, E_INVALIDARG);
     4739    AssertReturn(!aFormat.isNull(), E_INVALIDARG);
     4740    AssertReturn(!aProgress.isNull(), E_INVALIDARG);
     4741
     4742    AutoCaller autoCaller(this);
     4743    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     4744
     4745    HRESULT rc = S_OK;
     4746    Medium::Task *pTask = NULL;
     4747
     4748    try
     4749    {
     4750        // locking: we need the tree lock first because we access parent pointers
     4751        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     4752        // and we need to write-lock the media involved
     4753        AutoMultiWriteLock2 alock(this, aParent COMMA_LOCKVAL_SRC_POS);
     4754
     4755        if (   m->state != MediumState_NotCreated
     4756            && m->state != MediumState_Created)
     4757            throw setStateError();
     4758
     4759        /* Build the target lock list. */
     4760        MediumLockList *pTargetMediumLockList(new MediumLockList());
     4761        rc = createMediumLockList(true /* fFailIfInaccessible */,
     4762                                  true /* fMediumLockWrite */,
     4763                                  aParent,
     4764                                  *pTargetMediumLockList);
     4765        if (FAILED(rc))
     4766        {
     4767            delete pTargetMediumLockList;
     4768            throw rc;
     4769        }
     4770
     4771        rc = pTargetMediumLockList->Lock();
     4772        if (FAILED(rc))
     4773        {
     4774            delete pTargetMediumLockList;
     4775            throw setError(rc,
     4776                           tr("Failed to lock target media '%s'"),
     4777                           getLocationFull().c_str());
     4778        }
     4779
     4780        /* setup task object to carry out the operation asynchronously */
     4781        pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,
     4782                                       aVariant, aVDImageIOCallbacks,
     4783                                       aVDImageIOUser, aParent,
     4784                                       pTargetMediumLockList);
     4785        rc = pTask->rc();
     4786        AssertComRC(rc);
     4787        if (FAILED(rc))
     4788            throw rc;
     4789
     4790        if (m->state == MediumState_NotCreated)
     4791            m->state = MediumState_Creating;
     4792    }
     4793    catch (HRESULT aRC) { rc = aRC; }
     4794
     4795    if (SUCCEEDED(rc))
     4796        rc = startThread(pTask);
     4797    else if (pTask != NULL)
     4798        delete pTask;
     4799
     4800    return rc;
     4801}
     4802
     4803////////////////////////////////////////////////////////////////////////////////
     4804//
     4805// Private methods
     4806//
     4807////////////////////////////////////////////////////////////////////////////////
     4808
     4809/**
     4810 * Queries information from the medium.
     4811 *
     4812 * As a result of this call, the accessibility state and data members such as
     4813 * size and description will be updated with the current information.
     4814 *
     4815 * @note This method may block during a system I/O call that checks storage
     4816 *       accessibility.
     4817 *
     4818 * @note Locks medium tree for reading and writing (for new diff media checked
     4819 *       for the first time). Locks mParent for reading. Locks this object for
     4820 *       writing.
     4821 *
     4822 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
     4823 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs())
     4824 * @return
     4825 */
     4826HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
     4827{
    34974828    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    34984829
    3499     if (    m->devType == DeviceType_HardDisk
    3500          && m->llRegistryIDs.size() > 0
     4830    if (   m->state != MediumState_Created
     4831        && m->state != MediumState_Inaccessible
     4832        && m->state != MediumState_LockedRead)
     4833        return E_FAIL;
     4834
     4835    HRESULT rc = S_OK;
     4836
     4837    int vrc = VINF_SUCCESS;
     4838
     4839    /* check if a blocking queryInfo() call is in progress on some other thread,
     4840     * and wait for it to finish if so instead of querying data ourselves */
     4841    if (m->queryInfoRunning)
     4842    {
     4843        Assert(   m->state == MediumState_LockedRead
     4844               || m->state == MediumState_LockedWrite);
     4845
     4846        alock.leave();
     4847        vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
     4848        alock.enter();
     4849
     4850        AssertRC(vrc);
     4851
     4852        return S_OK;
     4853    }
     4854
     4855    bool success = false;
     4856    Utf8Str lastAccessError;
     4857
     4858    /* are we dealing with a new medium constructed using the existing
     4859     * location? */
     4860    bool isImport = m->id.isEmpty();
     4861    unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
     4862
     4863    /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
     4864     * media because that would prevent necessary modifications
     4865     * when opening media of some third-party formats for the first
     4866     * time in VirtualBox (such as VMDK for which VDOpen() needs to
     4867     * generate an UUID if it is missing) */
     4868    if (    (m->hddOpenMode == OpenReadOnly)
     4869         || m->type == MediumType_Readonly
     4870         || !isImport
    35014871       )
    3502         return false;
    3503 
    3504     // no need to add the UUID twice
    3505     for (GuidList::const_iterator it = m->llRegistryIDs.begin();
    3506          it != m->llRegistryIDs.end();
    3507          ++it)
    3508     {
    3509         if ((*it) == id)
    3510             return false;
    3511     }
    3512 
    3513     m->llRegistryIDs.push_back(id);
    3514     if (pfNeedsSaveSettings)
    3515         *pfNeedsSaveSettings = true;
    3516     return true;
    3517 }
    3518 
    3519 /**
    3520  * Returns true if id is in the list of media registries for this medium.
    3521  * @param id
    3522  * @return
    3523  */
    3524 bool Medium::isInRegistry(const Guid& id)
    3525 {
    3526     AutoCaller autoCaller(this);
    3527     if (FAILED(autoCaller.rc())) return false;
    3528 
    3529     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    3530 
    3531     for (GuidList::const_iterator it = m->llRegistryIDs.begin();
    3532          it != m->llRegistryIDs.end();
    3533          ++it)
    3534     {
    3535         if (*it == id)
    3536             return true;
    3537     }
    3538 
    3539     return false;
    3540 }
    3541 
    3542 /**
    3543  * Internal method to return the medium's first registry machine (i.e. the machine in whose
    3544  * machine XML this medium is listed).
    3545  *
    3546  * Every medium must now (4.0) reside in at least one media registry, which is identified by
    3547  * a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
    3548  * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
    3549  * object if the machine is old and still needs the global registry in VirtualBox.xml.
    3550  *
    3551  * By definition, hard disks may only be in one media registry, in which all its children
    3552  * will be stored as well. Otherwise we run into problems with having keep multiple registries
    3553  * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
    3554  * case, only VM2's registry is used for the disk in question.)
    3555  *
    3556  * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
    3557  * the user.
    3558  *
    3559  * Must have caller + locking!
    3560  *
    3561  * @return
    3562  */
    3563 const Guid& Medium::getFirstRegistryMachineId() const
    3564 {
    3565     return m->llRegistryIDs.front();
     4872        uOpenFlags |= VD_OPEN_FLAGS_READONLY;
     4873
     4874    /* Open shareable medium with the appropriate flags */
     4875    if (m->type == MediumType_Shareable)
     4876        uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
     4877
     4878    /* Lock the medium, which makes the behavior much more consistent */
     4879    if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
     4880        rc = LockRead(NULL);
     4881    else
     4882        rc = LockWrite(NULL);
     4883    if (FAILED(rc)) return rc;
     4884
     4885    /* Copies of the input state fields which are not read-only,
     4886     * as we're dropping the lock. CAUTION: be extremely careful what
     4887     * you do with the contents of this medium object, as you will
     4888     * create races if there are concurrent changes. */
     4889    Utf8Str format(m->strFormat);
     4890    Utf8Str location(m->strLocationFull);
     4891    ComObjPtr<MediumFormat> formatObj = m->formatObj;
     4892
     4893    /* "Output" values which can't be set because the lock isn't held
     4894     * at the time the values are determined. */
     4895    Guid mediumId = m->id;
     4896    uint64_t mediumSize = 0;
     4897    uint64_t mediumLogicalSize = 0;
     4898
     4899    /* Flag whether a base image has a non-zero parent UUID and thus
     4900     * need repairing after it was closed again. */
     4901    bool fRepairImageZeroParentUuid = false;
     4902
     4903    /* leave the lock before a lengthy operation */
     4904    vrc = RTSemEventMultiReset(m->queryInfoSem);
     4905    AssertRCReturn(vrc, E_FAIL);
     4906    m->queryInfoRunning = true;
     4907    alock.leave();
     4908
     4909    try
     4910    {
     4911        /* skip accessibility checks for host drives */
     4912        if (m->hostDrive)
     4913        {
     4914            success = true;
     4915            throw S_OK;
     4916        }
     4917
     4918        PVBOXHDD hdd;
     4919        vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
     4920        ComAssertRCThrow(vrc, E_FAIL);
     4921
     4922        try
     4923        {
     4924            /** @todo This kind of opening of media is assuming that diff
     4925             * media can be opened as base media. Should be documented that
     4926             * it must work for all medium format backends. */
     4927            vrc = VDOpen(hdd,
     4928                         format.c_str(),
     4929                         location.c_str(),
     4930                         uOpenFlags,
     4931                         m->vdImageIfaces);
     4932            if (RT_FAILURE(vrc))
     4933            {
     4934                lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
     4935                                             location.c_str(), vdError(vrc).c_str());
     4936                throw S_OK;
     4937            }
     4938
     4939            if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
     4940            {
     4941                /* Modify the UUIDs if necessary. The associated fields are
     4942                 * not modified by other code, so no need to copy. */
     4943                if (fSetImageId)
     4944                {
     4945                    vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
     4946                    ComAssertRCThrow(vrc, E_FAIL);
     4947                }
     4948                if (fSetParentId)
     4949                {
     4950                    vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
     4951                    ComAssertRCThrow(vrc, E_FAIL);
     4952                }
     4953                /* zap the information, these are no long-term members */
     4954                unconst(m->uuidImage).clear();
     4955                unconst(m->uuidParentImage).clear();
     4956
     4957                /* check the UUID */
     4958                RTUUID uuid;
     4959                vrc = VDGetUuid(hdd, 0, &uuid);
     4960                ComAssertRCThrow(vrc, E_FAIL);
     4961
     4962                if (isImport)
     4963                {
     4964                    mediumId = uuid;
     4965
     4966                    if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
     4967                        // only when importing a VDMK that has no UUID, create one in memory
     4968                        mediumId.create();
     4969                }
     4970                else
     4971                {
     4972                    Assert(!mediumId.isEmpty());
     4973
     4974                    if (mediumId != uuid)
     4975                    {
     4976                        lastAccessError = Utf8StrFmt(
     4977                                tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
     4978                                &uuid,
     4979                                location.c_str(),
     4980                                mediumId.raw(),
     4981                                m->pVirtualBox->settingsFilePath().c_str());
     4982                        throw S_OK;
     4983                    }
     4984                }
     4985            }
     4986            else
     4987            {
     4988                /* the backend does not support storing UUIDs within the
     4989                 * underlying storage so use what we store in XML */
     4990
     4991                /* generate an UUID for an imported UUID-less medium */
     4992                if (isImport)
     4993                {
     4994                    if (fSetImageId)
     4995                        mediumId = m->uuidImage;
     4996                    else
     4997                        mediumId.create();
     4998                }
     4999            }
     5000
     5001            /* get the medium variant */
     5002            unsigned uImageFlags;
     5003            vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
     5004            ComAssertRCThrow(vrc, E_FAIL);
     5005            m->variant = (MediumVariant_T)uImageFlags;
     5006
     5007            /* check/get the parent uuid and update corresponding state */
     5008            if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
     5009            {
     5010                RTUUID parentId;
     5011                vrc = VDGetParentUuid(hdd, 0, &parentId);
     5012                ComAssertRCThrow(vrc, E_FAIL);
     5013
     5014                /* streamOptimized VMDK images are only accepted as base
     5015                 * images, as this allows automatic repair of OVF appliances.
     5016                 * Since such images don't support random writes they will not
     5017                 * be created for diff images. Only an overly smart user might
     5018                 * manually create this case. Too bad for him. */
     5019                if (   isImport
     5020                    && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
     5021                {
     5022                    /* the parent must be known to us. Note that we freely
     5023                     * call locking methods of mVirtualBox and parent, as all
     5024                     * relevant locks must be already held. There may be no
     5025                     * concurrent access to the just opened medium on other
     5026                     * threads yet (and init() will fail if this method reports
     5027                     * MediumState_Inaccessible) */
     5028
     5029                    Guid id = parentId;
     5030                    ComObjPtr<Medium> pParent;
     5031                    rc = m->pVirtualBox->findHardDiskById(id, false /* aSetError */, &pParent);
     5032                    if (FAILED(rc))
     5033                    {
     5034                        lastAccessError = Utf8StrFmt(
     5035                                tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
     5036                                &parentId, location.c_str(),
     5037                                m->pVirtualBox->settingsFilePath().c_str());
     5038                        throw S_OK;
     5039                    }
     5040
     5041                    /* we set mParent & children() */
     5042                    AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     5043
     5044                    Assert(m->pParent.isNull());
     5045                    m->pParent = pParent;
     5046                    m->pParent->m->llChildren.push_back(this);
     5047                }
     5048                else
     5049                {
     5050                    /* we access mParent */
     5051                    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     5052
     5053                    /* check that parent UUIDs match. Note that there's no need
     5054                     * for the parent's AutoCaller (our lifetime is bound to
     5055                     * it) */
     5056
     5057                    if (m->pParent.isNull())
     5058                    {
     5059                        /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
     5060                         * and 3.1.0-3.1.8 there are base images out there
     5061                         * which have a non-zero parent UUID. No point in
     5062                         * complaining about them, instead automatically
     5063                         * repair the problem. Later we can bring back the
     5064                         * error message, but we should wait until really
     5065                         * most users have repaired their images, either with
     5066                         * VBoxFixHdd or this way. */
     5067#if 1
     5068                        fRepairImageZeroParentUuid = true;
     5069#else /* 0 */
     5070                        lastAccessError = Utf8StrFmt(
     5071                                tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
     5072                                location.c_str(),
     5073                                m->pVirtualBox->settingsFilePath().c_str());
     5074                        throw S_OK;
     5075#endif /* 0 */
     5076                    }
     5077
     5078                    AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
     5079                    if (   !fRepairImageZeroParentUuid
     5080                        && m->pParent->getState() != MediumState_Inaccessible
     5081                        && m->pParent->getId() != parentId)
     5082                    {
     5083                        lastAccessError = Utf8StrFmt(
     5084                                tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
     5085                                &parentId, location.c_str(),
     5086                                m->pParent->getId().raw(),
     5087                                m->pVirtualBox->settingsFilePath().c_str());
     5088                        throw S_OK;
     5089                    }
     5090
     5091                    /// @todo NEWMEDIA what to do if the parent is not
     5092                    /// accessible while the diff is? Probably nothing. The
     5093                    /// real code will detect the mismatch anyway.
     5094                }
     5095            }
     5096
     5097            mediumSize = VDGetFileSize(hdd, 0);
     5098            mediumLogicalSize = VDGetSize(hdd, 0);
     5099
     5100            success = true;
     5101        }
     5102        catch (HRESULT aRC)
     5103        {
     5104            rc = aRC;
     5105        }
     5106
     5107        VDDestroy(hdd);
     5108    }
     5109    catch (HRESULT aRC)
     5110    {
     5111        rc = aRC;
     5112    }
     5113
     5114    alock.enter();
     5115
     5116    if (isImport)
     5117        unconst(m->id) = mediumId;
     5118
     5119    if (success)
     5120    {
     5121        m->size = mediumSize;
     5122        m->logicalSize = mediumLogicalSize;
     5123        m->strLastAccessError.setNull();
     5124    }
     5125    else
     5126    {
     5127        m->strLastAccessError = lastAccessError;
     5128        LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
     5129                        location.c_str(), m->strLastAccessError.c_str(),
     5130                        rc, vrc));
     5131    }
     5132
     5133    /* inform other callers if there are any */
     5134    RTSemEventMultiSignal(m->queryInfoSem);
     5135    m->queryInfoRunning = false;
     5136
     5137    /* Set the proper state according to the result of the check */
     5138    if (success)
     5139        m->preLockState = MediumState_Created;
     5140    else
     5141        m->preLockState = MediumState_Inaccessible;
     5142
     5143    HRESULT rc2;
     5144    if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
     5145        rc2 = UnlockRead(NULL);
     5146    else
     5147        rc2 = UnlockWrite(NULL);
     5148    if (SUCCEEDED(rc) && FAILED(rc2))
     5149        rc = rc2;
     5150    if (FAILED(rc)) return rc;
     5151
     5152    /* If this is a base image which incorrectly has a parent UUID set,
     5153     * repair the image now by zeroing the parent UUID. This is only done
     5154     * when we have structural information from a config file, on import
     5155     * this is not possible. If someone would accidentally call openMedium
     5156     * with a diff image before the base is registered this would destroy
     5157     * the diff. Not acceptable. */
     5158    if (fRepairImageZeroParentUuid)
     5159    {
     5160        rc = LockWrite(NULL);
     5161        if (FAILED(rc)) return rc;
     5162
     5163        alock.leave();
     5164
     5165        try
     5166        {
     5167            PVBOXHDD hdd;
     5168            vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
     5169            ComAssertRCThrow(vrc, E_FAIL);
     5170
     5171            try
     5172            {
     5173                vrc = VDOpen(hdd,
     5174                             format.c_str(),
     5175                             location.c_str(),
     5176                             uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
     5177                             m->vdImageIfaces);
     5178                if (RT_FAILURE(vrc))
     5179                    throw S_OK;
     5180
     5181                RTUUID zeroParentUuid;
     5182                RTUuidClear(&zeroParentUuid);
     5183                vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
     5184                ComAssertRCThrow(vrc, E_FAIL);
     5185            }
     5186            catch (HRESULT aRC)
     5187            {
     5188                rc = aRC;
     5189            }
     5190
     5191            VDDestroy(hdd);
     5192        }
     5193        catch (HRESULT aRC)
     5194        {
     5195            rc = aRC;
     5196        }
     5197
     5198        alock.enter();
     5199
     5200        rc = UnlockWrite(NULL);
     5201        if (SUCCEEDED(rc) && FAILED(rc2))
     5202            rc = rc2;
     5203        if (FAILED(rc)) return rc;
     5204    }
     5205
     5206    return rc;
     5207}
     5208
     5209/**
     5210 * Performs extra checks if the medium can be closed and returns S_OK in
     5211 * this case. Otherwise, returns a respective error message. Called by
     5212 * Close() under the medium tree lock and the medium lock.
     5213 *
     5214 * @note Also reused by Medium::Reset().
     5215 *
     5216 * @note Caller must hold the media tree write lock!
     5217 */
     5218HRESULT Medium::canClose()
     5219{
     5220    Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
     5221
     5222    if (getChildren().size() != 0)
     5223        return setError(VBOX_E_OBJECT_IN_USE,
     5224                        tr("Cannot close medium '%s' because it has %d child media"),
     5225                        m->strLocationFull.c_str(), getChildren().size());
     5226
     5227    return S_OK;
     5228}
     5229
     5230/**
     5231 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
     5232 *
     5233 * This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending
     5234 * on the device type of this medium.
     5235 *
     5236 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
     5237 *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
     5238 *
     5239 * @note Caller must have locked the media tree lock for writing!
     5240 */
     5241HRESULT Medium::unregisterWithVirtualBox(bool *pfNeedsGlobalSaveSettings)
     5242{
     5243    /* Note that we need to de-associate ourselves from the parent to let
     5244     * unregisterHardDisk() properly save the registry */
     5245
     5246    /* we modify mParent and access children */
     5247    Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
     5248
     5249    Medium *pParentBackup = m->pParent;
     5250    AssertReturn(getChildren().size() == 0, E_FAIL);
     5251    if (m->pParent)
     5252        deparent();
     5253
     5254    HRESULT rc = E_FAIL;
     5255    switch (m->devType)
     5256    {
     5257        case DeviceType_DVD:
     5258            rc = m->pVirtualBox->unregisterImage(this, DeviceType_DVD, pfNeedsGlobalSaveSettings);
     5259        break;
     5260
     5261        case DeviceType_Floppy:
     5262            rc = m->pVirtualBox->unregisterImage(this, DeviceType_Floppy, pfNeedsGlobalSaveSettings);
     5263        break;
     5264
     5265        case DeviceType_HardDisk:
     5266            rc = m->pVirtualBox->unregisterHardDisk(this, pfNeedsGlobalSaveSettings);
     5267        break;
     5268
     5269        default:
     5270        break;
     5271    }
     5272
     5273    if (FAILED(rc))
     5274    {
     5275        if (pParentBackup)
     5276        {
     5277            // re-associate with the parent as we are still relatives in the registry
     5278            m->pParent = pParentBackup;
     5279            m->pParent->m->llChildren.push_back(this);
     5280        }
     5281    }
     5282
     5283    return rc;
     5284}
     5285
     5286/**
     5287 * Sets the extended error info according to the current media state.
     5288 *
     5289 * @note Must be called from under this object's write or read lock.
     5290 */
     5291HRESULT Medium::setStateError()
     5292{
     5293    HRESULT rc = E_FAIL;
     5294
     5295    switch (m->state)
     5296    {
     5297        case MediumState_NotCreated:
     5298        {
     5299            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
     5300                          tr("Storage for the medium '%s' is not created"),
     5301                          m->strLocationFull.c_str());
     5302            break;
     5303        }
     5304        case MediumState_Created:
     5305        {
     5306            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
     5307                          tr("Storage for the medium '%s' is already created"),
     5308                          m->strLocationFull.c_str());
     5309            break;
     5310        }
     5311        case MediumState_LockedRead:
     5312        {
     5313            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
     5314                          tr("Medium '%s' is locked for reading by another task"),
     5315                          m->strLocationFull.c_str());
     5316            break;
     5317        }
     5318        case MediumState_LockedWrite:
     5319        {
     5320            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
     5321                          tr("Medium '%s' is locked for writing by another task"),
     5322                          m->strLocationFull.c_str());
     5323            break;
     5324        }
     5325        case MediumState_Inaccessible:
     5326        {
     5327            /* be in sync with Console::powerUpThread() */
     5328            if (!m->strLastAccessError.isEmpty())
     5329                rc = setError(VBOX_E_INVALID_OBJECT_STATE,
     5330                              tr("Medium '%s' is not accessible. %s"),
     5331                              m->strLocationFull.c_str(), m->strLastAccessError.c_str());
     5332            else
     5333                rc = setError(VBOX_E_INVALID_OBJECT_STATE,
     5334                              tr("Medium '%s' is not accessible"),
     5335                              m->strLocationFull.c_str());
     5336            break;
     5337        }
     5338        case MediumState_Creating:
     5339        {
     5340            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
     5341                          tr("Storage for the medium '%s' is being created"),
     5342                          m->strLocationFull.c_str());
     5343            break;
     5344        }
     5345        case MediumState_Deleting:
     5346        {
     5347            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
     5348                          tr("Storage for the medium '%s' is being deleted"),
     5349                          m->strLocationFull.c_str());
     5350            break;
     5351        }
     5352        default:
     5353        {
     5354            AssertFailed();
     5355            break;
     5356        }
     5357    }
     5358
     5359    return rc;
    35665360}
    35675361
     
    37365530
    37375531/**
    3738  * Queries information from the medium.
    3739  *
    3740  * As a result of this call, the accessibility state and data members such as
    3741  * size and description will be updated with the current information.
    3742  *
    3743  * @note This method may block during a system I/O call that checks storage
    3744  *       accessibility.
    3745  *
    3746  * @note Locks medium tree for reading and writing (for new diff media checked
    3747  *       for the first time). Locks mParent for reading. Locks this object for
    3748  *       writing.
    3749  *
    3750  * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
    3751  * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs())
    3752  * @return
    3753  */
    3754 HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
    3755 {
    3756     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3757 
    3758     if (   m->state != MediumState_Created
    3759         && m->state != MediumState_Inaccessible
    3760         && m->state != MediumState_LockedRead)
    3761         return E_FAIL;
    3762 
    3763     HRESULT rc = S_OK;
    3764 
    3765     int vrc = VINF_SUCCESS;
    3766 
    3767     /* check if a blocking queryInfo() call is in progress on some other thread,
    3768      * and wait for it to finish if so instead of querying data ourselves */
    3769     if (m->queryInfoRunning)
    3770     {
    3771         Assert(   m->state == MediumState_LockedRead
    3772                || m->state == MediumState_LockedWrite);
    3773 
    3774         alock.leave();
    3775         vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
    3776         alock.enter();
    3777 
    3778         AssertRC(vrc);
    3779 
    3780         return S_OK;
    3781     }
    3782 
    3783     bool success = false;
    3784     Utf8Str lastAccessError;
    3785 
    3786     /* are we dealing with a new medium constructed using the existing
    3787      * location? */
    3788     bool isImport = m->id.isEmpty();
    3789     unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
    3790 
    3791     /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
    3792      * media because that would prevent necessary modifications
    3793      * when opening media of some third-party formats for the first
    3794      * time in VirtualBox (such as VMDK for which VDOpen() needs to
    3795      * generate an UUID if it is missing) */
    3796     if (    (m->hddOpenMode == OpenReadOnly)
    3797          || m->type == MediumType_Readonly
    3798          || !isImport
    3799        )
    3800         uOpenFlags |= VD_OPEN_FLAGS_READONLY;
    3801 
    3802     /* Open shareable medium with the appropriate flags */
    3803     if (m->type == MediumType_Shareable)
    3804         uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
    3805 
    3806     /* Lock the medium, which makes the behavior much more consistent */
    3807     if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
    3808         rc = LockRead(NULL);
    3809     else
    3810         rc = LockWrite(NULL);
    3811     if (FAILED(rc)) return rc;
    3812 
    3813     /* Copies of the input state fields which are not read-only,
    3814      * as we're dropping the lock. CAUTION: be extremely careful what
    3815      * you do with the contents of this medium object, as you will
    3816      * create races if there are concurrent changes. */
    3817     Utf8Str format(m->strFormat);
    3818     Utf8Str location(m->strLocationFull);
    3819     ComObjPtr<MediumFormat> formatObj = m->formatObj;
    3820 
    3821     /* "Output" values which can't be set because the lock isn't held
    3822      * at the time the values are determined. */
    3823     Guid mediumId = m->id;
    3824     uint64_t mediumSize = 0;
    3825     uint64_t mediumLogicalSize = 0;
    3826 
    3827     /* Flag whether a base image has a non-zero parent UUID and thus
    3828      * need repairing after it was closed again. */
    3829     bool fRepairImageZeroParentUuid = false;
    3830 
    3831     /* leave the lock before a lengthy operation */
    3832     vrc = RTSemEventMultiReset(m->queryInfoSem);
    3833     AssertRCReturn(vrc, E_FAIL);
    3834     m->queryInfoRunning = true;
    3835     alock.leave();
    3836 
    3837     try
    3838     {
    3839         /* skip accessibility checks for host drives */
    3840         if (m->hostDrive)
    3841         {
    3842             success = true;
    3843             throw S_OK;
    3844         }
    3845 
    3846         PVBOXHDD hdd;
    3847         vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
    3848         ComAssertRCThrow(vrc, E_FAIL);
    3849 
    3850         try
    3851         {
    3852             /** @todo This kind of opening of media is assuming that diff
    3853              * media can be opened as base media. Should be documented that
    3854              * it must work for all medium format backends. */
    3855             vrc = VDOpen(hdd,
    3856                          format.c_str(),
    3857                          location.c_str(),
    3858                          uOpenFlags,
    3859                          m->vdImageIfaces);
    3860             if (RT_FAILURE(vrc))
    3861             {
    3862                 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
    3863                                              location.c_str(), vdError(vrc).c_str());
    3864                 throw S_OK;
    3865             }
    3866 
    3867             if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
    3868             {
    3869                 /* Modify the UUIDs if necessary. The associated fields are
    3870                  * not modified by other code, so no need to copy. */
    3871                 if (fSetImageId)
    3872                 {
    3873                     vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
    3874                     ComAssertRCThrow(vrc, E_FAIL);
    3875                 }
    3876                 if (fSetParentId)
    3877                 {
    3878                     vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
    3879                     ComAssertRCThrow(vrc, E_FAIL);
    3880                 }
    3881                 /* zap the information, these are no long-term members */
    3882                 unconst(m->uuidImage).clear();
    3883                 unconst(m->uuidParentImage).clear();
    3884 
    3885                 /* check the UUID */
    3886                 RTUUID uuid;
    3887                 vrc = VDGetUuid(hdd, 0, &uuid);
    3888                 ComAssertRCThrow(vrc, E_FAIL);
    3889 
    3890                 if (isImport)
    3891                 {
    3892                     mediumId = uuid;
    3893 
    3894                     if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
    3895                         // only when importing a VDMK that has no UUID, create one in memory
    3896                         mediumId.create();
    3897                 }
    3898                 else
    3899                 {
    3900                     Assert(!mediumId.isEmpty());
    3901 
    3902                     if (mediumId != uuid)
    3903                     {
    3904                         lastAccessError = Utf8StrFmt(
    3905                                 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
    3906                                 &uuid,
    3907                                 location.c_str(),
    3908                                 mediumId.raw(),
    3909                                 m->pVirtualBox->settingsFilePath().c_str());
    3910                         throw S_OK;
    3911                     }
    3912                 }
    3913             }
    3914             else
    3915             {
    3916                 /* the backend does not support storing UUIDs within the
    3917                  * underlying storage so use what we store in XML */
    3918 
    3919                 /* generate an UUID for an imported UUID-less medium */
    3920                 if (isImport)
    3921                 {
    3922                     if (fSetImageId)
    3923                         mediumId = m->uuidImage;
    3924                     else
    3925                         mediumId.create();
    3926                 }
    3927             }
    3928 
    3929             /* get the medium variant */
    3930             unsigned uImageFlags;
    3931             vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
    3932             ComAssertRCThrow(vrc, E_FAIL);
    3933             m->variant = (MediumVariant_T)uImageFlags;
    3934 
    3935             /* check/get the parent uuid and update corresponding state */
    3936             if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
    3937             {
    3938                 RTUUID parentId;
    3939                 vrc = VDGetParentUuid(hdd, 0, &parentId);
    3940                 ComAssertRCThrow(vrc, E_FAIL);
    3941 
    3942                 /* streamOptimized VMDK images are only accepted as base
    3943                  * images, as this allows automatic repair of OVF appliances.
    3944                  * Since such images don't support random writes they will not
    3945                  * be created for diff images. Only an overly smart user might
    3946                  * manually create this case. Too bad for him. */
    3947                 if (   isImport
    3948                     && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
    3949                 {
    3950                     /* the parent must be known to us. Note that we freely
    3951                      * call locking methods of mVirtualBox and parent, as all
    3952                      * relevant locks must be already held. There may be no
    3953                      * concurrent access to the just opened medium on other
    3954                      * threads yet (and init() will fail if this method reports
    3955                      * MediumState_Inaccessible) */
    3956 
    3957                     Guid id = parentId;
    3958                     ComObjPtr<Medium> pParent;
    3959                     rc = m->pVirtualBox->findHardDiskById(id, false /* aSetError */, &pParent);
    3960                     if (FAILED(rc))
    3961                     {
    3962                         lastAccessError = Utf8StrFmt(
    3963                                 tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
    3964                                 &parentId, location.c_str(),
    3965                                 m->pVirtualBox->settingsFilePath().c_str());
    3966                         throw S_OK;
    3967                     }
    3968 
    3969                     /* we set mParent & children() */
    3970                     AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    3971 
    3972                     Assert(m->pParent.isNull());
    3973                     m->pParent = pParent;
    3974                     m->pParent->m->llChildren.push_back(this);
    3975                 }
    3976                 else
    3977                 {
    3978                     /* we access mParent */
    3979                     AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    3980 
    3981                     /* check that parent UUIDs match. Note that there's no need
    3982                      * for the parent's AutoCaller (our lifetime is bound to
    3983                      * it) */
    3984 
    3985                     if (m->pParent.isNull())
    3986                     {
    3987                         /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
    3988                          * and 3.1.0-3.1.8 there are base images out there
    3989                          * which have a non-zero parent UUID. No point in
    3990                          * complaining about them, instead automatically
    3991                          * repair the problem. Later we can bring back the
    3992                          * error message, but we should wait until really
    3993                          * most users have repaired their images, either with
    3994                          * VBoxFixHdd or this way. */
    3995 #if 1
    3996                         fRepairImageZeroParentUuid = true;
    3997 #else /* 0 */
    3998                         lastAccessError = Utf8StrFmt(
    3999                                 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
    4000                                 location.c_str(),
    4001                                 m->pVirtualBox->settingsFilePath().c_str());
    4002                         throw S_OK;
    4003 #endif /* 0 */
    4004                     }
    4005 
    4006                     AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
    4007                     if (   !fRepairImageZeroParentUuid
    4008                         && m->pParent->getState() != MediumState_Inaccessible
    4009                         && m->pParent->getId() != parentId)
    4010                     {
    4011                         lastAccessError = Utf8StrFmt(
    4012                                 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
    4013                                 &parentId, location.c_str(),
    4014                                 m->pParent->getId().raw(),
    4015                                 m->pVirtualBox->settingsFilePath().c_str());
    4016                         throw S_OK;
    4017                     }
    4018 
    4019                     /// @todo NEWMEDIA what to do if the parent is not
    4020                     /// accessible while the diff is? Probably nothing. The
    4021                     /// real code will detect the mismatch anyway.
    4022                 }
    4023             }
    4024 
    4025             mediumSize = VDGetFileSize(hdd, 0);
    4026             mediumLogicalSize = VDGetSize(hdd, 0);
    4027 
    4028             success = true;
    4029         }
    4030         catch (HRESULT aRC)
    4031         {
    4032             rc = aRC;
    4033         }
    4034 
    4035         VDDestroy(hdd);
    4036     }
    4037     catch (HRESULT aRC)
    4038     {
    4039         rc = aRC;
    4040     }
    4041 
    4042     alock.enter();
    4043 
    4044     if (isImport)
    4045         unconst(m->id) = mediumId;
    4046 
    4047     if (success)
    4048     {
    4049         m->size = mediumSize;
    4050         m->logicalSize = mediumLogicalSize;
    4051         m->strLastAccessError.setNull();
    4052     }
    4053     else
    4054     {
    4055         m->strLastAccessError = lastAccessError;
    4056         LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
    4057                         location.c_str(), m->strLastAccessError.c_str(),
    4058                         rc, vrc));
    4059     }
    4060 
    4061     /* inform other callers if there are any */
    4062     RTSemEventMultiSignal(m->queryInfoSem);
    4063     m->queryInfoRunning = false;
    4064 
    4065     /* Set the proper state according to the result of the check */
    4066     if (success)
    4067         m->preLockState = MediumState_Created;
    4068     else
    4069         m->preLockState = MediumState_Inaccessible;
    4070 
    4071     HRESULT rc2;
    4072     if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
    4073         rc2 = UnlockRead(NULL);
    4074     else
    4075         rc2 = UnlockWrite(NULL);
    4076     if (SUCCEEDED(rc) && FAILED(rc2))
    4077         rc = rc2;
    4078     if (FAILED(rc)) return rc;
    4079 
    4080     /* If this is a base image which incorrectly has a parent UUID set,
    4081      * repair the image now by zeroing the parent UUID. This is only done
    4082      * when we have structural information from a config file, on import
    4083      * this is not possible. If someone would accidentally call openMedium
    4084      * with a diff image before the base is registered this would destroy
    4085      * the diff. Not acceptable. */
    4086     if (fRepairImageZeroParentUuid)
    4087     {
    4088         rc = LockWrite(NULL);
    4089         if (FAILED(rc)) return rc;
    4090 
    4091         alock.leave();
    4092 
    4093         try
    4094         {
    4095             PVBOXHDD hdd;
    4096             vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
    4097             ComAssertRCThrow(vrc, E_FAIL);
    4098 
    4099             try
    4100             {
    4101                 vrc = VDOpen(hdd,
    4102                              format.c_str(),
    4103                              location.c_str(),
    4104                              uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
    4105                              m->vdImageIfaces);
    4106                 if (RT_FAILURE(vrc))
    4107                     throw S_OK;
    4108 
    4109                 RTUUID zeroParentUuid;
    4110                 RTUuidClear(&zeroParentUuid);
    4111                 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
    4112                 ComAssertRCThrow(vrc, E_FAIL);
    4113             }
    4114             catch (HRESULT aRC)
    4115             {
    4116                 rc = aRC;
    4117             }
    4118 
    4119             VDDestroy(hdd);
    4120         }
    4121         catch (HRESULT aRC)
    4122         {
    4123             rc = aRC;
    4124         }
    4125 
    4126         alock.enter();
    4127 
    4128         rc = UnlockWrite(NULL);
    4129         if (SUCCEEDED(rc) && FAILED(rc2))
    4130             rc = rc2;
    4131         if (FAILED(rc)) return rc;
    4132     }
    4133 
    4134     return rc;
    4135 }
    4136 
    4137 /**
    4138  * Sets the extended error info according to the current media state.
    4139  *
    4140  * @note Must be called from under this object's write or read lock.
    4141  */
    4142 HRESULT Medium::setStateError()
    4143 {
    4144     HRESULT rc = E_FAIL;
    4145 
    4146     switch (m->state)
    4147     {
    4148         case MediumState_NotCreated:
    4149         {
    4150             rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    4151                           tr("Storage for the medium '%s' is not created"),
    4152                           m->strLocationFull.c_str());
    4153             break;
    4154         }
    4155         case MediumState_Created:
    4156         {
    4157             rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    4158                           tr("Storage for the medium '%s' is already created"),
    4159                           m->strLocationFull.c_str());
    4160             break;
    4161         }
    4162         case MediumState_LockedRead:
    4163         {
    4164             rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    4165                           tr("Medium '%s' is locked for reading by another task"),
    4166                           m->strLocationFull.c_str());
    4167             break;
    4168         }
    4169         case MediumState_LockedWrite:
    4170         {
    4171             rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    4172                           tr("Medium '%s' is locked for writing by another task"),
    4173                           m->strLocationFull.c_str());
    4174             break;
    4175         }
    4176         case MediumState_Inaccessible:
    4177         {
    4178             /* be in sync with Console::powerUpThread() */
    4179             if (!m->strLastAccessError.isEmpty())
    4180                 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    4181                               tr("Medium '%s' is not accessible. %s"),
    4182                               m->strLocationFull.c_str(), m->strLastAccessError.c_str());
    4183             else
    4184                 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    4185                               tr("Medium '%s' is not accessible"),
    4186                               m->strLocationFull.c_str());
    4187             break;
    4188         }
    4189         case MediumState_Creating:
    4190         {
    4191             rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    4192                           tr("Storage for the medium '%s' is being created"),
    4193                           m->strLocationFull.c_str());
    4194             break;
    4195         }
    4196         case MediumState_Deleting:
    4197         {
    4198             rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    4199                           tr("Storage for the medium '%s' is being deleted"),
    4200                           m->strLocationFull.c_str());
    4201             break;
    4202         }
    4203         default:
    4204         {
    4205             AssertFailed();
    4206             break;
    4207         }
    4208     }
    4209 
    4210     return rc;
     5532 * Checks that the format ID is valid and sets it on success.
     5533 *
     5534 * Note that this method will caller-reference the format object on success!
     5535 * This reference must be released somewhere to let the MediumFormat object be
     5536 * uninitialized.
     5537 *
     5538 * @note Must be called from under this object's write lock.
     5539 */
     5540HRESULT Medium::setFormat(const Utf8Str &aFormat)
     5541{
     5542    /* get the format object first */
     5543    {
     5544        SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
     5545        AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
     5546
     5547        unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
     5548        if (m->formatObj.isNull())
     5549            return setError(E_INVALIDARG,
     5550                            tr("Invalid medium storage format '%s'"),
     5551                            aFormat.c_str());
     5552
     5553        /* reference the format permanently to prevent its unexpected
     5554         * uninitialization */
     5555        HRESULT rc = m->formatObj->addCaller();
     5556        AssertComRCReturnRC(rc);
     5557
     5558        /* get properties (preinsert them as keys in the map). Note that the
     5559         * map doesn't grow over the object life time since the set of
     5560         * properties is meant to be constant. */
     5561
     5562        Assert(m->mapProperties.empty());
     5563
     5564        for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
     5565             it != m->formatObj->getProperties().end();
     5566             ++it)
     5567        {
     5568            m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
     5569        }
     5570    }
     5571
     5572    unconst(m->strFormat) = aFormat;
     5573
     5574    return S_OK;
    42115575}
    42125576
     
    42625626
    42635627/**
    4264  * Implementation for the public Medium::Close() with the exception of calling
    4265  * VirtualBox::saveSettings(), in case someone wants to call this for several
    4266  * media.
    4267  *
    4268  * After this returns with success, uninit() has been called on the medium, and
    4269  * the object is no longer usable ("not ready" state).
    4270  *
    4271  * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
    4272  *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
    4273  *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
    4274  *                and this parameter is ignored.
    4275  * @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here
    4276  *                   upon which the Medium instance gets uninitialized.
    4277  * @return
    4278  */
    4279 HRESULT Medium::close(bool *pfNeedsGlobalSaveSettings, AutoCaller &autoCaller)
    4280 {
    4281     // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
    4282     AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
    4283                                   this->lockHandle()
    4284                                   COMMA_LOCKVAL_SRC_POS);
    4285 
    4286     LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str()));
    4287 
    4288     bool wasCreated = true;
    4289 
    4290     switch (m->state)
    4291     {
    4292         case MediumState_NotCreated:
    4293             wasCreated = false;
    4294             break;
    4295         case MediumState_Created:
    4296         case MediumState_Inaccessible:
    4297             break;
    4298         default:
    4299             return setStateError();
    4300     }
    4301 
    4302     if (m->backRefs.size() != 0)
    4303         return setError(VBOX_E_OBJECT_IN_USE,
    4304                         tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
    4305                         m->strLocationFull.c_str(), m->backRefs.size());
    4306 
    4307     // perform extra media-dependent close checks
    4308     HRESULT rc = canClose();
    4309     if (FAILED(rc)) return rc;
    4310 
    4311     if (wasCreated)
    4312     {
    4313         // remove from the list of known media before performing actual
    4314         // uninitialization (to keep the media registry consistent on
    4315         // failure to do so)
    4316         rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings);
    4317         if (FAILED(rc)) return rc;
    4318     }
    4319 
    4320     // leave the AutoCaller, as otherwise uninit() will simply hang
    4321     autoCaller.release();
    4322 
    4323     // Keep the locks held until after uninit, as otherwise the consistency
    4324     // of the medium tree cannot be guaranteed.
    4325     uninit();
    4326 
    4327     LogFlowFuncLeave();
    4328 
    4329     return rc;
    4330 }
    4331 
    4332 /**
    4333  * Deletes the medium storage unit.
    4334  *
    4335  * If @a aProgress is not NULL but the object it points to is @c null then a new
    4336  * progress object will be created and assigned to @a *aProgress on success,
    4337  * otherwise the existing progress object is used. If Progress is NULL, then no
    4338  * progress object is created/used at all.
    4339  *
    4340  * When @a aWait is @c false, this method will create a thread to perform the
    4341  * delete operation asynchronously and will return immediately. Otherwise, it
    4342  * will perform the operation on the calling thread and will not return to the
    4343  * caller until the operation is completed. Note that @a aProgress cannot be
    4344  * NULL when @a aWait is @c false (this method will assert in this case).
    4345  *
    4346  * @param aProgress     Where to find/store a Progress object to track operation
    4347  *                      completion.
    4348  * @param aWait         @c true if this method should block instead of creating
    4349  *                      an asynchronous thread.
    4350  * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
    4351  *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
    4352  *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
    4353  *                and this parameter is ignored.
    4354  *
    4355  * @note Locks mVirtualBox and this object for writing. Locks medium tree for
    4356  *       writing.
    4357  */
    4358 HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
    4359                               bool aWait,
    4360                               bool *pfNeedsGlobalSaveSettings)
    4361 {
    4362     AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
    4363 
    4364     AutoCaller autoCaller(this);
    4365     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4366 
    4367     HRESULT rc = S_OK;
    4368     ComObjPtr<Progress> pProgress;
    4369     Medium::Task *pTask = NULL;
    4370 
    4371     try
    4372     {
    4373         /* we're accessing the media tree, and canClose() needs it too */
    4374         AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
    4375                                       this->lockHandle()
    4376                                       COMMA_LOCKVAL_SRC_POS);
    4377         LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
    4378 
    4379         if (    !(m->formatObj->getCapabilities() & (   MediumFormatCapabilities_CreateDynamic
    4380                                                       | MediumFormatCapabilities_CreateFixed)))
    4381             throw setError(VBOX_E_NOT_SUPPORTED,
    4382                            tr("Medium format '%s' does not support storage deletion"),
    4383                            m->strFormat.c_str());
    4384 
    4385         /* Note that we are fine with Inaccessible state too: a) for symmetry
    4386          * with create calls and b) because it doesn't really harm to try, if
    4387          * it is really inaccessible, the delete operation will fail anyway.
    4388          * Accepting Inaccessible state is especially important because all
    4389          * registered media are initially Inaccessible upon VBoxSVC startup
    4390          * until COMGETTER(RefreshState) is called. Accept Deleting state
    4391          * because some callers need to put the medium in this state early
    4392          * to prevent races. */
    4393         switch (m->state)
    4394         {
    4395             case MediumState_Created:
    4396             case MediumState_Deleting:
    4397             case MediumState_Inaccessible:
    4398                 break;
    4399             default:
    4400                 throw setStateError();
    4401         }
    4402 
    4403         if (m->backRefs.size() != 0)
    4404         {
    4405             Utf8Str strMachines;
    4406             for (BackRefList::const_iterator it = m->backRefs.begin();
    4407                 it != m->backRefs.end();
    4408                 ++it)
    4409             {
    4410                 const BackRef &b = *it;
    4411                 if (strMachines.length())
    4412                     strMachines.append(", ");
    4413                 strMachines.append(b.machineId.toString().c_str());
    4414             }
    4415 #ifdef DEBUG
    4416             dumpBackRefs();
    4417 #endif
    4418             throw setError(VBOX_E_OBJECT_IN_USE,
    4419                            tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
    4420                            m->strLocationFull.c_str(),
    4421                            m->backRefs.size(),
    4422                            strMachines.c_str());
    4423         }
    4424 
    4425         rc = canClose();
    4426         if (FAILED(rc))
    4427             throw rc;
    4428 
    4429         /* go to Deleting state, so that the medium is not actually locked */
    4430         if (m->state != MediumState_Deleting)
    4431         {
    4432             rc = markForDeletion();
    4433             if (FAILED(rc))
    4434                 throw rc;
    4435         }
    4436 
    4437         /* Build the medium lock list. */
    4438         MediumLockList *pMediumLockList(new MediumLockList());
    4439         rc = createMediumLockList(true /* fFailIfInaccessible */,
    4440                                   true /* fMediumLockWrite */,
    4441                                   NULL,
    4442                                   *pMediumLockList);
    4443         if (FAILED(rc))
    4444         {
    4445             delete pMediumLockList;
    4446             throw rc;
    4447         }
    4448 
    4449         rc = pMediumLockList->Lock();
    4450         if (FAILED(rc))
    4451         {
    4452             delete pMediumLockList;
    4453             throw setError(rc,
    4454                            tr("Failed to lock media when deleting '%s'"),
    4455                            getLocationFull().c_str());
    4456         }
    4457 
    4458         /* try to remove from the list of known media before performing
    4459          * actual deletion (we favor the consistency of the media registry
    4460          * which would have been broken if unregisterWithVirtualBox() failed
    4461          * after we successfully deleted the storage) */
    4462         rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings);
    4463         if (FAILED(rc))
    4464             throw rc;
    4465         // no longer need lock
    4466         multilock.release();
    4467 
    4468         if (aProgress != NULL)
    4469         {
    4470             /* use the existing progress object... */
    4471             pProgress = *aProgress;
    4472 
    4473             /* ...but create a new one if it is null */
    4474             if (pProgress.isNull())
    4475             {
    4476                 pProgress.createObject();
    4477                 rc = pProgress->init(m->pVirtualBox,
    4478                                      static_cast<IMedium*>(this),
    4479                                      BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
    4480                                      FALSE /* aCancelable */);
    4481                 if (FAILED(rc))
    4482                     throw rc;
    4483             }
    4484         }
    4485 
    4486         /* setup task object to carry out the operation sync/async */
    4487         pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
    4488         rc = pTask->rc();
    4489         AssertComRC(rc);
    4490         if (FAILED(rc))
    4491             throw rc;
    4492     }
    4493     catch (HRESULT aRC) { rc = aRC; }
    4494 
    4495     if (SUCCEEDED(rc))
    4496     {
    4497         if (aWait)
    4498             rc = runNow(pTask, NULL /* pfNeedsGlobalSaveSettings*/);
    4499         else
    4500             rc = startThread(pTask);
    4501 
    4502         if (SUCCEEDED(rc) && aProgress != NULL)
    4503             *aProgress = pProgress;
    4504 
    4505     }
    4506     else
    4507     {
    4508         if (pTask)
    4509             delete pTask;
    4510 
    4511         /* Undo deleting state if necessary. */
    4512         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    4513         unmarkForDeletion();
    4514     }
    4515 
    4516     return rc;
    4517 }
    4518 
    4519 /**
    4520  * Mark a medium for deletion.
    4521  *
    4522  * @note Caller must hold the write lock on this medium!
    4523  */
    4524 HRESULT Medium::markForDeletion()
    4525 {
    4526     ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
    4527     switch (m->state)
    4528     {
    4529         case MediumState_Created:
    4530         case MediumState_Inaccessible:
    4531             m->preLockState = m->state;
    4532             m->state = MediumState_Deleting;
    4533             return S_OK;
    4534         default:
    4535             return setStateError();
    4536     }
    4537 }
    4538 
    4539 /**
    4540  * Removes the "mark for deletion".
    4541  *
    4542  * @note Caller must hold the write lock on this medium!
    4543  */
    4544 HRESULT Medium::unmarkForDeletion()
    4545 {
    4546     ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
    4547     switch (m->state)
    4548     {
    4549         case MediumState_Deleting:
    4550             m->state = m->preLockState;
    4551             return S_OK;
    4552         default:
    4553             return setStateError();
    4554     }
    4555 }
    4556 
    4557 /**
    4558  * Mark a medium for deletion which is in locked state.
    4559  *
    4560  * @note Caller must hold the write lock on this medium!
    4561  */
    4562 HRESULT Medium::markLockedForDeletion()
    4563 {
    4564     ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
    4565     if (   (   m->state == MediumState_LockedRead
    4566             || m->state == MediumState_LockedWrite)
    4567         && m->preLockState == MediumState_Created)
    4568     {
    4569         m->preLockState = MediumState_Deleting;
    4570         return S_OK;
    4571     }
    4572     else
    4573         return setStateError();
    4574 }
    4575 
    4576 /**
    4577  * Removes the "mark for deletion" for a medium in locked state.
    4578  *
    4579  * @note Caller must hold the write lock on this medium!
    4580  */
    4581 HRESULT Medium::unmarkLockedForDeletion()
    4582 {
    4583     ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
    4584     if (   (   m->state == MediumState_LockedRead
    4585             || m->state == MediumState_LockedWrite)
    4586         && m->preLockState == MediumState_Deleting)
    4587     {
    4588         m->preLockState = MediumState_Created;
    4589         return S_OK;
    4590     }
    4591     else
    4592         return setStateError();
    4593 }
    4594 
    4595 /**
    4596  * Creates a new differencing storage unit using the format of the given target
    4597  * medium and the location. Note that @c aTarget must be NotCreated.
    4598  *
    4599  * The @a aMediumLockList parameter contains the associated medium lock list,
    4600  * which must be in locked state. If @a aWait is @c true then the caller is
    4601  * responsible for unlocking.
    4602  *
    4603  * If @a aProgress is not NULL but the object it points to is @c null then a
    4604  * new progress object will be created and assigned to @a *aProgress on
    4605  * success, otherwise the existing progress object is used. If @a aProgress is
    4606  * NULL, then no progress object is created/used at all.
    4607  *
    4608  * When @a aWait is @c false, this method will create a thread to perform the
    4609  * create operation asynchronously and will return immediately. Otherwise, it
    4610  * will perform the operation on the calling thread and will not return to the
    4611  * caller until the operation is completed. Note that @a aProgress cannot be
    4612  * NULL when @a aWait is @c false (this method will assert in this case).
    4613  *
    4614  * @param aTarget           Target medium.
    4615  * @param aVariant          Precise medium variant to create.
    4616  * @param aMediumLockList   List of media which should be locked.
    4617  * @param aProgress         Where to find/store a Progress object to track
    4618  *                          operation completion.
    4619  * @param aWait             @c true if this method should block instead of
    4620  *                          creating an asynchronous thread.
    4621  * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
    4622  *                          initialized to false and that will be set to true
    4623  *                          by this function if the caller should invoke
    4624  *                          VirtualBox::saveSettings() because the global
    4625  *                          settings have changed. This only works in "wait"
    4626  *                          mode; otherwise saveSettings is called
    4627  *                          automatically by the thread that was created,
    4628  *                          and this parameter is ignored.
    4629  *
    4630  * @note Locks this object and @a aTarget for writing.
    4631  */
    4632 HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
    4633                                   MediumVariant_T aVariant,
    4634                                   MediumLockList *aMediumLockList,
    4635                                   ComObjPtr<Progress> *aProgress,
    4636                                   bool aWait,
    4637                                   bool *pfNeedsGlobalSaveSettings)
    4638 {
    4639     AssertReturn(!aTarget.isNull(), E_FAIL);
    4640     AssertReturn(aMediumLockList, E_FAIL);
    4641     AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
    4642 
    4643     AutoCaller autoCaller(this);
    4644     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4645 
    4646     AutoCaller targetCaller(aTarget);
    4647     if (FAILED(targetCaller.rc())) return targetCaller.rc();
    4648 
    4649     HRESULT rc = S_OK;
    4650     ComObjPtr<Progress> pProgress;
    4651     Medium::Task *pTask = NULL;
    4652 
    4653     try
    4654     {
    4655         AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
    4656 
    4657         ComAssertThrow(   m->type != MediumType_Writethrough
    4658                        && m->type != MediumType_Shareable
    4659                        && m->type != MediumType_Readonly, E_FAIL);
    4660         ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
    4661 
    4662         if (aTarget->m->state != MediumState_NotCreated)
    4663             throw aTarget->setStateError();
    4664 
    4665         /* Check that the medium is not attached to the current state of
    4666          * any VM referring to it. */
    4667         for (BackRefList::const_iterator it = m->backRefs.begin();
    4668              it != m->backRefs.end();
    4669              ++it)
    4670         {
    4671             if (it->fInCurState)
    4672             {
    4673                 /* Note: when a VM snapshot is being taken, all normal media
    4674                  * attached to the VM in the current state will be, as an
    4675                  * exception, also associated with the snapshot which is about
    4676                  * to create (see SnapshotMachine::init()) before deassociating
    4677                  * them from the current state (which takes place only on
    4678                  * success in Machine::fixupHardDisks()), so that the size of
    4679                  * snapshotIds will be 1 in this case. The extra condition is
    4680                  * used to filter out this legal situation. */
    4681                 if (it->llSnapshotIds.size() == 0)
    4682                     throw setError(VBOX_E_INVALID_OBJECT_STATE,
    4683                                    tr("Medium '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing media based on it may be created until it is detached"),
    4684                                    m->strLocationFull.c_str(), it->machineId.raw());
    4685 
    4686                 Assert(it->llSnapshotIds.size() == 1);
    4687             }
    4688         }
    4689 
    4690         if (aProgress != NULL)
    4691         {
    4692             /* use the existing progress object... */
    4693             pProgress = *aProgress;
    4694 
    4695             /* ...but create a new one if it is null */
    4696             if (pProgress.isNull())
    4697             {
    4698                 pProgress.createObject();
    4699                 rc = pProgress->init(m->pVirtualBox,
    4700                                      static_cast<IMedium*>(this),
    4701                                      BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
    4702                                      TRUE /* aCancelable */);
    4703                 if (FAILED(rc))
    4704                     throw rc;
    4705             }
    4706         }
    4707 
    4708         /* setup task object to carry out the operation sync/async */
    4709         pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
    4710                                            aMediumLockList,
    4711                                            aWait /* fKeepMediumLockList */);
    4712         rc = pTask->rc();
    4713         AssertComRC(rc);
    4714         if (FAILED(rc))
    4715              throw rc;
    4716 
    4717         /* register a task (it will deregister itself when done) */
    4718         ++m->numCreateDiffTasks;
    4719         Assert(m->numCreateDiffTasks != 0); /* overflow? */
    4720 
    4721         aTarget->m->state = MediumState_Creating;
    4722     }
    4723     catch (HRESULT aRC) { rc = aRC; }
    4724 
    4725     if (SUCCEEDED(rc))
    4726     {
    4727         if (aWait)
    4728             rc = runNow(pTask, pfNeedsGlobalSaveSettings);
    4729         else
    4730             rc = startThread(pTask);
    4731 
    4732         if (SUCCEEDED(rc) && aProgress != NULL)
    4733             *aProgress = pProgress;
    4734     }
    4735     else if (pTask != NULL)
    4736         delete pTask;
    4737 
    4738     return rc;
    4739 }
    4740 
    4741 /**
    4742  * Prepares this (source) medium, target medium and all intermediate media
    4743  * for the merge operation.
    4744  *
    4745  * This method is to be called prior to calling the #mergeTo() to perform
    4746  * necessary consistency checks and place involved media to appropriate
    4747  * states. If #mergeTo() is not called or fails, the state modifications
    4748  * performed by this method must be undone by #cancelMergeTo().
    4749  *
    4750  * See #mergeTo() for more information about merging.
    4751  *
    4752  * @param pTarget       Target medium.
    4753  * @param aMachineId    Allowed machine attachment. NULL means do not check.
    4754  * @param aSnapshotId   Allowed snapshot attachment. NULL or empty UUID means
    4755  *                      do not check.
    4756  * @param fLockMedia    Flag whether to lock the medium lock list or not.
    4757  *                      If set to false and the medium lock list locking fails
    4758  *                      later you must call #cancelMergeTo().
    4759  * @param fMergeForward Resulting merge direction (out).
    4760  * @param pParentForTarget New parent for target medium after merge (out).
    4761  * @param aChildrenToReparent List of children of the source which will have
    4762  *                      to be reparented to the target after merge (out).
    4763  * @param aMediumLockList Medium locking information (out).
    4764  *
    4765  * @note Locks medium tree for reading. Locks this object, aTarget and all
    4766  *       intermediate media for writing.
    4767  */
    4768 HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
    4769                                const Guid *aMachineId,
    4770                                const Guid *aSnapshotId,
    4771                                bool fLockMedia,
    4772                                bool &fMergeForward,
    4773                                ComObjPtr<Medium> &pParentForTarget,
    4774                                MediaList &aChildrenToReparent,
    4775                                MediumLockList * &aMediumLockList)
    4776 {
    4777     AssertReturn(pTarget != NULL, E_FAIL);
    4778     AssertReturn(pTarget != this, E_FAIL);
    4779 
    4780     AutoCaller autoCaller(this);
    4781     AssertComRCReturnRC(autoCaller.rc());
    4782 
    4783     AutoCaller targetCaller(pTarget);
    4784     AssertComRCReturnRC(targetCaller.rc());
    4785 
    4786     HRESULT rc = S_OK;
    4787     fMergeForward = false;
    4788     pParentForTarget.setNull();
    4789     aChildrenToReparent.clear();
    4790     Assert(aMediumLockList == NULL);
    4791     aMediumLockList = NULL;
    4792 
    4793     try
    4794     {
    4795         // locking: we need the tree lock first because we access parent pointers
    4796         AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    4797 
    4798         /* more sanity checking and figuring out the merge direction */
    4799         ComObjPtr<Medium> pMedium = getParent();
    4800         while (!pMedium.isNull() && pMedium != pTarget)
    4801             pMedium = pMedium->getParent();
    4802         if (pMedium == pTarget)
    4803             fMergeForward = false;
    4804         else
    4805         {
    4806             pMedium = pTarget->getParent();
    4807             while (!pMedium.isNull() && pMedium != this)
    4808                 pMedium = pMedium->getParent();
    4809             if (pMedium == this)
    4810                 fMergeForward = true;
    4811             else
    4812             {
    4813                 Utf8Str tgtLoc;
    4814                 {
    4815                     AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
    4816                     tgtLoc = pTarget->getLocationFull();
    4817                 }
    4818 
    4819                 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    4820                 throw setError(VBOX_E_INVALID_OBJECT_STATE,
    4821                                tr("Media '%s' and '%s' are unrelated"),
    4822                                m->strLocationFull.c_str(), tgtLoc.c_str());
    4823             }
    4824         }
    4825 
    4826         /* Build the lock list. */
    4827         aMediumLockList = new MediumLockList();
    4828         if (fMergeForward)
    4829             rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
    4830                                                true /* fMediumLockWrite */,
    4831                                                NULL,
    4832                                                *aMediumLockList);
    4833         else
    4834             rc = createMediumLockList(true /* fFailIfInaccessible */,
    4835                                       false /* fMediumLockWrite */,
    4836                                       NULL,
    4837                                       *aMediumLockList);
    4838         if (FAILED(rc))
    4839             throw rc;
    4840 
    4841         /* Sanity checking, must be after lock list creation as it depends on
    4842          * valid medium states. The medium objects must be accessible. Only
    4843          * do this if immediate locking is requested, otherwise it fails when
    4844          * we construct a medium lock list for an already running VM. Snapshot
    4845          * deletion uses this to simplify its life. */
    4846         if (fLockMedia)
    4847         {
    4848             {
    4849                 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    4850                 if (m->state != MediumState_Created)
    4851                     throw setStateError();
    4852             }
    4853             {
    4854                 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
    4855                 if (pTarget->m->state != MediumState_Created)
    4856                     throw pTarget->setStateError();
    4857             }
    4858         }
    4859 
    4860         /* check medium attachment and other sanity conditions */
    4861         if (fMergeForward)
    4862         {
    4863             AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    4864             if (getChildren().size() > 1)
    4865             {
    4866                 throw setError(VBOX_E_INVALID_OBJECT_STATE,
    4867                                tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
    4868                                m->strLocationFull.c_str(), getChildren().size());
    4869             }
    4870             /* One backreference is only allowed if the machine ID is not empty
    4871              * and it matches the machine the medium is attached to (including
    4872              * the snapshot ID if not empty). */
    4873             if (   m->backRefs.size() != 0
    4874                 && (   !aMachineId
    4875                     || m->backRefs.size() != 1
    4876                     || aMachineId->isEmpty()
    4877                     || *getFirstMachineBackrefId() != *aMachineId
    4878                     || (   (!aSnapshotId || !aSnapshotId->isEmpty())
    4879                         && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
    4880                 throw setError(VBOX_E_OBJECT_IN_USE,
    4881                                tr("Medium '%s' is attached to %d virtual machines"),
    4882                                m->strLocationFull.c_str(), m->backRefs.size());
    4883             if (m->type == MediumType_Immutable)
    4884                 throw setError(VBOX_E_INVALID_OBJECT_STATE,
    4885                                tr("Medium '%s' is immutable"),
    4886                                m->strLocationFull.c_str());
    4887         }
    4888         else
    4889         {
    4890             AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
    4891             if (pTarget->getChildren().size() > 1)
    4892             {
    4893                 throw setError(VBOX_E_OBJECT_IN_USE,
    4894                                tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
    4895                                pTarget->m->strLocationFull.c_str(),
    4896                                pTarget->getChildren().size());
    4897             }
    4898             if (pTarget->m->type == MediumType_Immutable)
    4899                 throw setError(VBOX_E_INVALID_OBJECT_STATE,
    4900                                tr("Medium '%s' is immutable"),
    4901                                pTarget->m->strLocationFull.c_str());
    4902         }
    4903         ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
    4904         ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
    4905         for (pLast = pLastIntermediate;
    4906              !pLast.isNull() && pLast != pTarget && pLast != this;
    4907              pLast = pLast->getParent())
    4908         {
    4909             AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
    4910             if (pLast->getChildren().size() > 1)
    4911             {
    4912                 throw setError(VBOX_E_OBJECT_IN_USE,
    4913                                tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
    4914                                pLast->m->strLocationFull.c_str(),
    4915                                pLast->getChildren().size());
    4916             }
    4917             if (pLast->m->backRefs.size() != 0)
    4918                 throw setError(VBOX_E_OBJECT_IN_USE,
    4919                                tr("Medium '%s' is attached to %d virtual machines"),
    4920                                pLast->m->strLocationFull.c_str(),
    4921                                pLast->m->backRefs.size());
    4922 
    4923         }
    4924 
    4925         /* Update medium states appropriately */
    4926         if (m->state == MediumState_Created)
    4927         {
    4928             rc = markForDeletion();
    4929             if (FAILED(rc))
    4930                 throw rc;
    4931         }
    4932         else
    4933         {
    4934             if (fLockMedia)
    4935                 throw setStateError();
    4936             else if (   m->state == MediumState_LockedWrite
    4937                      || m->state == MediumState_LockedRead)
    4938             {
    4939                 /* Either mark it for deletion in locked state or allow
    4940                  * others to have done so. */
    4941                 if (m->preLockState == MediumState_Created)
    4942                     markLockedForDeletion();
    4943                 else if (m->preLockState != MediumState_Deleting)
    4944                     throw setStateError();
    4945             }
    4946             else
    4947                 throw setStateError();
    4948         }
    4949 
    4950         if (fMergeForward)
    4951         {
    4952             /* we will need parent to reparent target */
    4953             pParentForTarget = m->pParent;
    4954         }
    4955         else
    4956         {
    4957             /* we will need to reparent children of the source */
    4958             for (MediaList::const_iterator it = getChildren().begin();
    4959                  it != getChildren().end();
    4960                  ++it)
    4961             {
    4962                 pMedium = *it;
    4963                 if (fLockMedia)
    4964                 {
    4965                     rc = pMedium->LockWrite(NULL);
    4966                     if (FAILED(rc))
    4967                         throw rc;
    4968                 }
    4969 
    4970                 aChildrenToReparent.push_back(pMedium);
    4971             }
    4972         }
    4973         for (pLast = pLastIntermediate;
    4974              !pLast.isNull() && pLast != pTarget && pLast != this;
    4975              pLast = pLast->getParent())
    4976         {
    4977             AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
    4978             if (pLast->m->state == MediumState_Created)
    4979             {
    4980                 rc = pLast->markForDeletion();
    4981                 if (FAILED(rc))
    4982                     throw rc;
    4983             }
    4984             else
    4985                 throw pLast->setStateError();
    4986         }
    4987 
    4988         /* Tweak the lock list in the backward merge case, as the target
    4989          * isn't marked to be locked for writing yet. */
    4990         if (!fMergeForward)
    4991         {
    4992             MediumLockList::Base::iterator lockListBegin =
    4993                 aMediumLockList->GetBegin();
    4994             MediumLockList::Base::iterator lockListEnd =
    4995                 aMediumLockList->GetEnd();
    4996             lockListEnd--;
    4997             for (MediumLockList::Base::iterator it = lockListBegin;
    4998                  it != lockListEnd;
    4999                  ++it)
    5000             {
    5001                 MediumLock &mediumLock = *it;
    5002                 if (mediumLock.GetMedium() == pTarget)
    5003                 {
    5004                     HRESULT rc2 = mediumLock.UpdateLock(true);
    5005                     AssertComRC(rc2);
    5006                     break;
    5007                 }
    5008             }
    5009         }
    5010 
    5011         if (fLockMedia)
    5012         {
    5013             rc = aMediumLockList->Lock();
    5014             if (FAILED(rc))
    5015             {
    5016                 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
    5017                 throw setError(rc,
    5018                                tr("Failed to lock media when merging to '%s'"),
    5019                                pTarget->getLocationFull().c_str());
    5020             }
    5021         }
    5022     }
    5023     catch (HRESULT aRC) { rc = aRC; }
    5024 
    5025     if (FAILED(rc))
    5026     {
    5027         delete aMediumLockList;
    5028         aMediumLockList = NULL;
    5029     }
    5030 
    5031     return rc;
    5032 }
    5033 
    5034 /**
    5035  * Merges this medium to the specified medium which must be either its
    5036  * direct ancestor or descendant.
    5037  *
    5038  * Given this medium is SOURCE and the specified medium is TARGET, we will
    5039  * get two variants of the merge operation:
    5040  *
    5041  *                forward merge
    5042  *                ------------------------->
    5043  *  [Extra] <- SOURCE <- Intermediate <- TARGET
    5044  *  Any        Del       Del             LockWr
    5045  *
    5046  *
    5047  *                            backward merge
    5048  *                <-------------------------
    5049  *             TARGET <- Intermediate <- SOURCE <- [Extra]
    5050  *             LockWr    Del             Del       LockWr
    5051  *
    5052  * Each diagram shows the involved media on the media chain where
    5053  * SOURCE and TARGET belong. Under each medium there is a state value which
    5054  * the medium must have at a time of the mergeTo() call.
    5055  *
    5056  * The media in the square braces may be absent (e.g. when the forward
    5057  * operation takes place and SOURCE is the base medium, or when the backward
    5058  * merge operation takes place and TARGET is the last child in the chain) but if
    5059  * they present they are involved too as shown.
    5060  *
    5061  * Neither the source medium nor intermediate media may be attached to
    5062  * any VM directly or in the snapshot, otherwise this method will assert.
    5063  *
    5064  * The #prepareMergeTo() method must be called prior to this method to place all
    5065  * involved to necessary states and perform other consistency checks.
    5066  *
    5067  * If @a aWait is @c true then this method will perform the operation on the
    5068  * calling thread and will not return to the caller until the operation is
    5069  * completed. When this method succeeds, all intermediate medium objects in
    5070  * the chain will be uninitialized, the state of the target medium (and all
    5071  * involved extra media) will be restored. @a aMediumLockList will not be
    5072  * deleted, whether the operation is successful or not. The caller has to do
    5073  * this if appropriate. Note that this (source) medium is not uninitialized
    5074  * because of possible AutoCaller instances held by the caller of this method
    5075  * on the current thread. It's therefore the responsibility of the caller to
    5076  * call Medium::uninit() after releasing all callers.
    5077  *
    5078  * If @a aWait is @c false then this method will create a thread to perform the
    5079  * operation asynchronously and will return immediately. If the operation
    5080  * succeeds, the thread will uninitialize the source medium object and all
    5081  * intermediate medium objects in the chain, reset the state of the target
    5082  * medium (and all involved extra media) and delete @a aMediumLockList.
    5083  * If the operation fails, the thread will only reset the states of all
    5084  * involved media and delete @a aMediumLockList.
    5085  *
    5086  * When this method fails (regardless of the @a aWait mode), it is a caller's
    5087  * responsibility to undo state changes and delete @a aMediumLockList using
    5088  * #cancelMergeTo().
    5089  *
    5090  * If @a aProgress is not NULL but the object it points to is @c null then a new
    5091  * progress object will be created and assigned to @a *aProgress on success,
    5092  * otherwise the existing progress object is used. If Progress is NULL, then no
    5093  * progress object is created/used at all. Note that @a aProgress cannot be
    5094  * NULL when @a aWait is @c false (this method will assert in this case).
    5095  *
    5096  * @param pTarget       Target medium.
    5097  * @param fMergeForward Merge direction.
    5098  * @param pParentForTarget New parent for target medium after merge.
    5099  * @param aChildrenToReparent List of children of the source which will have
    5100  *                      to be reparented to the target after merge.
    5101  * @param aMediumLockList Medium locking information.
    5102  * @param aProgress     Where to find/store a Progress object to track operation
    5103  *                      completion.
    5104  * @param aWait         @c true if this method should block instead of creating
    5105  *                      an asynchronous thread.
    5106  * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
    5107  *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
    5108  *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
    5109  *                and this parameter is ignored.
    5110  *
    5111  * @note Locks the tree lock for writing. Locks the media from the chain
    5112  *       for writing.
    5113  */
    5114 HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
    5115                         bool fMergeForward,
    5116                         const ComObjPtr<Medium> &pParentForTarget,
    5117                         const MediaList &aChildrenToReparent,
    5118                         MediumLockList *aMediumLockList,
    5119                         ComObjPtr <Progress> *aProgress,
    5120                         bool aWait,
    5121                         bool *pfNeedsGlobalSaveSettings)
    5122 {
    5123     AssertReturn(pTarget != NULL, E_FAIL);
    5124     AssertReturn(pTarget != this, E_FAIL);
    5125     AssertReturn(aMediumLockList != NULL, E_FAIL);
    5126     AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
    5127 
    5128     AutoCaller autoCaller(this);
    5129     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5130 
    5131     AutoCaller targetCaller(pTarget);
    5132     AssertComRCReturnRC(targetCaller.rc());
    5133 
    5134     HRESULT rc = S_OK;
    5135     ComObjPtr <Progress> pProgress;
    5136     Medium::Task *pTask = NULL;
    5137 
    5138     try
    5139     {
    5140         if (aProgress != NULL)
    5141         {
    5142             /* use the existing progress object... */
    5143             pProgress = *aProgress;
    5144 
    5145             /* ...but create a new one if it is null */
    5146             if (pProgress.isNull())
    5147             {
    5148                 Utf8Str tgtName;
    5149                 {
    5150                     AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
    5151                     tgtName = pTarget->getName();
    5152                 }
    5153 
    5154                 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    5155 
    5156                 pProgress.createObject();
    5157                 rc = pProgress->init(m->pVirtualBox,
    5158                                      static_cast<IMedium*>(this),
    5159                                      BstrFmt(tr("Merging medium '%s' to '%s'"),
    5160                                              getName().c_str(),
    5161                                              tgtName.c_str()).raw(),
    5162                                      TRUE /* aCancelable */);
    5163                 if (FAILED(rc))
    5164                     throw rc;
    5165             }
    5166         }
    5167 
    5168         /* setup task object to carry out the operation sync/async */
    5169         pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
    5170                                       pParentForTarget, aChildrenToReparent,
    5171                                       pProgress, aMediumLockList,
    5172                                       aWait /* fKeepMediumLockList */);
    5173         rc = pTask->rc();
    5174         AssertComRC(rc);
    5175         if (FAILED(rc))
    5176             throw rc;
    5177     }
    5178     catch (HRESULT aRC) { rc = aRC; }
    5179 
    5180     if (SUCCEEDED(rc))
    5181     {
    5182         if (aWait)
    5183             rc = runNow(pTask, pfNeedsGlobalSaveSettings);
    5184         else
    5185             rc = startThread(pTask);
    5186 
    5187         if (SUCCEEDED(rc) && aProgress != NULL)
    5188             *aProgress = pProgress;
    5189     }
    5190     else if (pTask != NULL)
    5191         delete pTask;
    5192 
    5193     return rc;
    5194 }
    5195 
    5196 /**
    5197  * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
    5198  * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
    5199  * the medium objects in @a aChildrenToReparent.
    5200  *
    5201  * @param aChildrenToReparent List of children of the source which will have
    5202  *                      to be reparented to the target after merge.
    5203  * @param aMediumLockList Medium locking information.
    5204  *
    5205  * @note Locks the media from the chain for writing.
    5206  */
    5207 void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
    5208                            MediumLockList *aMediumLockList)
    5209 {
    5210     AutoCaller autoCaller(this);
    5211     AssertComRCReturnVoid(autoCaller.rc());
    5212 
    5213     AssertReturnVoid(aMediumLockList != NULL);
    5214 
    5215     /* Revert media marked for deletion to previous state. */
    5216     HRESULT rc;
    5217     MediumLockList::Base::const_iterator mediumListBegin =
    5218         aMediumLockList->GetBegin();
    5219     MediumLockList::Base::const_iterator mediumListEnd =
    5220         aMediumLockList->GetEnd();
    5221     for (MediumLockList::Base::const_iterator it = mediumListBegin;
    5222          it != mediumListEnd;
    5223          ++it)
    5224     {
    5225         const MediumLock &mediumLock = *it;
    5226         const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
    5227         AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
    5228 
    5229         if (pMedium->m->state == MediumState_Deleting)
    5230         {
    5231             rc = pMedium->unmarkForDeletion();
    5232             AssertComRC(rc);
    5233         }
    5234     }
    5235 
    5236     /* the destructor will do the work */
    5237     delete aMediumLockList;
    5238 
    5239     /* unlock the children which had to be reparented */
    5240     for (MediaList::const_iterator it = aChildrenToReparent.begin();
    5241          it != aChildrenToReparent.end();
    5242          ++it)
    5243     {
    5244         const ComObjPtr<Medium> &pMedium = *it;
    5245 
    5246         AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
    5247         pMedium->UnlockWrite(NULL);
    5248     }
    5249 }
    5250 
    5251 
    5252 HRESULT Medium::exportFile(const char *aFilename,
    5253                            const ComObjPtr<MediumFormat> &aFormat,
    5254                            MediumVariant_T aVariant,
    5255                            void *aVDImageIOCallbacks, void *aVDImageIOUser,
    5256                            const ComObjPtr<Progress> &aProgress)
    5257 {
    5258     AssertPtrReturn(aFilename, E_INVALIDARG);
    5259     AssertReturn(!aFormat.isNull(), E_INVALIDARG);
    5260     AssertReturn(!aProgress.isNull(), E_INVALIDARG);
    5261 
    5262     AutoCaller autoCaller(this);
    5263     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5264 
    5265     HRESULT rc = S_OK;
    5266     Medium::Task *pTask = NULL;
    5267 
    5268     try
    5269     {
    5270         // locking: we need the tree lock first because we access parent pointers
    5271         AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    5272         // and we need to write-lock the media involved
    5273         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    5274 
    5275         /* Build the source lock list. */
    5276         MediumLockList *pSourceMediumLockList(new MediumLockList());
    5277         rc = createMediumLockList(true /* fFailIfInaccessible */,
    5278                                   false /* fMediumLockWrite */,
    5279                                   NULL,
    5280                                   *pSourceMediumLockList);
    5281         if (FAILED(rc))
    5282         {
    5283             delete pSourceMediumLockList;
    5284             throw rc;
    5285         }
    5286 
    5287         rc = pSourceMediumLockList->Lock();
    5288         if (FAILED(rc))
    5289         {
    5290             delete pSourceMediumLockList;
    5291             throw setError(rc,
    5292                            tr("Failed to lock source media '%s'"),
    5293                            getLocationFull().c_str());
    5294         }
    5295 
    5296         /* setup task object to carry out the operation asynchronously */
    5297         pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
    5298                                        aVariant, aVDImageIOCallbacks,
    5299                                        aVDImageIOUser, pSourceMediumLockList);
    5300         rc = pTask->rc();
    5301         AssertComRC(rc);
    5302         if (FAILED(rc))
    5303             throw rc;
    5304     }
    5305     catch (HRESULT aRC) { rc = aRC; }
    5306 
    5307     if (SUCCEEDED(rc))
    5308         rc = startThread(pTask);
    5309     else if (pTask != NULL)
    5310         delete pTask;
    5311 
    5312     return rc;
    5313 }
    5314 
    5315 HRESULT Medium::importFile(const char *aFilename,
    5316                            const ComObjPtr<MediumFormat> &aFormat,
    5317                            MediumVariant_T aVariant,
    5318                            void *aVDImageIOCallbacks, void *aVDImageIOUser,
    5319                            const ComObjPtr<Medium> &aParent,
    5320                            const ComObjPtr<Progress> &aProgress)
    5321 {
    5322     AssertPtrReturn(aFilename, E_INVALIDARG);
    5323     AssertReturn(!aFormat.isNull(), E_INVALIDARG);
    5324     AssertReturn(!aProgress.isNull(), E_INVALIDARG);
    5325 
    5326     AutoCaller autoCaller(this);
    5327     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    5328 
    5329     HRESULT rc = S_OK;
    5330     Medium::Task *pTask = NULL;
    5331 
    5332     try
    5333     {
    5334         // locking: we need the tree lock first because we access parent pointers
    5335         AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    5336         // and we need to write-lock the media involved
    5337         AutoMultiWriteLock2 alock(this, aParent COMMA_LOCKVAL_SRC_POS);
    5338 
    5339         if (   m->state != MediumState_NotCreated
    5340             && m->state != MediumState_Created)
    5341             throw setStateError();
    5342 
    5343         /* Build the target lock list. */
    5344         MediumLockList *pTargetMediumLockList(new MediumLockList());
    5345         rc = createMediumLockList(true /* fFailIfInaccessible */,
    5346                                   true /* fMediumLockWrite */,
    5347                                   aParent,
    5348                                   *pTargetMediumLockList);
    5349         if (FAILED(rc))
    5350         {
    5351             delete pTargetMediumLockList;
    5352             throw rc;
    5353         }
    5354 
    5355         rc = pTargetMediumLockList->Lock();
    5356         if (FAILED(rc))
    5357         {
    5358             delete pTargetMediumLockList;
    5359             throw setError(rc,
    5360                            tr("Failed to lock target media '%s'"),
    5361                            getLocationFull().c_str());
    5362         }
    5363 
    5364         /* setup task object to carry out the operation asynchronously */
    5365         pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,
    5366                                        aVariant, aVDImageIOCallbacks,
    5367                                        aVDImageIOUser, aParent,
    5368                                        pTargetMediumLockList);
    5369         rc = pTask->rc();
    5370         AssertComRC(rc);
    5371         if (FAILED(rc))
    5372             throw rc;
    5373 
    5374         if (m->state == MediumState_NotCreated)
    5375             m->state = MediumState_Creating;
    5376     }
    5377     catch (HRESULT aRC) { rc = aRC; }
    5378 
    5379     if (SUCCEEDED(rc))
    5380         rc = startThread(pTask);
    5381     else if (pTask != NULL)
    5382         delete pTask;
    5383 
    5384     return rc;
    5385 }
    5386 
    5387 ////////////////////////////////////////////////////////////////////////////////
    5388 //
    5389 // Private methods
    5390 //
    5391 ////////////////////////////////////////////////////////////////////////////////
    5392 
    5393 /**
    5394  * Performs extra checks if the medium can be closed and returns S_OK in
    5395  * this case. Otherwise, returns a respective error message. Called by
    5396  * Close() under the medium tree lock and the medium lock.
    5397  *
    5398  * @note Also reused by Medium::Reset().
    5399  *
    5400  * @note Caller must hold the media tree write lock!
    5401  */
    5402 HRESULT Medium::canClose()
    5403 {
    5404     Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    5405 
    5406     if (getChildren().size() != 0)
    5407         return setError(VBOX_E_OBJECT_IN_USE,
    5408                         tr("Cannot close medium '%s' because it has %d child media"),
    5409                         m->strLocationFull.c_str(), getChildren().size());
    5410 
    5411     return S_OK;
    5412 }
    5413 
    5414 /**
    5415  * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
    5416  *
    5417  * This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending
    5418  * on the device type of this medium.
    5419  *
    5420  * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
    5421  *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
    5422  *
    5423  * @note Caller must have locked the media tree lock for writing!
    5424  */
    5425 HRESULT Medium::unregisterWithVirtualBox(bool *pfNeedsGlobalSaveSettings)
    5426 {
    5427     /* Note that we need to de-associate ourselves from the parent to let
    5428      * unregisterHardDisk() properly save the registry */
    5429 
    5430     /* we modify mParent and access children */
    5431     Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    5432 
    5433     Medium *pParentBackup = m->pParent;
    5434     AssertReturn(getChildren().size() == 0, E_FAIL);
    5435     if (m->pParent)
    5436         deparent();
    5437 
    5438     HRESULT rc = E_FAIL;
    5439     switch (m->devType)
    5440     {
    5441         case DeviceType_DVD:
    5442             rc = m->pVirtualBox->unregisterImage(this, DeviceType_DVD, pfNeedsGlobalSaveSettings);
    5443         break;
    5444 
    5445         case DeviceType_Floppy:
    5446             rc = m->pVirtualBox->unregisterImage(this, DeviceType_Floppy, pfNeedsGlobalSaveSettings);
    5447         break;
    5448 
    5449         case DeviceType_HardDisk:
    5450             rc = m->pVirtualBox->unregisterHardDisk(this, pfNeedsGlobalSaveSettings);
    5451         break;
    5452 
    5453         default:
    5454         break;
    5455     }
    5456 
    5457     if (FAILED(rc))
    5458     {
    5459         if (pParentBackup)
    5460         {
    5461             // re-associate with the parent as we are still relatives in the registry
    5462             m->pParent = pParentBackup;
    5463             m->pParent->m->llChildren.push_back(this);
    5464         }
    5465     }
    5466 
    5467     return rc;
    5468 }
    5469 
    5470 /**
    5471  * Checks that the format ID is valid and sets it on success.
    5472  *
    5473  * Note that this method will caller-reference the format object on success!
    5474  * This reference must be released somewhere to let the MediumFormat object be
    5475  * uninitialized.
    5476  *
    5477  * @note Must be called from under this object's write lock.
    5478  */
    5479 HRESULT Medium::setFormat(const Utf8Str &aFormat)
    5480 {
    5481     /* get the format object first */
    5482     {
    5483         SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
    5484         AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
    5485 
    5486         unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
    5487         if (m->formatObj.isNull())
    5488             return setError(E_INVALIDARG,
    5489                             tr("Invalid medium storage format '%s'"),
    5490                             aFormat.c_str());
    5491 
    5492         /* reference the format permanently to prevent its unexpected
    5493          * uninitialization */
    5494         HRESULT rc = m->formatObj->addCaller();
    5495         AssertComRCReturnRC(rc);
    5496 
    5497         /* get properties (preinsert them as keys in the map). Note that the
    5498          * map doesn't grow over the object life time since the set of
    5499          * properties is meant to be constant. */
    5500 
    5501         Assert(m->mapProperties.empty());
    5502 
    5503         for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
    5504              it != m->formatObj->getProperties().end();
    5505              ++it)
    5506         {
    5507             m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
    5508         }
    5509     }
    5510 
    5511     unconst(m->strFormat) = aFormat;
    5512 
    5513     return S_OK;
    5514 }
    5515 
    5516 /**
    55175628 * Returns the last error message collected by the vdErrorCall callback and
    55185629 * resets it.
     
    57445855}
    57455856
    5746 
    57475857/**
    57485858 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
     
    57745884
    57755885    return S_OK;
    5776 }
    5777 
    5778 /**
    5779  * Fix the parent UUID of all children to point to this medium as their
    5780  * parent.
    5781  */
    5782 HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
    5783 {
    5784     MediumLockList mediumLockList;
    5785     HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
    5786                                       false /* fMediumLockWrite */,
    5787                                       this,
    5788                                       mediumLockList);
    5789     AssertComRCReturnRC(rc);
    5790 
    5791     try
    5792     {
    5793         PVBOXHDD hdd;
    5794         int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
    5795         ComAssertRCThrow(vrc, E_FAIL);
    5796 
    5797         try
    5798         {
    5799             MediumLockList::Base::iterator lockListBegin =
    5800                 mediumLockList.GetBegin();
    5801             MediumLockList::Base::iterator lockListEnd =
    5802                 mediumLockList.GetEnd();
    5803             for (MediumLockList::Base::iterator it = lockListBegin;
    5804                  it != lockListEnd;
    5805                  ++it)
    5806             {
    5807                 MediumLock &mediumLock = *it;
    5808                 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
    5809                 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
    5810 
    5811                 // open the medium
    5812                 vrc = VDOpen(hdd,
    5813                              pMedium->m->strFormat.c_str(),
    5814                              pMedium->m->strLocationFull.c_str(),
    5815                              VD_OPEN_FLAGS_READONLY,
    5816                              pMedium->m->vdImageIfaces);
    5817                 if (RT_FAILURE(vrc))
    5818                     throw vrc;
    5819             }
    5820 
    5821             for (MediaList::const_iterator it = childrenToReparent.begin();
    5822                  it != childrenToReparent.end();
    5823                  ++it)
    5824             {
    5825                 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
    5826                 vrc = VDOpen(hdd,
    5827                              (*it)->m->strFormat.c_str(),
    5828                              (*it)->m->strLocationFull.c_str(),
    5829                              VD_OPEN_FLAGS_INFO,
    5830                              (*it)->m->vdImageIfaces);
    5831                 if (RT_FAILURE(vrc))
    5832                     throw vrc;
    5833 
    5834                 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
    5835                 if (RT_FAILURE(vrc))
    5836                     throw vrc;
    5837 
    5838                 vrc = VDClose(hdd, false /* fDelete */);
    5839                 if (RT_FAILURE(vrc))
    5840                     throw vrc;
    5841 
    5842                 (*it)->UnlockWrite(NULL);
    5843             }
    5844         }
    5845         catch (HRESULT aRC) { rc = aRC; }
    5846         catch (int aVRC)
    5847         {
    5848             throw setError(E_FAIL,
    5849                             tr("Could not update medium UUID references to parent '%s' (%s)"),
    5850                             m->strLocationFull.c_str(),
    5851                             vdError(aVRC).c_str());
    5852         }
    5853 
    5854         VDDestroy(hdd);
    5855     }
    5856     catch (HRESULT aRC) { rc = aRC; }
    5857 
    5858     return rc;
    58595886}
    58605887
  • trunk/src/VBox/Main/include/MediumImpl.h

    r33567 r33908  
    213213                              bool aWait,
    214214                              bool *pfNeedsGlobalSaveSettings);
     215    Utf8Str getPreferredDiffFormat();
    215216
    216217    HRESULT close(bool *pfNeedsGlobalSaveSettings, AutoCaller &autoCaller);
     
    242243    HRESULT fixParentUuidOfChildren(const MediaList &childrenToReparent);
    243244
    244     /**
    245      * Used by IAppliance to export disk images.
    246      *
    247      * @param aFilename             Filename to create (UTF8).
    248      * @param aFormat               Medium format for creating @a aFilename.
    249      * @param aVariant              Which exact image format variant to use
    250      *                              for the destination image.
    251      * @param aVDImageIOCallbacks   Pointer to the callback table for a
    252      *                              VDINTERFACEIO interface. May be NULL.
    253      * @param aVDImageIOUser        Opaque data for the callbacks.
    254      * @param aProgress             Progress object to use.
    255      * @return
    256      * @note The source format is defined by the Medium instance.
    257      */
    258245    HRESULT exportFile(const char *aFilename,
    259246                       const ComObjPtr<MediumFormat> &aFormat,
     
    261248                       void *aVDImageIOCallbacks, void *aVDImageIOUser,
    262249                       const ComObjPtr<Progress> &aProgress);
    263     /**
    264      * Used by IAppliance to import disk images.
    265      *
    266      * @param aFilename             Filename to read (UTF8).
    267      * @param aFormat               Medium format for reading @a aFilename.
    268      * @param aVariant              Which exact image format variant to use
    269      *                              for the destination image.
    270      * @param aVDImageIOCallbacks   Pointer to the callback table for a
    271      *                              VDINTERFACEIO interface. May be NULL.
    272      * @param aVDImageIOUser        Opaque data for the callbacks.
    273      * @param aParent               Parent medium. May be NULL.
    274      * @param aProgress             Progress object to use.
    275      * @return
    276      * @note The destination format is defined by the Medium instance.
    277      */
    278250    HRESULT importFile(const char *aFilename,
    279251                       const ComObjPtr<MediumFormat> &aFormat,
     
    283255                       const ComObjPtr<Progress> &aProgress);
    284256
    285     /** Returns a preferred format for a differencing hard disk. */
    286     Utf8Str getPreferredDiffFormat();
    287 
    288257private:
    289258
     
    298267    HRESULT setFormat(const Utf8Str &aFormat);
    299268
    300     Utf8Str vdError(int aVRC);
    301 
    302269    VDTYPE convertDeviceType();
    303270    DeviceType_T convertToDeviceType(VDTYPE enmType);
     271
     272    Utf8Str vdError(int aVRC);
    304273
    305274    static DECLCALLBACK(void) vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
Note: See TracChangeset for help on using the changeset viewer.

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