VirtualBox

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


Ignore:
Timestamp:
Mar 25, 2015 4:56:48 PM (10 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
99196
Message:

Main/Medium+Snapshot: make all code recursing over trees (objects containing lists of child objects) use as little stack as possible, and establish safe depth limits to avoid crashes, plus related cleanups in related code areas

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

Legend:

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

    r54927 r54948  
    42734273    }
    42744274
     4275    /* Save modified registries, but skip this machine as it's the caller's
     4276     * job to save its settings like all other settings changes. */
     4277    mParent->i_unmarkRegistryModified(i_getId());
    42754278    mParent->i_saveModifiedRegistries();
    42764279
     
    43534356    alock.release();
    43544357
     4358    /* Save modified registries, but skip this machine as it's the caller's
     4359     * job to save its settings like all other settings changes. */
     4360    mParent->i_unmarkRegistryModified(i_getId());
    43554361    mParent->i_saveModifiedRegistries();
    43564362
     
    47944800    multiLock.release();
    47954801
     4802    /* Save modified registries, but skip this machine as it's the caller's
     4803     * job to save its settings like all other settings changes. */
     4804    mParent->i_unmarkRegistryModified(i_getId());
    47964805    mParent->i_saveModifiedRegistries();
    47974806
     
    51185127    if (cSnapshots)
    51195128    {
    5120         // autoCleanup must be true here, or we would have failed above
    5121 
    51225129        // add the media from the medium attachments of the snapshots to llMedia
    51235130        // as well, after the "main" machine media; Snapshot::uninitRecursively()
     
    92789285            if (puuidRegistry)
    92799286                // caller wants registry ID to be set on all attached media (OVF import case)
    9280                 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
     9287                medium->i_addRegistry(*puuidRegistry);
    92819288        }
    92829289
     
    99639970        if (mData->mFirstSnapshot)
    99649971        {
    9965             settings::Snapshot snapNew;
    9966             config.llFirstSnapshot.push_back(snapNew);
    9967 
    9968             // get reference to the fresh copy of the snapshot on the list and
    9969             // work on that copy directly to avoid excessive copying later
    9970             settings::Snapshot &snap = config.llFirstSnapshot.front();
    9971 
    9972             rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
     9972            // the settings use a list for "the first snapshot"
     9973            config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
     9974
     9975            // get reference to the snapshot on the list and work on that
     9976            // element straight in the list to avoid excessive copying later
     9977            rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
    99739978            if (FAILED(rc)) throw rc;
    99749979        }
     
    1047310478        uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
    1047410479
    10475     if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
     10480    if (pMedium->i_addRegistry(uuid))
    1047610481        mParent->i_markRegistryModified(uuid);
    1047710482
     
    1048010485    if (pMedium != pBase)
    1048110486    {
    10482         if (pBase->i_addRegistry(uuid, true /* fRecurse */))
     10487        /* Tree lock needed by Medium::addRegistry when recursing. */
     10488        AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     10489        if (pBase->i_addRegistryRecursive(uuid))
     10490        {
     10491            treeLock.release();
    1048310492            mParent->i_markRegistryModified(uuid);
     10493        }
    1048410494    }
    1048510495}
  • trunk/src/VBox/Main/src-server/MachineImplCloneVM.cpp

    r54645 r54948  
    13421342                mlock.acquire();
    13431343            }
    1344             pMedium->i_addRegistry(uuid, false /* fRecurse */);
     1344            pMedium->i_addRegistry(uuid);
    13451345        }
    13461346        /* Check if a snapshot folder is necessary and if so doesn't already
  • trunk/src/VBox/Main/src-server/MediumImpl.cpp

    r54945 r54948  
    12131213/**
    12141214 * Initializes the medium object by loading its data from the given settings
    1215  * node. In this mode, the medium will always be opened read/write.
     1215 * node. The medium will always be opened read/write.
    12161216 *
    12171217 * In this case, since we're loading from a registry, uuidMachineRegistry is
     
    12191219 * loading from a per-machine registry.
    12201220 *
    1221  * @param aVirtualBox   VirtualBox object.
    12221221 * @param aParent       Parent medium disk or NULL for a root (base) medium.
    12231222 * @param aDeviceType   Device type of the medium.
    12241223 * @param uuidMachineRegistry The registry to which this medium should be
    12251224 *                            added (global registry UUID or machine UUID).
    1226  * @param aNode         Configuration settings.
     1225 * @param data          Configuration settings.
    12271226 * @param strMachineFolder The machine folder with which to resolve relative paths;
    12281227 *                         if empty, then we use the VirtualBox home directory
     
    12301229 * @note Locks the medium tree for writing.
    12311230 */
    1232 HRESULT Medium::init(VirtualBox *aVirtualBox,
    1233                      Medium *aParent,
    1234                      DeviceType_T aDeviceType,
    1235                      const Guid &uuidMachineRegistry,
    1236                      const settings::Medium &data,
    1237                      const Utf8Str &strMachineFolder)
    1238 {
    1239     using namespace settings;
    1240 
    1241     AssertReturn(aVirtualBox, E_INVALIDARG);
    1242 
    1243     /* Enclose the state transition NotReady->InInit->Ready */
    1244     AutoInitSpan autoInitSpan(this);
    1245     AssertReturn(autoInitSpan.isOk(), E_FAIL);
    1246 
    1247     HRESULT rc = S_OK;
    1248 
    1249     unconst(m->pVirtualBox) = aVirtualBox;
     1231HRESULT Medium::initOne(Medium *aParent,
     1232                        DeviceType_T aDeviceType,
     1233                        const Guid &uuidMachineRegistry,
     1234                        const settings::Medium &data,
     1235                        const Utf8Str &strMachineFolder)
     1236{
     1237    HRESULT rc;
    12501238
    12511239    if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
     
    12581246        // differencing medium: add to parent
    12591247        AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    1260         m->pParent = aParent;
    1261         aParent->m->llChildren.push_back(this);
     1248        // no need to check maximum depth as settings reading did it
     1249        i_setParent(aParent);
    12621250    }
    12631251
     
    13691357                     m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw()));
    13701358
     1359    return S_OK;
     1360}
     1361
     1362/**
     1363 * Initializes the medium object and its children by loading its data from the
     1364 * given settings node. The medium will always be opened read/write.
     1365 *
     1366 * In this case, since we're loading from a registry, uuidMachineRegistry is
     1367 * always set: it's either the global registry UUID or a machine UUID when
     1368 * loading from a per-machine registry.
     1369 *
     1370 * @param aVirtualBox   VirtualBox object.
     1371 * @param aParent       Parent medium disk or NULL for a root (base) medium.
     1372 * @param aDeviceType   Device type of the medium.
     1373 * @param uuidMachineRegistry The registry to which this medium should be added (global registry UUID or machine UUID).
     1374 * @param data          Configuration settings.
     1375 * @param strMachineFolder The machine folder with which to resolve relative paths; if empty, then we use the VirtualBox home directory
     1376 *
     1377 * @note Locks the medium tree for writing.
     1378 */
     1379HRESULT Medium::init(VirtualBox *aVirtualBox,
     1380                     Medium *aParent,
     1381                     DeviceType_T aDeviceType,
     1382                     const Guid &uuidMachineRegistry,
     1383                     const settings::Medium &data,
     1384                     const Utf8Str &strMachineFolder,
     1385                     AutoWriteLock &mediaTreeLock)
     1386{
     1387    using namespace settings;
     1388
     1389    Assert(aVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
     1390    AssertReturn(aVirtualBox, E_INVALIDARG);
     1391
     1392    /* Enclose the state transition NotReady->InInit->Ready */
     1393    AutoInitSpan autoInitSpan(this);
     1394    AssertReturn(autoInitSpan.isOk(), E_FAIL);
     1395
     1396    unconst(m->pVirtualBox) = aVirtualBox;
     1397
     1398    // Do not inline this method call, as the purpose of having this separate
     1399    // is to save on stack size. Less local variables are the key for reaching
     1400    // deep recursion levels with small stack (XPCOM/g++ without optimization).
     1401    HRESULT rc = initOne(aParent, aDeviceType, uuidMachineRegistry, data, strMachineFolder);
     1402
     1403
    13711404    /* Don't call Medium::i_queryInfo for registered media to prevent the calling
    13721405     * thread (i.e. the VirtualBox server startup thread) from an unexpected
     
    13901423                           uuidMachineRegistry,
    13911424                           med,               // child data
    1392                            strMachineFolder);
     1425                           strMachineFolder,
     1426                           mediaTreeLock);
    13931427        if (FAILED(rc)) break;
    13941428
    1395         AutoWriteLock treeLock(aVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    1396 
    1397         rc = m->pVirtualBox->i_registerMedium(pMedium, &pMedium, treeLock);
     1429        rc = m->pVirtualBox->i_registerMedium(pMedium, &pMedium, mediaTreeLock);
    13981430        if (FAILED(rc)) break;
    13991431    }
     
    30733105                            m->strLocationFull.c_str(), m->backRefs.size());
    30743106
    3075         if (m->llChildren.size() != 0)
     3107        if (i_getChildren().size() != 0)
    30763108            return setError(VBOX_E_INVALID_OBJECT_STATE,
    30773109                            tr("Cannot encrypt medium '%s' because it has %d children"),
    3078                             m->strLocationFull.c_str(), m->llChildren.size());
     3110                            m->strLocationFull.c_str(), i_getChildren().size());
    30793111
    30803112        /* Build the medium lock list. */
     
    31273159                break;
    31283160            }
    3129             else if (pMedium->m->llChildren.size() > 1)
     3161            else if (pMedium->i_getChildren().size() > 1)
    31303162            {
    31313163                rc = setError(VBOX_E_INVALID_OBJECT_STATE,
    31323164                              tr("Cannot encrypt medium '%s' because it has %d children"),
    3133                               pMedium->m->strLocationFull.c_str(), pMedium->m->llChildren.size());
     3165                              pMedium->m->strLocationFull.c_str(), pMedium->i_getChildren().size());
    31343166                break;
    31353167            }
     
    34733505 * See getFirstRegistryMachineId() for details.
    34743506 *
    3475  * If fRecurse == true, then the media tree lock must be held for reading.
    3476  *
    34773507 * @param id
    3478  * @param fRecurse If true, recurses into child media to make sure the whole tree has registries in sync.
    34793508 * @return true if the registry was added; false if the given id was already on the list.
    34803509 */
    3481 bool Medium::i_addRegistry(const Guid& id, bool fRecurse)
     3510bool Medium::i_addRegistry(const Guid& id)
    34823511{
    34833512    AutoCaller autoCaller(this);
     
    35113540        m->llRegistryIDs.push_back(id);
    35123541
    3513     if (fRecurse)
    3514     {
    3515         // Get private list of children and release medium lock straight away.
    3516         MediaList llChildren(m->llChildren);
    3517         alock.release();
    3518 
    3519         for (MediaList::iterator it = llChildren.begin();
    3520              it != llChildren.end();
    3521              ++it)
    3522         {
    3523             Medium *pChild = *it;
    3524             fAdd |= pChild->i_addRegistry(id, true);
    3525         }
    3526     }
    3527 
    35283542    return fAdd;
    35293543}
    35303544
    35313545/**
    3532  * Removes the given UUID from the list of media registry UUIDs. Returns true
    3533  * if found or false if not.
    3534  *
    3535  * If fRecurse == true, then the media tree lock must be held for reading.
     3546 * This adds the given UUID to the list of media registries in which this
     3547 * medium should be registered. The UUID can either be a machine UUID,
     3548 * to add a machine registry, or the global registry UUID as returned by
     3549 * VirtualBox::getGlobalRegistryId(). This recurses over all children.
     3550 *
     3551 * Note that for hard disks, this method does nothing if the medium is
     3552 * already in another registry to avoid having hard disks in more than
     3553 * one registry, which causes trouble with keeping diff images in sync.
     3554 * See getFirstRegistryMachineId() for details.
     3555 *
     3556 * @note the caller must hold the media tree lock for reading.
    35363557 *
    35373558 * @param id
    3538  * @param fRecurse If true, recurses into child media to make sure the whole tree has registries in sync.
    3539  * @return
    3540  */
    3541 bool Medium::i_removeRegistry(const Guid& id, bool fRecurse)
     3559 * @return true if the registry was added; false if the given id was already on the list.
     3560 */
     3561bool Medium::i_addRegistryRecursive(const Guid &id)
     3562{
     3563    AutoCaller autoCaller(this);
     3564    if (FAILED(autoCaller.rc()))
     3565        return false;
     3566
     3567    bool fAdd = i_addRegistry(id);
     3568
     3569    // protected by the medium tree lock held by our original caller
     3570    for (MediaList::const_iterator it = i_getChildren().begin();
     3571         it != i_getChildren().end();
     3572         ++it)
     3573    {
     3574        Medium *pChild = *it;
     3575        fAdd |= pChild->i_addRegistryRecursive(id);
     3576    }
     3577
     3578    return fAdd;
     3579}
     3580
     3581/**
     3582 * Removes the given UUID from the list of media registry UUIDs of this medium.
     3583 *
     3584 * @param id
     3585 * @return true if the UUID was found or false if not.
     3586 */
     3587bool Medium::i_removeRegistry(const Guid &id)
    35423588{
    35433589    AutoCaller autoCaller(this);
     
    35483594    bool fRemove = false;
    35493595
     3596    // @todo r=klaus eliminate this code, replace it by using find.
    35503597    for (GuidList::iterator it = m->llRegistryIDs.begin();
    35513598         it != m->llRegistryIDs.end();
     
    35543601        if ((*it) == id)
    35553602        {
     3603            // getting away with this as the iterator isn't used after
    35563604            m->llRegistryIDs.erase(it);
    35573605            fRemove = true;
     
    35603608    }
    35613609
    3562     if (fRecurse)
    3563     {
    3564         // Get private list of children and release medium lock straight away.
    3565         MediaList llChildren(m->llChildren);
    3566         alock.release();
    3567 
    3568         for (MediaList::iterator it = llChildren.begin();
    3569              it != llChildren.end();
    3570              ++it)
    3571         {
    3572             Medium *pChild = *it;
    3573             fRemove |= pChild->i_removeRegistry(id, true);
    3574         }
     3610    return fRemove;
     3611}
     3612
     3613/**
     3614 * Removes the given UUID from the list of media registry UUIDs, for this
     3615 * medium and all its children recursively.
     3616 *
     3617 * @note the caller must hold the media tree lock for reading.
     3618 *
     3619 * @param id
     3620 * @return true if the UUID was found or false if not.
     3621 */
     3622bool Medium::i_removeRegistryRecursive(const Guid &id)
     3623{
     3624    AutoCaller autoCaller(this);
     3625    if (FAILED(autoCaller.rc()))
     3626        return false;
     3627
     3628    bool fRemove = i_removeRegistry(id);
     3629
     3630    // protected by the medium tree lock held by our original caller
     3631    for (MediaList::const_iterator it = i_getChildren().begin();
     3632         it != i_getChildren().end();
     3633         ++it)
     3634    {
     3635        Medium *pChild = *it;
     3636        fRemove |= pChild->i_removeRegistryRecursive(id);
    35753637    }
    35763638
     
    35863648 * @return
    35873649 */
    3588 bool Medium::i_isInRegistry(const Guid& id)
    3589 {
     3650bool Medium::i_isInRegistry(const Guid &id)
     3651{
     3652    // @todo r=klaus eliminate this code, replace it by using find.
    35903653    for (GuidList::const_iterator it = m->llRegistryIDs.begin();
    35913654         it != m->llRegistryIDs.end();
     
    38323895        return &m->backRefs.front().machineId;
    38333896
    3834     for (MediaList::iterator it = m->llChildren.begin();
    3835          it != m->llChildren.end();
     3897    for (MediaList::const_iterator it = i_getChildren().begin();
     3898         it != i_getChildren().end();
    38363899         ++it)
    38373900    {
     
    39814044
    39824045    return pBase;
     4046}
     4047
     4048/**
     4049 * Returns the depth of this medium in the media chain.
     4050 *
     4051 * @note Locks medium tree for reading.
     4052 */
     4053uint32_t Medium::i_getDepth()
     4054{
     4055    /* it is possible that some previous/concurrent uninit has already cleared
     4056     * the pVirtualBox reference, and in this case we don't need to continue */
     4057    ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
     4058    if (!pVirtualBox)
     4059        return 1;
     4060
     4061    /* we access mParent */
     4062    AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     4063
     4064    uint32_t cDepth = 0;
     4065    ComObjPtr<Medium> pMedium(this);
     4066    while (!pMedium.isNull())
     4067    {
     4068        AutoCaller autoCaller(this);
     4069        AssertReturn(autoCaller.isOk(), cDepth + 1);
     4070
     4071        pMedium = pMedium->m->pParent;
     4072        cDepth++;
     4073    }
     4074
     4075    return cDepth;
    39834076}
    39844077
     
    40414134
    40424135/**
    4043  * Saves medium data by appending a new child node to the given
    4044  * parent XML settings node.
     4136 * Saves the settings of one medium.
     4137 *
     4138 * @note Caller MUST take care of the medium tree lock and caller.
    40454139 *
    40464140 * @param data      Settings struct to be updated.
    40474141 * @param strHardDiskFolder Folder for which paths should be relative.
    4048  *
    4049  * @note Locks this object, medium tree and children for reading.
    4050  */
    4051 HRESULT Medium::i_saveSettings(settings::Medium &data,
    4052                                const Utf8Str &strHardDiskFolder)
    4053 {
    4054     AutoCaller autoCaller(this);
    4055     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    4056 
    4057     /* we access mParent */
    4058     AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    4059 
     4142 */
     4143void Medium::i_saveSettingsOne(settings::Medium &data, const Utf8Str &strHardDiskFolder)
     4144{
    40604145    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    40614146
     
    41174202    if (m->pParent.isNull())
    41184203        data.hdType = m->type;
     4204}
     4205
     4206/**
     4207 * Saves medium data by putting it into the provided data structure.
     4208 * Recurses over all children to save their settings, too.
     4209 *
     4210 * @param data      Settings struct to be updated.
     4211 * @param strHardDiskFolder Folder for which paths should be relative.
     4212 *
     4213 * @note Locks this object, medium tree and children for reading.
     4214 */
     4215HRESULT Medium::i_saveSettings(settings::Medium &data,
     4216                               const Utf8Str &strHardDiskFolder)
     4217{
     4218    AutoCaller autoCaller(this);
     4219    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     4220
     4221    /* we access mParent */
     4222    AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     4223
     4224    i_saveSettingsOne(data, strHardDiskFolder);
    41194225
    41204226    /* save all children */
     4227    settings::MediaList &llSettingsChildren = data.llChildren;
    41214228    for (MediaList::const_iterator it = i_getChildren().begin();
    41224229         it != i_getChildren().end();
    41234230         ++it)
    41244231    {
    4125         settings::Medium med;
    4126         HRESULT rc = (*it)->i_saveSettings(med, strHardDiskFolder);
    4127         AssertComRCReturnRC(rc);
    4128         data.llChildren.push_back(med);
     4232        // Use the element straight in the list to reduce both unnecessary
     4233        // deep copying (when unwinding the recursion the entire medium
     4234        // settings sub-tree is copied) and the stack footprint (the settings
     4235        // need almost 1K, and there can be VMs with long image chains.
     4236        llSettingsChildren.push_back(settings::g_MediumEmpty);
     4237        HRESULT rc = (*it)->i_saveSettings(llSettingsChildren.back(), strHardDiskFolder);
     4238        if (FAILED(rc))
     4239        {
     4240            llSettingsChildren.pop_back();
     4241            return rc;
     4242        }
    41294243    }
    41304244
     
    61206234                    if (m->pParent)
    61216235                        i_deparent();
     6236
     6237                    if (!pParent.isNull())
     6238                        if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
     6239                        {
     6240                            AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
     6241                            throw setError(VBOX_E_INVALID_OBJECT_STATE,
     6242                                           tr("Cannot open differencing image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
     6243                                           pParent->m->strLocationFull.c_str());
     6244                        }
    61226245                    i_setParent(pParent);
    61236246
     
    63536476        {
    63546477            // re-associate with the parent as we are still relatives in the registry
    6355             m->pParent = pParentBackup;
    6356             m->pParent->m->llChildren.push_back(this);
     6478            i_setParent(pParentBackup);
    63576479        }
    63586480    }
     
    73467468    try
    73477469    {
     7470        if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
     7471        {
     7472            AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     7473            throw setError(VBOX_E_INVALID_OBJECT_STATE,
     7474                           tr("Cannot create differencing image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
     7475                           m->strLocationFull.c_str());
     7476        }
     7477
    73487478        /* Lock both in {parent,child} order. */
    73497479        AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
     
    74677597        Assert(pTarget->m->pParent.isNull());
    74687598
    7469         /* associate the child with the parent */
    7470         pTarget->m->pParent = this;
    7471         m->llChildren.push_back(pTarget);
    7472 
    7473         /** @todo r=klaus neither target nor base() are locked,
    7474             * potential race! */
     7599        /* associate child with the parent, maximum depth was checked above */
     7600        pTarget->i_setParent(this);
     7601
    74757602        /* diffs for immutable media are auto-reset by default */
    7476         pTarget->m->autoReset = (i_getBase()->m->type == MediumType_Immutable);
     7603        bool fAutoReset;
     7604        {
     7605            ComObjPtr<Medium> pBase = i_getBase();
     7606            AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
     7607            fAutoReset = (pBase->m->type == MediumType_Immutable);
     7608        }
     7609        {
     7610            AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
     7611            pTarget->m->autoReset = fAutoReset;
     7612        }
    74777613
    74787614        /* register with mVirtualBox as the last step and move to
     
    75487684    try
    75497685    {
     7686        if (!task.mParentForTarget.isNull())
     7687            if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
     7688            {
     7689                AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
     7690                throw setError(VBOX_E_INVALID_OBJECT_STATE,
     7691                               tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
     7692                               task.mParentForTarget->m->strLocationFull.c_str());
     7693            }
     7694
    75507695        PVBOXHDD hdd;
    75517696        int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
     
    77027847            AssertComRC(rc2);
    77037848
    7704             /* then, reparent it and disconnect the deleted branch at
    7705              * both ends (chain->parent() is source's parent) */
     7849            /* then, reparent it and disconnect the deleted branch at both ends
     7850             * (chain->parent() is source's parent). Depth check above. */
    77067851            pTarget->i_deparent();
    7707             pTarget->m->pParent = task.mParentForTarget;
    7708             if (pTarget->m->pParent)
    7709             {
    7710                 pTarget->m->pParent->m->llChildren.push_back(pTarget);
     7852            pTarget->i_setParent(task.mParentForTarget);
     7853            if (task.mParentForTarget)
    77117854                i_deparent();
    7712             }
    77137855
    77147856            /* then, register again */
     
    77437885
    77447886                    pMedium->i_deparent();  // removes pMedium from source
     7887                    // no depth check, reduces depth
    77457888                    pMedium->i_setParent(pTarget);
    77467889                }
     
    78598002    try
    78608003    {
     8004        if (!pParent.isNull())
     8005            if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
     8006            {
     8007                AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
     8008                throw setError(VBOX_E_INVALID_OBJECT_STATE,
     8009                               tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
     8010                               pParent->m->strLocationFull.c_str());
     8011            }
     8012
    78618013        /* Lock all in {parent,child} order. The lock is also used as a
    78628014         * signal from the task initiator (which releases it only after
     
    79838135                }
    79848136
    7985                 /** @todo r=klaus target isn't locked, race getting the state */
     8137                /* target isn't locked, but no changing data is accessed */
    79868138                if (task.midxSrcImageSame == UINT32_MAX)
    79878139                {
     
    80538205        if (pParent)
    80548206        {
    8055             /* associate the clone with the parent and deassociate
    8056              * from VirtualBox */
    8057             pTarget->m->pParent = pParent;
    8058             pParent->m->llChildren.push_back(pTarget);
     8207            /* Associate the clone with the parent and deassociate
     8208             * from VirtualBox. Depth check above. */
     8209            pTarget->i_setParent(pParent);
    80598210
    80608211            /* register with mVirtualBox as the last step and move to
     
    86928843    try
    86938844    {
     8845        if (!pParent.isNull())
     8846            if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
     8847            {
     8848                AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
     8849                throw setError(VBOX_E_INVALID_OBJECT_STATE,
     8850                               tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
     8851                               pParent->m->strLocationFull.c_str());
     8852            }
     8853
    86948854        /* Lock all in {parent,child} order. The lock is also used as a
    86958855         * signal from the task initiator (which releases it only after
     
    87998959                }
    88008960
    8801                 /** @todo r=klaus target isn't locked, race getting the state */
    88028961                vrc = VDCopy(hdd,
    88038962                             VD_LAST_IMAGE,
     
    88489007        if (pParent)
    88499008        {
    8850             /* associate the imported medium with the parent and deassociate
    8851              * from VirtualBox */
    8852             m->pParent = pParent;
    8853             pParent->m->llChildren.push_back(this);
     9009            /* Associate the imported medium with the parent and deassociate
     9010             * from VirtualBox. Depth check above. */
     9011            i_setParent(pParent);
    88549012
    88559013            /* register with mVirtualBox as the last step and move to
  • trunk/src/VBox/Main/src-server/SnapshotImpl.cpp

    r54438 r54948  
    11/* $Id$ */
    22/** @file
    3  *
    43 * COM class implementation for Snapshot and SnapshotMachine in VBoxSVC.
    54 */
     
    175174    m->llChildren.clear();          // this unsets all the ComPtrs and probably calls delete
    176175
     176    // since there is no guarantee anyone holds a reference to us except the
     177    // list of children in our parent, make sure that the reference count
     178    // will not drop to 0 before we've declared ourselves as uninitialized,
     179    // otherwise there will be another uninit call which causes a self-deadlock
     180    // because this uninit isn't complete yet.
     181    ComObjPtr<Snapshot> pSnapshot(this);
    177182    if (m->pParent)
    178183        i_deparent();
     
    186191    delete m;
    187192    m = NULL;
     193
     194    autoUninitSpan.setSucceeded();
     195    // see above, now the refcount may reach 0
     196    pSnapshot.setNull();
    188197}
    189198
     
    725734
    726735/**
    727  * Internal implementation for Snapshot::saveSnapshot (below). Caller has
    728  * requested the snapshots tree (machine) lock.
    729  *
    730  * @param aNode
    731  * @param aAttrsOnly
     736 * Saves the settings attributes of one snapshot.
     737 *
     738 * @param data      Target for saving snapshot settings.
    732739 * @return
    733740 */
    734 HRESULT Snapshot::i_saveSnapshotImpl(settings::Snapshot &data, bool aAttrsOnly)
     741HRESULT Snapshot::i_saveSnapshotImplOne(settings::Snapshot &data) const
    735742{
    736743    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     
    740747    data.timestamp = m->timeStamp;
    741748    data.strDescription = m->strDescription;
    742 
    743     if (aAttrsOnly)
    744         return S_OK;
    745749
    746750    // state file (only if this snapshot is online)
     
    756760    if (FAILED(rc)) return rc;
    757761
    758     alock.release();
    759 
    760     data.llChildSnapshots.clear();
    761 
    762     if (m->llChildren.size())
    763     {
    764         for (SnapshotsList::const_iterator it = m->llChildren.begin();
    765              it != m->llChildren.end();
    766              ++it)
     762    return S_OK;
     763}
     764
     765/**
     766 * Internal implementation for Snapshot::saveSnapshot (below). Caller has
     767 * requested the snapshots tree (machine) lock.
     768 *
     769 * @param data      Target for saving snapshot settings.
     770 * @return
     771 */
     772HRESULT Snapshot::i_saveSnapshotImpl(settings::Snapshot &data) const
     773{
     774    HRESULT rc = i_saveSnapshotImplOne(data);
     775    if (FAILED(rc))
     776        return rc;
     777
     778    settings::SnapshotsList &llSettingsChildren = data.llChildSnapshots;
     779    for (SnapshotsList::const_iterator it = m->llChildren.begin();
     780         it != m->llChildren.end();
     781         ++it)
     782    {
     783        // Use the heap (indirectly through the list container) to reduce the
     784        // stack footprint, avoiding local settings objects on the stack which
     785        // need a lot of stack space. There can be VMs with deeply nested
     786        // snapshots. The stack can be quite small, especially with XPCOM.
     787        llSettingsChildren.push_back(settings::g_SnapshotEmpty);
     788        Snapshot *pSnap = *it;
     789        rc = pSnap->i_saveSnapshotImpl(llSettingsChildren.back());
     790        if (FAILED(rc))
    767791        {
    768            // Use the heap to reduce the stack footprint. Each recursion needs
    769            // over 1K, and there can be VMs with deeply nested snapshots. The
    770            // stack can be quite small, especially with XPCOM.
    771 
    772             settings::Snapshot *snap = new settings::Snapshot();
    773             rc = (*it)->i_saveSnapshotImpl(*snap, aAttrsOnly);
    774             if (FAILED(rc))
    775             {
    776                 delete snap;
    777                 return rc;
    778             }
    779             data.llChildSnapshots.push_back(*snap);
    780             delete snap;
     792            llSettingsChildren.pop_back();
     793            return rc;
    781794        }
    782795    }
     
    786799
    787800/**
    788  *  Saves the given snapshot and all its children (unless \a aAttrsOnly is true).
    789  *  It is assumed that the given node is empty (unless \a aAttrsOnly is true).
    790  *
    791  *  @param aNode        <Snapshot> node to save the snapshot to.
    792  *  @param aSnapshot    Snapshot to save.
    793  *  @param aAttrsOnly   If true, only update user-changeable attrs.
    794  */
    795 HRESULT Snapshot::i_saveSnapshot(settings::Snapshot &data, bool aAttrsOnly)
     801 * Saves the given snapshot and all its children.
     802 * It is assumed that the given node is empty.
     803 *
     804 * @param data      Target for saving snapshot settings.
     805 */
     806HRESULT Snapshot::i_saveSnapshot(settings::Snapshot &data) const
    796807{
    797808    // snapshots tree is protected by machine lock
    798809    AutoReadLock alock(m->pMachine COMMA_LOCKVAL_SRC_POS);
    799810
    800     return i_saveSnapshotImpl(data, aAttrsOnly);
     811    return i_saveSnapshotImpl(data);
    801812}
    802813
     
    804815 * Part of the cleanup engine of Machine::Unregister().
    805816 *
    806  * This recursively removes all medium attachments from the snapshot's machine
    807  * and returns the snapshot's saved state file name, if any, and then calls
    808  * uninit() on "this" itself.
    809  *
    810  * This recurses into children first, so the given MediaList receives child
    811  * media first before their parents. If the caller wants to close all media,
    812  * they should go thru the list from the beginning to the end because media
    813  * cannot be closed if they have children.
    814  *
    815  * This calls uninit() on itself, so the snapshots tree (beginning with a machine's pFirstSnapshot) becomes invalid after this.
    816  * It does not alter the main machine's snapshot pointers (pFirstSnapshot, pCurrentSnapshot).
     817 * This removes all medium attachments from the snapshot's machine and returns
     818 * the snapshot's saved state file name, if any, and then calls uninit() on
     819 * "this" itself.
    817820 *
    818821 * Caller must hold the machine write lock (which protects the snapshots tree!)
     
    824827 * @return
    825828 */
    826 HRESULT Snapshot::i_uninitRecursively(AutoWriteLock &writeLock,
    827                                       CleanupMode_T cleanupMode,
    828                                       MediaList &llMedia,
    829                                       std::list<Utf8Str> &llFilenames)
    830 {
    831     Assert(m->pMachine->isWriteLockOnCurrentThread());
    832 
    833     HRESULT rc = S_OK;
    834 
    835     // make a copy of the Guid for logging before we uninit ourselves
    836 #ifdef LOG_ENABLED
    837     Guid uuid = i_getId();
    838     Utf8Str name = i_getName();
    839     LogFlowThisFunc(("Entering for snapshot '%s' {%RTuuid}\n", name.c_str(), uuid.raw()));
    840 #endif
    841 
    842     // recurse into children first so that the child media appear on
    843     // the list first; this way caller can close the media from the
    844     // beginning to the end because parent media can't be closed if
    845     // they have children
    846 
    847     // make a copy of the children list since uninit() modifies it
    848     SnapshotsList llChildrenCopy(m->llChildren);
    849     for (SnapshotsList::iterator it = llChildrenCopy.begin();
    850          it != llChildrenCopy.end();
    851          ++it)
    852     {
    853         Snapshot *pChild = *it;
    854         rc = pChild->i_uninitRecursively(writeLock, cleanupMode, llMedia, llFilenames);
    855         if (FAILED(rc))
    856             return rc;
    857     }
    858 
     829HRESULT Snapshot::i_uninitOne(AutoWriteLock &writeLock,
     830                              CleanupMode_T cleanupMode,
     831                              MediaList &llMedia,
     832                              std::list<Utf8Str> &llFilenames)
     833{
    859834    // now call detachAllMedia on the snapshot machine
    860     rc = m->pMachine->i_detachAllMedia(writeLock,
    861                                        this /* pSnapshot */,
    862                                        cleanupMode,
    863                                        llMedia);
     835    HRESULT rc = m->pMachine->i_detachAllMedia(writeLock,
     836                                               this /* pSnapshot */,
     837                                               cleanupMode,
     838                                               llMedia);
    864839    if (FAILED(rc))
    865840        return rc;
     
    884859    }
    885860
    886     this->i_beginSnapshotDelete();
    887     this->uninit();
    888 
     861    i_beginSnapshotDelete();
     862    uninit();
     863
     864    return S_OK;
     865}
     866
     867/**
     868 * Part of the cleanup engine of Machine::Unregister().
     869 *
     870 * This recursively removes all medium attachments from the snapshot's machine
     871 * and returns the snapshot's saved state file name, if any, and then calls
     872 * uninit() on "this" itself.
     873 *
     874 * This recurses into children first, so the given MediaList receives child
     875 * media first before their parents. If the caller wants to close all media,
     876 * they should go thru the list from the beginning to the end because media
     877 * cannot be closed if they have children.
     878 *
     879 * This calls uninit() on itself, so the snapshots tree (beginning with a machine's pFirstSnapshot) becomes invalid after this.
     880 * It does not alter the main machine's snapshot pointers (pFirstSnapshot, pCurrentSnapshot).
     881 *
     882 * Caller must hold the machine write lock (which protects the snapshots tree!)
     883 *
     884 * @param writeLock Machine write lock, which can get released temporarily here.
     885 * @param cleanupMode Cleanup mode; see Machine::detachAllMedia().
     886 * @param llMedia List of media returned to caller, depending on cleanupMode.
     887 * @param llFilenames
     888 * @return
     889 */
     890HRESULT Snapshot::i_uninitRecursively(AutoWriteLock &writeLock,
     891                                      CleanupMode_T cleanupMode,
     892                                      MediaList &llMedia,
     893                                      std::list<Utf8Str> &llFilenames)
     894{
     895    Assert(m->pMachine->isWriteLockOnCurrentThread());
     896
     897    HRESULT rc = S_OK;
     898
     899    // make a copy of the Guid for logging before we uninit ourselves
    889900#ifdef LOG_ENABLED
    890     LogFlowThisFunc(("Leaving for snapshot '%s' {%RTuuid}\n", name.c_str(), uuid.raw()));
     901    Guid uuid = i_getId();
     902    Utf8Str name = i_getName();
     903    LogFlowThisFunc(("Entering for snapshot '%s' {%RTuuid}\n", name.c_str(), uuid.raw()));
    891904#endif
    892905
    893     return S_OK;
     906    // Recurse into children first so that the child media appear on the list
     907    // first; this way caller can close the media from the beginning to the end
     908    // because parent media can't be closed if they have children and
     909    // additionally it postpones the uninit() call until we no longer need
     910    // anything from the list. Oh, and remember that the child removes itself
     911    // from the list, so keep the iterator at the beginning.
     912    for (SnapshotsList::const_iterator it = m->llChildren.begin();
     913         it != m->llChildren.end();
     914         it = m->llChildren.begin())
     915    {
     916        Snapshot *pChild = *it;
     917        rc = pChild->i_uninitRecursively(writeLock, cleanupMode, llMedia, llFilenames);
     918        if (FAILED(rc))
     919            break;
     920    }
     921
     922    if (SUCCEEDED(rc))
     923        rc = i_uninitOne(writeLock, cleanupMode, llMedia, llFilenames);
     924
     925#ifdef LOG_ENABLED
     926    LogFlowThisFunc(("Leaving for snapshot '%s' {%RTuuid}: %Rhrc\n", name.c_str(), uuid.raw(), rc));
     927#endif
     928
     929    return rc;
    894930}
    895931
  • trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp

    r54438 r54948  
    650650 */
    651651HRESULT VirtualBox::initMedia(const Guid &uuidRegistry,
    652                               const settings::MediaRegistry mediaRegistry,
     652                              const settings::MediaRegistry &mediaRegistry,
    653653                              const Utf8Str &strMachineFolder)
    654654{
     
    674674                                 uuidRegistry,
    675675                                 xmlHD,         // XML data; this recurses to processes the children
    676                                  strMachineFolder);
     676                                 strMachineFolder,
     677                                 treeLock);
    677678        if (FAILED(rc)) return rc;
    678679
     
    694695                              uuidRegistry,
    695696                              xmlDvd,
    696                               strMachineFolder);
     697                              strMachineFolder,
     698                              treeLock);
    697699        if (FAILED(rc)) return rc;
    698700
     
    714716                              uuidRegistry,
    715717                              xmlFloppy,
    716                               strMachineFolder);
     718                              strMachineFolder,
     719                              treeLock);
    717720        if (FAILED(rc)) return rc;
    718721
     
    40144017            if (pMedium->i_isInRegistry(uuidRegistry))
    40154018            {
    4016                 settings::Medium med;
    4017                 rc = pMedium->i_saveSettings(med, strMachineFolder);     // this recurses into child hard disks
    4018                 if (FAILED(rc)) throw rc;
    4019                 llTarget.push_back(med);
     4019                llTarget.push_back(settings::g_MediumEmpty);
     4020                rc = pMedium->i_saveSettings(llTarget.back(), strMachineFolder);     // this recurses into child hard disks
     4021                if (FAILED(rc))
     4022                {
     4023                    llTarget.pop_back();
     4024                    throw rc;
     4025                }
    40204026            }
    40214027        }
     
    44824488            AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
    44834489
    4484             if (pMedium->i_removeRegistry(id, true /* fRecurse */))
     4490            if (pMedium->i_removeRegistryRecursive(id))
    44854491            {
    44864492                // machine ID was found in base medium's registry list:
     
    44914497                {
    44924498                    // 2) better registry found: then use that
    4493                     pMedium->i_addRegistry(*puuidBetter, true /* fRecurse */);
     4499                    pMedium->i_addRegistryRecursive(*puuidBetter);
    44944500                    // 3) and make sure the registry is saved below
    44954501                    mlock.release();
     
    44974503                    i_markRegistryModified(*puuidBetter);
    44984504                    tlock.acquire();
    4499                     mlock.release();
     4505                    mlock.acquire();
    45004506                }
    45014507            }
     
    45334539            if (SUCCEEDED(machineCaller.rc()))
    45344540                ASMAtomicIncU64(&pMachine->uRegistryNeedsSaving);
     4541        }
     4542    }
     4543}
     4544
     4545/**
     4546 * Marks the registry for @a uuid as unmodified, so that it's not saved in
     4547 * a later call to saveModifiedRegistries().
     4548 *
     4549 * @param uuid
     4550 */
     4551void VirtualBox::i_unmarkRegistryModified(const Guid &uuid)
     4552{
     4553    uint64_t uOld;
     4554    if (uuid == i_getGlobalRegistryId())
     4555    {
     4556        for (;;)
     4557        {
     4558            uOld = ASMAtomicReadU64(&m->uRegistryNeedsSaving);
     4559            if (!uOld)
     4560                break;
     4561            if (ASMAtomicCmpXchgU64(&m->uRegistryNeedsSaving, 0, uOld))
     4562                break;
     4563            ASMNopPause();
     4564        }
     4565    }
     4566    else
     4567    {
     4568        ComObjPtr<Machine> pMachine;
     4569        HRESULT rc = i_findMachine(uuid,
     4570                                   false /* fPermitInaccessible */,
     4571                                   false /* aSetError */,
     4572                                   &pMachine);
     4573        if (SUCCEEDED(rc))
     4574        {
     4575            AutoCaller machineCaller(pMachine);
     4576            if (SUCCEEDED(machineCaller.rc()))
     4577            {
     4578                for (;;)
     4579                {
     4580                    uOld = ASMAtomicReadU64(&pMachine->uRegistryNeedsSaving);
     4581                    if (!uOld)
     4582                        break;
     4583                    if (ASMAtomicCmpXchgU64(&pMachine->uRegistryNeedsSaving, 0, uOld))
     4584                        break;
     4585                    ASMNopPause();
     4586                }
     4587            }
    45354588        }
    45364589    }
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