VirtualBox

Changeset 94598 in vbox


Ignore:
Timestamp:
Apr 13, 2022 9:50:00 PM (3 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
150959
Message:

Main/Machine+Medium+Snapshot+VirtualBox: Recursion elimination to save stack space. Caused trouble with the current settings limits already in ASAN builds, now much higher limits would be possible, but that's not urgent. Also fix the behavior of forgetting medium objects when unregistering VMs (was previously not doing what the API documentation said in the CleanupMode_UnregisterOnly case). bugref:7717

Settings.cpp: Recursion elimination and make the handling of settings reading and writing more similar.

ValidationKit/tests/api/tdTreeDepth1.py: Improve testcase. Make sure that VM unregistering does the right thing with the associated medium objects.

Location:
trunk
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/settings.h

    r93115 r94598  
    5959 * XPCOM has a relatively low stack size for its workers, and we have
    6060 * to avoid crashes due to exceeding the limit both on reading and
    61  * writing config files.
     61 * writing config files. The bottleneck is in libxml2.
     62 * Data point: a release and asan build could both handle 3800 on Debian 10.
    6263 */
    6364#define SETTINGS_MEDIUM_DEPTH_MAX 300
     
    6869 * to avoid crashes due to exceeding the limit both on reading and
    6970 * writing config files. The bottleneck is reading config files with
    70  * deep snapshot nesting, as libxml2 needs quite some stack space,
    71  * so with the current stack size the margin isn't big.
     71 * deep snapshot nesting, as libxml2 needs quite some stack space.
     72 * Data point: a release and asan build could both handle 1300 on Debian 10.
    7273 */
    7374#define SETTINGS_SNAPSHOT_DEPTH_MAX 250
     
    257258                              USBDeviceFiltersList &ll);
    258259    void readMediumOne(MediaType t, const xml::ElementNode &elmMedium, Medium &med);
    259     void readMedium(MediaType t, uint32_t depth, const xml::ElementNode &elmMedium, Medium &med);
     260    void readMedium(MediaType t, const xml::ElementNode &elmMedium, Medium &med);
    260261    void readMediaRegistry(const xml::ElementNode &elmMediaRegistry, MediaRegistry &mr);
    261262    void readNATForwardRulesMap(const xml::ElementNode  &elmParent, NATRulesMap &mapRules);
     
    271272                               bool fHostMode);
    272273    void buildMedium(MediaType t,
    273                      uint32_t depth,
    274274                     xml::ElementNode &elmMedium,
    275                      const Medium &mdm);
     275                     const Medium &med);
    276276    void buildMediaRegistry(xml::ElementNode &elmParent,
    277277                            const MediaRegistry &mr);
     
    14091409    void readStorageControllers(const xml::ElementNode &elmStorageControllers, Storage &strg);
    14101410    void readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware, Storage &strg);
    1411     void readTeleporter(const xml::ElementNode *pElmTeleporter, MachineUserData *pUserData);
    1412     void readDebugging(const xml::ElementNode *pElmDbg, Debugging *pDbg);
    1413     void readAutostart(const xml::ElementNode *pElmAutostart, Autostart *pAutostart);
    1414     void readGroups(const xml::ElementNode *elmGroups, StringsList *pllGroups);
    1415     bool readSnapshot(const com::Guid &curSnapshotUuid, uint32_t depth, const xml::ElementNode &elmSnapshot, Snapshot &snap);
     1411    void readTeleporter(const xml::ElementNode &elmTeleporter, MachineUserData &userData);
     1412    void readDebugging(const xml::ElementNode &elmDbg, Debugging &dbg);
     1413    void readAutostart(const xml::ElementNode &elmAutostart, Autostart &autostrt);
     1414    void readGroups(const xml::ElementNode &elmGroups, StringsList &llGroups);
     1415    bool readSnapshot(const com::Guid &curSnapshotUuid, const xml::ElementNode &elmSnapshot, Snapshot &snap);
    14161416    void convertOldOSType_pre1_5(com::Utf8Str &str);
    14171417    void readMachine(const xml::ElementNode &elmMachine);
     
    14231423                                    bool fSkipRemovableMedia,
    14241424                                    std::list<xml::ElementNode*> *pllElementsWithUuidAttributes);
    1425     void buildDebuggingXML(xml::ElementNode *pElmParent, const Debugging *pDbg);
    1426     void buildAutostartXML(xml::ElementNode *pElmParent, const Autostart *pAutostart);
    1427     void buildGroupsXML(xml::ElementNode *pElmParent, const StringsList *pllGroups);
    1428     void buildSnapshotXML(uint32_t depth, xml::ElementNode &elmParent, const Snapshot &snap);
     1425    void buildDebuggingXML(xml::ElementNode &elmParent, const Debugging &dbg);
     1426    void buildAutostartXML(xml::ElementNode &elmParent, const Autostart &autostrt);
     1427    void buildGroupsXML(xml::ElementNode &elmParent, const StringsList &llGroups);
     1428    void buildSnapshotXML(xml::ElementNode &elmParent, const Snapshot &snap);
    14291429
    14301430    void bumpSettingsVersionIfNeeded();
  • trunk/src/VBox/Main/include/MachineImpl.h

    r93891 r94598  
    2424#include "AuthLibrary.h"
    2525#include "VirtualBoxBase.h"
    26 #include "SnapshotImpl.h"
    2726#include "ProgressImpl.h"
    2827#include "VRDEServerImpl.h"
     
    645644                                          const Guid *puuidRegistry);
    646645    HRESULT i_loadSnapshot(const settings::Snapshot &data,
    647                            const Guid &aCurSnapshotId,
    648                            Snapshot *aParentSnapshot);
     646                           const Guid &aCurSnapshotId);
    649647    HRESULT i_loadHardware(const Guid *puuidRegistry,
    650648                           const Guid *puuidSnapshot,
  • trunk/src/VBox/Main/include/MediumImpl.h

    r93115 r94598  
    7777                    DeviceType_T aDeviceType,
    7878                    const Guid &uuidMachineRegistry,
    79                     const settings::Medium &data,
    80                     const Utf8Str &strMachineFolder);
    81     HRESULT initFromSettings(VirtualBox *aVirtualBox,
    82                              Medium *aParent,
    83                              DeviceType_T aDeviceType,
    84                              const Guid &uuidMachineRegistry,
    85                              const settings::Medium &data,
    86                              const Utf8Str &strMachineFolder,
    87                              AutoWriteLock &mediaTreeLock,
    88                              ComObjPtr<Medium> *ppRegistered);
     79                    const Utf8Str &strMachineFolder,
     80                    const settings::Medium &data);
     81    static HRESULT initFromSettings(VirtualBox *aVirtualBox,
     82                                    DeviceType_T aDeviceType,
     83                                    const Guid &uuidMachineRegistry,
     84                                    const Utf8Str &strMachineFolder,
     85                                    const settings::Medium &data,
     86                                    AutoWriteLock &mediaTreeLock,
     87                                    std::list<std::pair<Guid, DeviceType_T> > &uIdsForNotify);
    8988
    9089    // initializer for host floppy/DVD
     
    122121    bool i_addRegistryNoCallerCheck(const Guid &id);
    123122    /* handles caller/locking itself, caller is responsible for tree lock */
    124     bool i_addRegistryRecursive(const Guid &id);
     123    bool i_addRegistryAll(const Guid &id);
    125124    /* handles caller/locking itself */
    126125    bool i_removeRegistry(const Guid& id);
    127126    /* handles caller/locking itself, caller is responsible for tree lock */
    128     bool i_removeRegistryRecursive(const Guid& id);
     127    bool i_removeRegistryAll(const Guid& id);
    129128    bool i_isInRegistry(const Guid& id);
    130129    bool i_getFirstRegistryMachineId(Guid &uuid) const;
     
    140139
    141140    const Guid* i_getFirstMachineBackrefId() const;
    142     const Guid* i_getAnyMachineBackref() const;
     141    const Guid* i_getAnyMachineBackref(const Guid &aId) const;
    143142    const Guid* i_getFirstMachineBackrefSnapshotId() const;
    144143    size_t i_getMachineBackRefCount() const;
  • trunk/src/VBox/Main/include/SnapshotImpl.h

    r93115 r94598  
    7474    ULONG i_getChildrenCount();
    7575    ULONG i_getAllChildrenCount();
    76     ULONG i_getAllChildrenCountImpl();
    7776
    7877    const ComObjPtr<SnapshotMachine>& i_getSnapshotMachine() const;
     
    9897                                const Utf8Str &strNewPath);
    9998
     99    HRESULT i_saveSnapshotOne(settings::Snapshot &data) const;
    100100    HRESULT i_saveSnapshot(settings::Snapshot &data) const;
    101     HRESULT i_saveSnapshotImpl(settings::Snapshot &data) const;
    102     HRESULT i_saveSnapshotImplOne(settings::Snapshot &data) const;
    103101
    104     HRESULT i_uninitOne(AutoWriteLock &writeLock,
     102    HRESULT i_uninitAll(AutoWriteLock &writeLock,
    105103                        CleanupMode_T cleanupMode,
    106104                        MediaList &llMedia,
    107105                        std::list<Utf8Str> &llFilenames);
    108     HRESULT i_uninitRecursively(AutoWriteLock &writeLock,
    109                                 CleanupMode_T cleanupMode,
    110                                 MediaList &llMedia,
    111                                 std::list<Utf8Str> &llFilenames);
    112106
    113107
  • trunk/src/VBox/Main/include/VirtualBoxImpl.h

    r94249 r94598  
    271271                             AutoWriteLock &mediaTreeLock, bool fCalledFromMediumInit = false);
    272272    HRESULT i_unregisterMedium(Medium *pMedium);
    273     void i_pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium);
    274273    HRESULT i_unregisterMachineMedia(const Guid &id);
    275     HRESULT i_unregisterMachine(Machine *pMachine, const Guid &id);
     274    HRESULT i_unregisterMachine(Machine *pMachine, CleanupMode_T aCleanupMode, const Guid &id);
    276275    void i_rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
    277276                                             const Utf8Str &strNewConfigDir);
  • trunk/src/VBox/Main/src-server/MachineImpl.cpp

    r94470 r94598  
    2929#include "VirtualBoxImpl.h"
    3030#include "MachineImpl.h"
     31#include "SnapshotImpl.h"
    3132#include "ClientToken.h"
    3233#include "ProgressImpl.h"
     
    49494950        uninit();
    49504951
    4951         mParent->i_unregisterMachine(this, id);
     4952        mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
    49524953            // calls VirtualBox::i_saveSettings()
    49534954
     
    49864987    if (mData->mFirstSnapshot)
    49874988    {
    4988         // add the media from the medium attachments of the snapshots to llMedia
    4989         // as well, after the "main" machine media; Snapshot::uninitRecursively()
    4990         // calls Machine::detachAllMedia() for the snapshot machine, recursing
    4991         // into the children first
     4989        // add the media from the medium attachments of the snapshots to
     4990        // llMedia as well, after the "main" machine media;
     4991        // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
     4992        // snapshot machine, depth first.
    49924993
    49934994        // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
     
    50015002
    50025003        // GO!
    5003         pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
     5004        pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
    50045005
    50055006        mData->mMachineState = oldState;
     
    50345035        (*it).queryInterfaceTo(aMedia[i].asOutParam());
    50355036
    5036     mParent->i_unregisterMachine(this, id);
     5037    mParent->i_unregisterMachine(this, aCleanupMode, id);
    50375038            // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
     5039
     5040    autoCaller.release();
     5041    uninit();
    50385042
    50395043    return S_OK;
     
    84028406    if (!i_isSessionMachine() && !i_isSnapshotMachine())
    84038407    {
    8404         // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
     8408        // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
    84058409        if (mData->mFirstSnapshot)
    84068410        {
    8407             // snapshots tree is protected by machine write lock; strictly
    8408             // this isn't necessary here since we're deleting the entire
    8409             // machine, but otherwise we assert in Snapshot::uninit()
     8411            // Snapshots tree is protected by machine write lock.
     8412            // Otherwise we assert in Snapshot::uninit()
    84108413            AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    84118414            mData->mFirstSnapshot->uninit();
     
    86538656        // there must be only one root snapshot
    86548657        Assert(cRootSnapshots == 1);
    8655 
    86568658        const settings::Snapshot &snap = config.llFirstSnapshot.front();
    86578659
    86588660        rc = i_loadSnapshot(snap,
    8659                             config.uuidCurrentSnapshot,
    8660                             NULL);        // no parent == first snapshot
     8661                            config.uuidCurrentSnapshot);
    86618662        if (FAILED(rc)) return rc;
    86628663    }
     
    86998700
    87008701/**
    8701  *  Recursively loads all snapshots starting from the given.
     8702 *  Loads all snapshots starting from the given settings.
    87028703 *
    87038704 *  @param data             snapshot settings.
    87048705 *  @param aCurSnapshotId   Current snapshot ID from the settings file.
    8705  *  @param aParentSnapshot  Parent snapshot.
    87068706 */
    87078707HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
    8708                                 const Guid &aCurSnapshotId,
    8709                                 Snapshot *aParentSnapshot)
     8708                                const Guid &aCurSnapshotId)
    87108709{
    87118710    AssertReturn(!i_isSnapshotMachine(), E_FAIL);
     
    87148713    HRESULT rc = S_OK;
    87158714
    8716     Utf8Str strStateFile;
    8717     if (!data.strStateFile.isEmpty())
    8718     {
    8719         /* optional */
    8720         strStateFile = data.strStateFile;
    8721         int vrc = i_calculateFullPath(strStateFile, strStateFile);
    8722         if (RT_FAILURE(vrc))
    8723             return setErrorBoth(E_FAIL, vrc,
    8724                                 tr("Invalid saved state file path '%s' (%Rrc)"),
    8725                                 strStateFile.c_str(),
    8726                                 vrc);
    8727     }
    8728 
    8729     /* create a snapshot machine object */
    8730     ComObjPtr<SnapshotMachine> pSnapshotMachine;
    8731     pSnapshotMachine.createObject();
    8732     rc = pSnapshotMachine->initFromSettings(this,
    8733                                             data.hardware,
    8734                                             &data.debugging,
    8735                                             &data.autostart,
    8736                                             data.uuid.ref(),
    8737                                             strStateFile);
    8738     if (FAILED(rc)) return rc;
    8739 
    8740     /* create a snapshot object */
    8741     ComObjPtr<Snapshot> pSnapshot;
    8742     pSnapshot.createObject();
    8743     /* initialize the snapshot */
    8744     rc = pSnapshot->init(mParent, // VirtualBox object
    8745                          data.uuid,
    8746                          data.strName,
    8747                          data.strDescription,
    8748                          data.timestamp,
    8749                          pSnapshotMachine,
    8750                          aParentSnapshot);
    8751     if (FAILED(rc)) return rc;
    8752 
    8753     /* memorize the first snapshot if necessary */
    8754     if (!mData->mFirstSnapshot)
    8755         mData->mFirstSnapshot = pSnapshot;
    8756 
    8757     /* memorize the current snapshot when appropriate */
    8758     if (    !mData->mCurrentSnapshot
    8759          && pSnapshot->i_getId() == aCurSnapshotId
    8760        )
    8761         mData->mCurrentSnapshot = pSnapshot;
    8762 
    8763     // now create the children
    8764     for (settings::SnapshotsList::const_iterator
    8765          it = data.llChildSnapshots.begin();
    8766          it != data.llChildSnapshots.end();
    8767          ++it)
    8768     {
    8769         const settings::Snapshot &childData = *it;
    8770         // recurse
    8771         rc = i_loadSnapshot(childData,
    8772                             aCurSnapshotId,
    8773                             pSnapshot);       // parent = the one we created above
    8774         if (FAILED(rc)) return rc;
     8715    std::list<const settings::Snapshot *> llSettingsTodo;
     8716    llSettingsTodo.push_back(&data);
     8717    std::list<Snapshot *> llParentsTodo;
     8718    llParentsTodo.push_back(NULL);
     8719
     8720    while (llSettingsTodo.size() > 0)
     8721    {
     8722        const settings::Snapshot *current = llSettingsTodo.front();
     8723        llSettingsTodo.pop_front();
     8724        Snapshot *pParent = llParentsTodo.front();
     8725        llParentsTodo.pop_front();
     8726
     8727        Utf8Str strStateFile;
     8728        if (!current->strStateFile.isEmpty())
     8729        {
     8730            /* optional */
     8731            strStateFile = current->strStateFile;
     8732            int vrc = i_calculateFullPath(strStateFile, strStateFile);
     8733            if (RT_FAILURE(vrc))
     8734            {
     8735                setErrorBoth(E_FAIL, vrc,
     8736                             tr("Invalid saved state file path '%s' (%Rrc)"),
     8737                             strStateFile.c_str(), vrc);
     8738            }
     8739        }
     8740
     8741        /* create a snapshot machine object */
     8742        ComObjPtr<SnapshotMachine> pSnapshotMachine;
     8743        pSnapshotMachine.createObject();
     8744        rc = pSnapshotMachine->initFromSettings(this,
     8745                                                current->hardware,
     8746                                                &current->debugging,
     8747                                                &current->autostart,
     8748                                                current->uuid.ref(),
     8749                                                strStateFile);
     8750        if (FAILED(rc)) break;
     8751
     8752        /* create a snapshot object */
     8753        ComObjPtr<Snapshot> pSnapshot;
     8754        pSnapshot.createObject();
     8755        /* initialize the snapshot */
     8756        rc = pSnapshot->init(mParent, // VirtualBox object
     8757                             current->uuid,
     8758                             current->strName,
     8759                             current->strDescription,
     8760                             current->timestamp,
     8761                             pSnapshotMachine,
     8762                             pParent);
     8763        if (FAILED(rc)) break;
     8764
     8765        /* memorize the first snapshot if necessary */
     8766        if (!mData->mFirstSnapshot)
     8767        {
     8768            Assert(pParent == NULL);
     8769            mData->mFirstSnapshot = pSnapshot;
     8770        }
     8771
     8772        /* memorize the current snapshot when appropriate */
     8773        if (    !mData->mCurrentSnapshot
     8774             && pSnapshot->i_getId() == aCurSnapshotId
     8775           )
     8776            mData->mCurrentSnapshot = pSnapshot;
     8777
     8778        /* create all children */
     8779        std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
     8780        std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
     8781        for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
     8782        {
     8783            llSettingsTodo.push_back(&*it);
     8784            llParentsTodo.push_back(pSnapshot);
     8785        }
    87758786    }
    87768787
     
    1065410665    if (pMedium != pBase)
    1065510666    {
    10656         /* Tree lock needed by Medium::addRegistry when recursing. */
     10667        /* Tree lock needed by Medium::addRegistryAll. */
    1065710668        AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    10658         if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
     10669        if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
    1065910670        {
    1066010671            treeLock.release();
     
    1066210673            treeLock.acquire();
    1066310674        }
    10664         if (pBase->i_addRegistryRecursive(uuid))
     10675        if (pBase->i_addRegistryAll(uuid))
    1066510676        {
    1066610677            treeLock.release();
  • trunk/src/VBox/Main/src-server/MachineImplMoveVM.cpp

    r93115 r94598  
    2626
    2727#include "MachineImplMoveVM.h"
     28#include "SnapshotImpl.h"
    2829#include "MediumFormatImpl.h"
    2930#include "VirtualBoxImpl.h"
  • trunk/src/VBox/Main/src-server/MediumImpl.cpp

    r93115 r94598  
    12131213                        DeviceType_T aDeviceType,
    12141214                        const Guid &uuidMachineRegistry,
    1215                         const settings::Medium &data,
    1216                         const Utf8Str &strMachineFolder)
     1215                        const Utf8Str &strMachineFolder,
     1216                        const settings::Medium &data)
    12171217{
    12181218    HRESULT rc;
     
    13561356 *
    13571357 * @param aVirtualBox   VirtualBox object.
    1358  * @param aParent       Parent medium disk or NULL for a root (base) medium.
    13591358 * @param aDeviceType   Device type of the medium.
    13601359 * @param uuidMachineRegistry The registry to which this medium should be added
    13611360 *                      (global registry UUID or machine UUID).
    1362  * @param data          Configuration settings.
    13631361 * @param strMachineFolder The machine folder with which to resolve relative
    13641362 *                      paths; if empty, then we use the VirtualBox home directory
     1363 * @param data          Configuration settings.
    13651364 * @param mediaTreeLock Autolock.
    1366  * @param ppRegistered  Where to return the registered Medium object.  This is
    1367  *                      for handling the case where the medium object we're
    1368  *                      constructing from settings already has a registered
    1369  *                      instance that should be used instead of this one.
    1370  *
    1371  * @note Locks the medium tree for writing.
     1365 * @param uIdsForNotify List to be updated with newly registered media.
     1366 *
     1367 * @note Assumes that the medium tree lock is held for writing. May release
     1368 * and lock it again. At the end it is always held.
    13721369 */
     1370/* static */
    13731371HRESULT Medium::initFromSettings(VirtualBox *aVirtualBox,
    1374                                  Medium *aParent,
    13751372                                 DeviceType_T aDeviceType,
    13761373                                 const Guid &uuidMachineRegistry,
     1374                                 const Utf8Str &strMachineFolder,
    13771375                                 const settings::Medium &data,
    1378                                  const Utf8Str &strMachineFolder,
    13791376                                 AutoWriteLock &mediaTreeLock,
    1380                                  ComObjPtr<Medium> *ppRegistered)
    1381 {
    1382     using namespace settings;
    1383 
     1377                                 std::list<std::pair<Guid, DeviceType_T> > &uIdsForNotify)
     1378{
    13841379    Assert(aVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    13851380    AssertReturn(aVirtualBox, E_INVALIDARG);
    13861381
    1387     /*
    1388      * Enclose the state transition NotReady->InInit->Ready.
    1389      *
    1390      * Note! This has to be scoped so that we can temporarily drop the
    1391      *       mediaTreeLock ownership on failure.
    1392      */
    1393     HRESULT rc;
    1394     bool    fRetakeLock = false;
    1395     {
    1396         AutoInitSpan autoInitSpan(this);
    1397         AssertReturn(autoInitSpan.isOk(), E_FAIL);
    1398 
    1399         unconst(m->pVirtualBox) = aVirtualBox;
    1400         ComObjPtr<Medium> pActualThis = this;
    1401 
    1402         // Do not inline this method call, as the purpose of having this separate
    1403         // is to save on stack size. Less local variables are the key for reaching
    1404         // deep recursion levels with small stack (XPCOM/g++ without optimization).
    1405         rc = initOne(aParent, aDeviceType, uuidMachineRegistry, data, strMachineFolder);
    1406         if (SUCCEEDED(rc))
    1407         {
    1408             /* In order to avoid duplicate instances of the medium object, we need
    1409                to register the parent before loading any children.  */
    1410             rc = m->pVirtualBox->i_registerMedium(pActualThis, &pActualThis, mediaTreeLock, true /*fCalledFromMediumInit*/);
    1411             if (SUCCEEDED(rc))
    1412             {
    1413                 /* Don't call Medium::i_queryInfo for registered media to prevent the calling
    1414                  * thread (i.e. the VirtualBox server startup thread) from an unexpected
    1415                  * freeze but mark it as initially inaccessible instead. The vital UUID,
    1416                  * location and format properties are read from the registry file above; to
    1417                  * get the actual state and the rest of the data, the user will have to call
    1418                  * COMGETTER(State). */
    1419 
    1420                 /* load all children */
    1421                 for (settings::MediaList::const_iterator it = data.llChildren.begin();
    1422                      it != data.llChildren.end();
    1423                      ++it)
    1424                 {
    1425                     const settings::Medium &med = *it;
    1426 
    1427                     ComObjPtr<Medium> pMedium;
    1428                     rc = pMedium.createObject();
    1429                     if (FAILED(rc)) break;
    1430 
    1431                     rc = pMedium->initFromSettings(aVirtualBox,
    1432                                                    pActualThis,       // parent
    1433                                                    aDeviceType,
    1434                                                    uuidMachineRegistry,
    1435                                                    med,               // child data
    1436                                                    strMachineFolder,
    1437                                                    mediaTreeLock,
    1438                                                    NULL /*ppRegistered - must _not_ be &pMedium*/);
    1439                     if (FAILED(rc)) break;
    1440                 }
    1441             }
    1442         }
    1443 
    1444         /* Confirm a successful initialization when it's the case.  Do not confirm duplicates. */
    1445         if (SUCCEEDED(rc) && (Medium *)pActualThis == this)
    1446             autoInitSpan.setSucceeded();
    1447         /* Otherwise we have release the media tree lock so that Medium::uninit()
    1448            doesn't freak out when it is called by AutoInitSpan::~AutoInitSpan().
    1449            In the case of duplicates, the uninit will i_deparent this object. */
    1450         else
    1451         {
    1452             /** @todo Non-duplicate: Deregister? What about child load errors, should they
    1453              *        affect the parents? */
    1454             mediaTreeLock.release();
    1455             fRetakeLock = true;
    1456         }
    1457 
    1458         /* Must be done after the mediaTreeLock.release above. */
    1459         if (ppRegistered)
    1460             *ppRegistered = pActualThis;
    1461     }
    1462 
    1463     if (fRetakeLock)
     1382    HRESULT rc = S_OK;
     1383
     1384    MediaList llMediaTocleanup;
     1385
     1386    std::list<const settings::Medium *> llSettingsTodo;
     1387    llSettingsTodo.push_back(&data);
     1388    MediaList llParentsTodo;
     1389    llParentsTodo.push_back(NULL);
     1390
     1391    while (llSettingsTodo.size() > 0)
     1392    {
     1393        const settings::Medium *current = llSettingsTodo.front();
     1394        llSettingsTodo.pop_front();
     1395        ComObjPtr<Medium> pParent = llParentsTodo.front();
     1396        llParentsTodo.pop_front();
     1397
     1398        bool fReleasedMediaTreeLock = false;
     1399        ComObjPtr<Medium> pMedium;
     1400        rc = pMedium.createObject();
     1401        if (FAILED(rc))
     1402            break;
     1403        ComObjPtr<Medium> pActualMedium(pMedium);
     1404
     1405        {
     1406            AutoInitSpan autoInitSpan(pMedium);
     1407            AssertBreakStmt(autoInitSpan.isOk(), rc = E_FAIL);
     1408
     1409            unconst(pMedium->m->pVirtualBox) = aVirtualBox;
     1410            rc = pMedium->initOne(pParent, aDeviceType, uuidMachineRegistry, strMachineFolder, *current);
     1411            if (FAILED(rc))
     1412                break;
     1413            rc = aVirtualBox->i_registerMedium(pActualMedium, &pActualMedium, mediaTreeLock, true /*fCalledFromMediumInit*/);
     1414            if (FAILED(rc))
     1415                break;
     1416
     1417            if (pActualMedium == pMedium)
     1418            {
     1419                /* It is a truly new medium, remember details for cleanup. */
     1420                autoInitSpan.setSucceeded();
     1421                llMediaTocleanup.push_front(pMedium);
     1422            }
     1423            else
     1424            {
     1425                /* Since the newly created medium was replaced by an already
     1426                 * known one when merging medium trees, we can immediately mark
     1427                 * it as failed. */
     1428                autoInitSpan.setFailed();
     1429                mediaTreeLock.release();
     1430                fReleasedMediaTreeLock = true;
     1431            }
     1432        }
     1433        if (fReleasedMediaTreeLock)
     1434        {
     1435            /* With the InitSpan out of the way it's safe to let the refcount
     1436             * drop to 0 without causing uninit trouble. */
     1437            pMedium.setNull();
     1438            mediaTreeLock.acquire();
     1439        }
     1440
     1441        /* create all children */
     1442        std::list<settings::Medium>::const_iterator itBegin = current->llChildren.begin();
     1443        std::list<settings::Medium>::const_iterator itEnd = current->llChildren.end();
     1444        for (std::list<settings::Medium>::const_iterator it = itBegin; it != itEnd; ++it)
     1445        {
     1446            llSettingsTodo.push_back(&*it);
     1447            llParentsTodo.push_back(pActualMedium);
     1448        }
     1449    }
     1450
     1451    if (SUCCEEDED(rc))
     1452    {
     1453        /* Check for consistency. */
     1454        Assert(llSettingsTodo.size() == 0);
     1455        Assert(llParentsTodo.size() == 0);
     1456        /* Create the list of notifications, parent first. */
     1457        MediaList::const_reverse_iterator itBegin = llMediaTocleanup.rbegin();
     1458        MediaList::const_reverse_iterator itEnd = llMediaTocleanup.rend();
     1459        for (MediaList::const_reverse_iterator it = itBegin; it != itEnd; --it)
     1460        {
     1461            ComObjPtr<Medium> pMedium = *it;
     1462            AutoCaller mediumCaller(pMedium);
     1463            if (FAILED(mediumCaller.rc())) continue;
     1464            const Guid &id = pMedium->i_getId();
     1465            uIdsForNotify.push_back(std::pair<Guid, DeviceType_T>(id, aDeviceType));
     1466        }
     1467    }
     1468    else
     1469    {
     1470        /* Forget state of the settings processing. */
     1471        llSettingsTodo.clear();
     1472        llParentsTodo.clear();
     1473        /* Unregister all accumulated medium objects in the right order (last
     1474         * created to first created, avoiding config leftovers). */
     1475        MediaList::const_iterator itBegin = llMediaTocleanup.begin();
     1476        MediaList::const_iterator itEnd = llMediaTocleanup.end();
     1477        for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
     1478        {
     1479            ComObjPtr<Medium> pMedium = *it;
     1480            pMedium->i_unregisterWithVirtualBox();
     1481        }
     1482        /* Forget the only references to all newly created medium objects,
     1483         * triggering freeing (uninit happened in unregistering above). */
     1484        mediaTreeLock.release();
     1485        llMediaTocleanup.clear();
    14641486        mediaTreeLock.acquire();
     1487    }
    14651488
    14661489    return rc;
     
    15331556 * Called either from FinalRelease() or by the parent when it gets destroyed.
    15341557 *
    1535  * @note All children of this medium get uninitialized by calling their
    1536  *       uninit() methods.
     1558 * @note All children of this medium get uninitialized, too, in a stack
     1559 *       friendly manner.
    15371560 */
    15381561void Medium::uninit()
     
    15521575        return;
    15531576
    1554     /* Caller must not hold the object or media tree lock over uninit(). */
    1555     Assert(!isWriteLockOnCurrentThread());
     1577    /* Caller must not hold the object (checked below) or media tree lock. */
    15561578    Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    15571579
    15581580    AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     1581
     1582    MediaList llMediaTodo;
     1583    llMediaTodo.push_back(this);
     1584
     1585    while (!llMediaTodo.empty())
     1586    {
     1587        /* This also guarantees that the refcount doesn't actually drop to 0
     1588         * again while the uninit is already ongoing. */
     1589        ComObjPtr<Medium> pMedium = llMediaTodo.front();
     1590        llMediaTodo.pop_front();
     1591
     1592        /* Enclose the state transition Ready->InUninit->NotReady */
     1593        AutoUninitSpan autoUninitSpan(pMedium);
     1594        if (autoUninitSpan.uninitDone())
     1595            continue;
     1596
     1597        Assert(!pMedium->isWriteLockOnCurrentThread());
    15591598#ifdef DEBUG
    1560     if (!m->backRefs.empty())
    1561         i_dumpBackRefs();
     1599        if (!pMedium->m->backRefs.empty())
     1600            pMedium->i_dumpBackRefs();
    15621601#endif
    1563     Assert(m->backRefs.empty());
    1564 
    1565     /* Enclose the state transition Ready->InUninit->NotReady */
    1566     AutoUninitSpan autoUninitSpan(this);
    1567     if (autoUninitSpan.uninitDone())
    1568         return;
    1569 
    1570     if (!m->formatObj.isNull())
    1571         m->formatObj.setNull();
    1572 
    1573     if (m->state == MediumState_Deleting)
    1574     {
    1575         /* This medium has been already deleted (directly or as part of a
    1576          * merge).  Reparenting has already been done. */
    1577         Assert(m->pParent.isNull());
    1578     }
    1579     else
    1580     {
    1581         MediaList llChildren(m->llChildren);
    1582         m->llChildren.clear();
     1602        Assert(pMedium->m->backRefs.empty());
     1603
     1604        pMedium->m->formatObj.setNull();
     1605
     1606        if (m->state == MediumState_Deleting)
     1607        {
     1608            /* This medium has been already deleted (directly or as part of a
     1609             * merge).  Reparenting has already been done. */
     1610            Assert(m->pParent.isNull());
     1611            Assert(m->llChildren.empty());
     1612            continue;
     1613        }
     1614
     1615        //Assert(!pMedium->m->pParent);
     1616        /** @todo r=klaus Should not be necessary, since the caller should be
     1617         * doing the deparenting. No time right now to test everything. */
     1618        if (pMedium == this && pMedium->m->pParent)
     1619            pMedium->i_deparent();
     1620
     1621        /* Process all children */
     1622        MediaList::const_iterator itBegin = pMedium->m->llChildren.begin();
     1623        MediaList::const_iterator itEnd = pMedium->m->llChildren.end();
     1624        for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
     1625        {
     1626            Medium *pChild = *it;
     1627            pChild->m->pParent.setNull();
     1628            llMediaTodo.push_back(pChild);
     1629        }
     1630
     1631        /* Children information obsolete, will be processed anyway. */
     1632        pMedium->m->llChildren.clear();
     1633
     1634        unconst(pMedium->m->pVirtualBox) = NULL;
     1635
    15831636        autoUninitSpan.setSucceeded();
    1584 
    1585         while (!llChildren.empty())
    1586         {
    1587             ComObjPtr<Medium> pChild = llChildren.front();
    1588             llChildren.pop_front();
    1589             pChild->m->pParent.setNull();
    1590             treeLock.release();
    1591             pChild->uninit();
    1592             treeLock.acquire();
    1593         }
    1594 
    1595         if (m->pParent)
    1596         {
    1597             // this is a differencing disk: then remove it from the parent's children list
    1598             i_deparent();
    1599         }
    1600     }
    1601 
    1602     unconst(m->pVirtualBox) = NULL;
     1637    }
    16031638}
    16041639
     
    36223657        ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
    36233658
    3624         /* canClose() needs the tree lock */
     3659        /* i_canClose() needs the tree lock */
    36253660        AutoMultiWriteLock2 multilock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL,
    36263661                                      this->lockHandle()
     
    42234258 * medium should be registered. The UUID can either be a machine UUID,
    42244259 * to add a machine registry, or the global registry UUID as returned by
    4225  * VirtualBox::getGlobalRegistryId(). This recurses over all children.
     4260 * VirtualBox::getGlobalRegistryId(). Thisis applied to all children.
    42264261 *
    42274262 * Note that for hard disks, this method does nothing if the medium is
     
    42354270 * @return true if the registry was added; false if the given id was already on the list.
    42364271 */
    4237 bool Medium::i_addRegistryRecursive(const Guid &id)
    4238 {
    4239     AutoCaller autoCaller(this);
    4240     if (FAILED(autoCaller.rc()))
    4241         return false;
    4242 
    4243     bool fAdd = i_addRegistryNoCallerCheck(id);
    4244 
    4245     // protected by the medium tree lock held by our original caller
    4246     for (MediaList::const_iterator it = i_getChildren().begin();
    4247          it != i_getChildren().end();
    4248          ++it)
    4249     {
    4250         Medium *pChild = *it;
    4251         fAdd |= pChild->i_addRegistryRecursive(id);
     4272bool Medium::i_addRegistryAll(const Guid &id)
     4273{
     4274    MediaList llMediaTodo;
     4275    llMediaTodo.push_back(this);
     4276
     4277    bool fAdd = false;
     4278
     4279    while (llMediaTodo.size() > 0)
     4280    {
     4281        ComObjPtr<Medium> pMedium = llMediaTodo.front();
     4282        llMediaTodo.pop_front();
     4283
     4284        AutoCaller mediumCaller(pMedium);
     4285        if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
     4286
     4287        fAdd |= pMedium->i_addRegistryNoCallerCheck(id);
     4288
     4289        // protected by the medium tree lock held by our original caller
     4290        MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
     4291        MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
     4292        for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
     4293            llMediaTodo.push_back(*it);
    42524294    }
    42534295
     
    42894331/**
    42904332 * Removes the given UUID from the list of media registry UUIDs, for this
    4291  * medium and all its children recursively.
     4333 * medium and all its children.
    42924334 *
    42934335 * @note the caller must hold the media tree lock for reading.
     
    42964338 * @return true if the UUID was found or false if not.
    42974339 */
    4298 bool Medium::i_removeRegistryRecursive(const Guid &id)
    4299 {
    4300     AutoCaller autoCaller(this);
    4301     if (FAILED(autoCaller.rc()))
    4302         return false;
    4303 
    4304     bool fRemove = i_removeRegistry(id);
    4305 
    4306     // protected by the medium tree lock held by our original caller
    4307     for (MediaList::const_iterator it = i_getChildren().begin();
    4308          it != i_getChildren().end();
    4309          ++it)
    4310     {
    4311         Medium *pChild = *it;
    4312         fRemove |= pChild->i_removeRegistryRecursive(id);
     4340bool Medium::i_removeRegistryAll(const Guid &id)
     4341{
     4342    MediaList llMediaTodo;
     4343    llMediaTodo.push_back(this);
     4344
     4345    bool fRemove = false;
     4346
     4347    while (llMediaTodo.size() > 0)
     4348    {
     4349        ComObjPtr<Medium> pMedium = llMediaTodo.front();
     4350        llMediaTodo.pop_front();
     4351
     4352        AutoCaller mediumCaller(pMedium);
     4353        if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
     4354
     4355        fRemove |= pMedium->i_removeRegistry(id);
     4356
     4357        // protected by the medium tree lock held by our original caller
     4358        MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
     4359        MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
     4360        for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
     4361            llMediaTodo.push_back(*it);
    43134362    }
    43144363
     
    45974646 *
    45984647 * Must have caller + locking, *and* caller must hold the media tree lock!
     4648 * @param aId   Id to ignore when looking for backrefs.
    45994649 * @return
    46004650 */
    4601 const Guid* Medium::i_getAnyMachineBackref() const
    4602 {
    4603     if (m->backRefs.size())
    4604         return &m->backRefs.front().machineId;
    4605 
    4606     for (MediaList::const_iterator it = i_getChildren().begin();
    4607          it != i_getChildren().end();
    4608          ++it)
    4609     {
    4610         Medium *pChild = *it;
    4611         // recurse for this child
    4612         const Guid* puuid;
    4613         if ((puuid = pChild->i_getAnyMachineBackref()))
    4614             return puuid;
     4651const Guid* Medium::i_getAnyMachineBackref(const Guid &aId) const
     4652{
     4653    std::list<const Medium *> llMediaTodo;
     4654    llMediaTodo.push_back(this);
     4655
     4656    while (llMediaTodo.size() > 0)
     4657    {
     4658        const Medium *pMedium = llMediaTodo.front();
     4659        llMediaTodo.pop_front();
     4660
     4661        if (pMedium->m->backRefs.size())
     4662        {
     4663            if (pMedium->m->backRefs.front().machineId != aId)
     4664                return &pMedium->m->backRefs.front().machineId;
     4665            if (pMedium->m->backRefs.size() > 1)
     4666            {
     4667                BackRefList::const_iterator it = pMedium->m->backRefs.begin();
     4668                ++it;
     4669                return &it->machineId;
     4670            }
     4671        }
     4672
     4673        MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
     4674        MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
     4675        for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
     4676            llMediaTodo.push_back(*it);
    46154677    }
    46164678
     
    49364998/**
    49374999 * Saves medium data by putting it into the provided data structure.
    4938  * Recurses over all children to save their settings, too.
     5000 * The settings of all children is saved, too.
    49395001 *
    49405002 * @param data      Settings struct to be updated.
     
    49525014    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    49535015
    4954     i_saveSettingsOne(data, strHardDiskFolder);
    4955 
    4956     /* save all children */
    4957     settings::MediaList &llSettingsChildren = data.llChildren;
    4958     for (MediaList::const_iterator it = i_getChildren().begin();
    4959          it != i_getChildren().end();
    4960          ++it)
    4961     {
    4962         // Use the element straight in the list to reduce both unnecessary
    4963         // deep copying (when unwinding the recursion the entire medium
    4964         // settings sub-tree is copied) and the stack footprint (the settings
    4965         // need almost 1K, and there can be VMs with long image chains.
    4966         llSettingsChildren.push_back(settings::Medium::Empty);
    4967         HRESULT rc = (*it)->i_saveSettings(llSettingsChildren.back(), strHardDiskFolder);
    4968         if (FAILED(rc))
    4969         {
    4970             llSettingsChildren.pop_back();
    4971             return rc;
     5016    MediaList llMediaTodo;
     5017    llMediaTodo.push_back(this);
     5018    std::list<settings::Medium *> llSettingsTodo;
     5019    llSettingsTodo.push_back(&data);
     5020
     5021    while (llMediaTodo.size() > 0)
     5022    {
     5023        ComObjPtr<Medium> pMedium = llMediaTodo.front();
     5024        llMediaTodo.pop_front();
     5025        settings::Medium *current = llSettingsTodo.front();
     5026        llSettingsTodo.pop_front();
     5027
     5028        AutoCaller mediumCaller(pMedium);
     5029        if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
     5030
     5031        pMedium->i_saveSettingsOne(*current, strHardDiskFolder);
     5032
     5033        /* save all children */
     5034        MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
     5035        MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
     5036        for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
     5037        {
     5038            llMediaTodo.push_back(*it);
     5039            current->llChildren.push_back(settings::Medium::Empty);
     5040            llSettingsTodo.push_back(&current->llChildren.back());
    49725041        }
    49735042    }
     
    54025471    try
    54035472    {
    5404         /* we're accessing the media tree, and canClose() needs it too */
     5473        /* we're accessing the media tree, and i_canClose() needs it too */
    54055474        AutoWriteLock treelock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    54065475
  • trunk/src/VBox/Main/src-server/SnapshotImpl.cpp

    r93410 r94598  
    157157 *  Since this manipulates the snapshots tree, the caller must hold the
    158158 *  machine lock in write mode (which protects the snapshots tree)!
     159 *
     160 * @note All children of this snapshot get uninitialized, too, in a stack
     161 *       friendly manner.
    159162 */
    160163void Snapshot::uninit()
     
    162165    LogFlowThisFunc(("\n"));
    163166
    164     /* Enclose the state transition Ready->InUninit->NotReady */
    165     AutoUninitSpan autoUninitSpan(this);
    166     if (autoUninitSpan.uninitDone())
    167         return;
    168 
    169     Assert(m->pMachine->isWriteLockOnCurrentThread());
    170 
    171     // uninit all children
    172     SnapshotsList::iterator it;
    173     for (it = m->llChildren.begin();
    174          it != m->llChildren.end();
    175          ++it)
    176     {
    177         Snapshot *pChild = *it;
    178         pChild->m->pParent.setNull();
    179         pChild->uninit();
    180     }
    181     m->llChildren.clear();          // this unsets all the ComPtrs and probably calls delete
    182 
    183     // since there is no guarantee anyone holds a reference to us except the
    184     // list of children in our parent, make sure that the reference count
    185     // will not drop to 0 before we've declared ourselves as uninitialized,
    186     // otherwise there will be another uninit call which causes a self-deadlock
    187     // because this uninit isn't complete yet.
    188     ComObjPtr<Snapshot> pSnapshot(this);
    189     if (m->pParent)
    190         i_deparent();
    191 
    192     if (m->pMachine)
    193     {
    194         m->pMachine->uninit();
    195         m->pMachine.setNull();
    196     }
    197 
    198     delete m;
    199     m = NULL;
    200 
    201     autoUninitSpan.setSucceeded();
    202     // see above, now the refcount may reach 0
    203     pSnapshot.setNull();
     167    {
     168        /* If "this" is already uninitialized or was never initialized, skip
     169         * all activity since it makes no sense. Also would cause asserts with
     170         * the automatic refcount updating with SnapshotList/ComPtr. Also,
     171         * make sure that the possible fake error is undone. */
     172        ErrorInfoKeeper eik;
     173        AutoLimitedCaller autoCaller(this);
     174        if (FAILED(autoCaller.rc()))
     175            return;
     176    }
     177
     178    SnapshotsList llSnapshotsTodo;
     179    llSnapshotsTodo.push_back(this);
     180    SnapshotsList llSnapshotsAll;
     181
     182    while (llSnapshotsTodo.size() > 0)
     183    {
     184        /* This also guarantees that the refcount doesn't actually drop to 0
     185         * again while the uninit is already ongoing. */
     186        ComObjPtr<Snapshot> pSnapshot = llSnapshotsTodo.front();
     187        llSnapshotsTodo.pop_front();
     188
     189        /* Enclose the state transition Ready->InUninit->NotReady */
     190        AutoUninitSpan autoUninitSpan(pSnapshot);
     191        if (autoUninitSpan.uninitDone())
     192            continue;
     193
     194        /* Remember snapshots (depth first), for associated SnapshotMachine
     195         * uninitialization, which must be done in dept first order, otherwise
     196         * the Medium object uninit is done in the wrong order. */
     197        llSnapshotsAll.push_front(pSnapshot);
     198
     199        Assert(pSnapshot->m->pMachine->isWriteLockOnCurrentThread());
     200
     201        /* Paranoia. Shouldn't be set any more at processing time. */
     202        pSnapshot->m->pParent.setNull();
     203
     204        /* Process all children */
     205        SnapshotsList::const_iterator itBegin = pSnapshot->m->llChildren.begin();
     206        SnapshotsList::const_iterator itEnd = pSnapshot->m->llChildren.end();
     207        for (SnapshotsList::const_iterator it = itBegin; it != itEnd; ++it)
     208        {
     209            Snapshot *pChild = *it;
     210            pChild->m->pParent.setNull();
     211            llSnapshotsTodo.push_back(pChild);
     212        }
     213
     214        /* Children information obsolete, will be processed anyway. */
     215        pSnapshot->m->llChildren.clear();
     216
     217        autoUninitSpan.setSucceeded();
     218    }
     219
     220    /* Now handle SnapshotMachine uninit and free memory. */
     221    while (llSnapshotsAll.size() > 0)
     222    {
     223        ComObjPtr<Snapshot> pSnapshot = llSnapshotsAll.front();
     224        llSnapshotsAll.pop_front();
     225
     226        if (pSnapshot->m->pMachine)
     227        {
     228            pSnapshot->m->pMachine->uninit();
     229            pSnapshot->m->pMachine.setNull();
     230        }
     231
     232        delete pSnapshot->m;
     233        pSnapshot->m = NULL;
     234    }
    204235}
    205236
     
    278309/**
    279310 * Internal helper that removes "this" from the list of children of its
    280  * parent. Used in uninit() and other places when reparenting is necessary.
     311 * parent. Used in places when reparenting is necessary.
    281312 *
    282313 * The caller must hold the machine lock in write mode (which protects the snapshots tree)!
     
    488519/**
    489520 * Returns the number of direct child snapshots, without grandchildren.
    490  * Does not recurse.
    491521 * @return
    492522 */
     
    503533
    504534/**
    505  * Implementation method for getAllChildrenCount() so we request the
    506  * tree lock only once before recursing. Don't call directly.
     535 * Returns the number of child snapshots including all grandchildren.
    507536 * @return
    508537 */
    509 ULONG Snapshot::i_getAllChildrenCountImpl()
     538ULONG Snapshot::i_getAllChildrenCount()
    510539{
    511540    AutoCaller autoCaller(this);
    512541    AssertComRC(autoCaller.rc());
    513542
    514     ULONG count = (ULONG)m->llChildren.size();
    515     for (SnapshotsList::const_iterator it = m->llChildren.begin();
    516          it != m->llChildren.end();
    517          ++it)
    518     {
    519         count += (*it)->i_getAllChildrenCountImpl();
    520     }
    521 
    522     return count;
    523 }
    524 
    525 /**
    526  * Returns the number of child snapshots including all grandchildren.
    527  * Recurses into the snapshots tree.
    528  * @return
    529  */
    530 ULONG Snapshot::i_getAllChildrenCount()
    531 {
    532     AutoCaller autoCaller(this);
    533     AssertComRC(autoCaller.rc());
    534 
    535543    // snapshots tree is protected by machine lock
    536544    AutoReadLock alock(m->pMachine COMMA_LOCKVAL_SRC_POS);
    537545
    538     return i_getAllChildrenCountImpl();
     546    std::list<const Snapshot *> llSnapshotsTodo;
     547    llSnapshotsTodo.push_back(this);
     548
     549    ULONG cChildren = 0;
     550
     551    while (llSnapshotsTodo.size() > 0)
     552    {
     553        const Snapshot *pSnapshot = llSnapshotsTodo.front();
     554        llSnapshotsTodo.pop_front();
     555
     556        cChildren += (ULONG)pSnapshot->m->llChildren.size();
     557
     558        /* count all children */
     559        SnapshotsList::const_iterator itBegin = pSnapshot->m->llChildren.begin();
     560        SnapshotsList::const_iterator itEnd = pSnapshot->m->llChildren.end();
     561        for (SnapshotsList::const_iterator it = itBegin; it != itEnd; ++it)
     562            llSnapshotsTodo.push_back(*it);
     563    }
     564
     565    return cChildren;
    539566}
    540567
     
    720747    const Utf8Str &path = m->pMachine->mSSData->strStateFilePath;
    721748
    722     if (!pSnapshotToIgnore || pSnapshotToIgnore != this)
    723         if (path.isNotEmpty())
     749    if (path.isEmpty())
     750        return false;
     751
     752    std::list<const Snapshot *> llSnapshotsTodo;
     753    llSnapshotsTodo.push_back(this);
     754
     755    while (llSnapshotsTodo.size() > 0)
     756    {
     757        const Snapshot *pSnapshot = llSnapshotsTodo.front();
     758        llSnapshotsTodo.pop_front();
     759
     760        if (!pSnapshotToIgnore || pSnapshotToIgnore != this)
    724761            if (path == strPath)
    725                 return true;        // no need to recurse then
    726 
    727     // but otherwise we must check children
    728     for (SnapshotsList::const_iterator it = m->llChildren.begin();
    729          it != m->llChildren.end();
    730          ++it)
    731     {
    732         Snapshot *pChild = *it;
    733         if (!pSnapshotToIgnore || pSnapshotToIgnore != pChild)
    734             if (pChild->i_sharesSavedStateFile(strPath, pSnapshotToIgnore))
    735762                return true;
     763
     764        /* check all children */
     765        SnapshotsList::const_iterator itBegin = pSnapshot->m->llChildren.begin();
     766        SnapshotsList::const_iterator itEnd = pSnapshot->m->llChildren.end();
     767        for (SnapshotsList::const_iterator it = itBegin; it != itEnd; ++it)
     768            llSnapshotsTodo.push_back(*it);
    736769    }
    737770
     
    805838 * @return
    806839 */
    807 HRESULT Snapshot::i_saveSnapshotImplOne(settings::Snapshot &data) const
     840HRESULT Snapshot::i_saveSnapshotOne(settings::Snapshot &data) const
    808841{
    809842    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     
    827860
    828861/**
    829  * Internal implementation for Snapshot::saveSnapshot (below). Caller has
    830  * requested the snapshots tree (machine) lock.
    831  *
    832  * @param data      Target for saving snapshot settings.
    833  * @return
    834  */
    835 HRESULT Snapshot::i_saveSnapshotImpl(settings::Snapshot &data) const
    836 {
    837     HRESULT rc = i_saveSnapshotImplOne(data);
    838     if (FAILED(rc))
    839         return rc;
    840 
    841     settings::SnapshotsList &llSettingsChildren = data.llChildSnapshots;
    842     for (SnapshotsList::const_iterator it = m->llChildren.begin();
    843          it != m->llChildren.end();
    844          ++it)
    845     {
    846         // Use the heap (indirectly through the list container) to reduce the
    847         // stack footprint, avoiding local settings objects on the stack which
    848         // need a lot of stack space. There can be VMs with deeply nested
    849         // snapshots. The stack can be quite small, especially with XPCOM.
    850         llSettingsChildren.push_back(settings::Snapshot::Empty);
    851         Snapshot *pSnap = *it;
    852         rc = pSnap->i_saveSnapshotImpl(llSettingsChildren.back());
    853         if (FAILED(rc))
    854         {
    855             llSettingsChildren.pop_back();
    856             return rc;
    857         }
    858     }
    859 
    860     return S_OK;
    861 }
    862 
    863 /**
    864862 * Saves the given snapshot and all its children.
    865863 * It is assumed that the given node is empty.
     
    872870    AutoReadLock alock(m->pMachine COMMA_LOCKVAL_SRC_POS);
    873871
    874     return i_saveSnapshotImpl(data);
     872    std::list<const Snapshot *> llSnapshotsTodo;
     873    llSnapshotsTodo.push_back(this);
     874    std::list<settings::Snapshot *> llSettingsTodo;
     875    llSettingsTodo.push_back(&data);
     876
     877    while (llSnapshotsTodo.size() > 0)
     878    {
     879        const Snapshot *pSnapshot = llSnapshotsTodo.front();
     880        llSnapshotsTodo.pop_front();
     881        settings::Snapshot *current = llSettingsTodo.front();
     882        llSettingsTodo.pop_front();
     883
     884        HRESULT rc = pSnapshot->i_saveSnapshotOne(*current);
     885        if (FAILED(rc))
     886            return rc;
     887
     888        /* save all children */
     889        SnapshotsList::const_iterator itBegin = pSnapshot->m->llChildren.begin();
     890        SnapshotsList::const_iterator itEnd = pSnapshot->m->llChildren.end();
     891        for (SnapshotsList::const_iterator it = itBegin; it != itEnd; ++it)
     892        {
     893            llSnapshotsTodo.push_back(*it);
     894            current->llChildSnapshots.push_back(settings::Snapshot::Empty);
     895            llSettingsTodo.push_back(&current->llChildSnapshots.back());
     896        }
     897    }
     898
     899    return S_OK;
    875900}
    876901
     
    879904 *
    880905 * This removes all medium attachments from the snapshot's machine and returns
    881  * the snapshot's saved state file name, if any, and then calls uninit() on
    882  * "this" itself.
     906 * the snapshot's saved state file name, if any, and then calls uninit().
     907 *
     908 * This processes children depth first, so the given MediaList receives child
     909 * media first before their parents. If the caller wants to close all media,
     910 * they should go thru the list from the beginning to the end because media
     911 * cannot be closed if they have children.
     912 *
     913 * This calls uninit() on itself, so the snapshots tree (beginning with a machine's pFirstSnapshot) becomes invalid after this.
     914 * It does not alter the main machine's snapshot pointers (pFirstSnapshot, pCurrentSnapshot).
    883915 *
    884916 * Caller must hold the machine write lock (which protects the snapshots tree!)
     
    890922 * @return
    891923 */
    892 HRESULT Snapshot::i_uninitOne(AutoWriteLock &writeLock,
     924HRESULT Snapshot::i_uninitAll(AutoWriteLock &writeLock,
    893925                              CleanupMode_T cleanupMode,
    894926                              MediaList &llMedia,
    895927                              std::list<Utf8Str> &llFilenames)
    896928{
    897     // now call detachAllMedia on the snapshot machine
    898     HRESULT rc = m->pMachine->i_detachAllMedia(writeLock,
    899                                                this /* pSnapshot */,
    900                                                cleanupMode,
    901                                                llMedia);
    902     if (FAILED(rc))
    903         return rc;
    904 
    905     // report the saved state file if it's not on the list yet
    906     if (m->pMachine->mSSData->strStateFilePath.isNotEmpty())
    907     {
    908         bool fFound = false;
    909         for (std::list<Utf8Str>::const_iterator it = llFilenames.begin();
    910              it != llFilenames.end();
    911              ++it)
    912         {
    913             const Utf8Str &str = *it;
    914             if (str == m->pMachine->mSSData->strStateFilePath)
     929    Assert(m->pMachine->isWriteLockOnCurrentThread());
     930
     931    HRESULT rc = S_OK;
     932
     933    SnapshotsList llSnapshotsTodo;
     934    llSnapshotsTodo.push_front(this);
     935    SnapshotsList llSnapshotsAll;
     936
     937    /* Enumerate all snapshots depth first, avoids trouble with updates. */
     938    while (llSnapshotsTodo.size() > 0)
     939    {
     940        ComObjPtr<Snapshot> pSnapshot = llSnapshotsTodo.front();
     941        llSnapshotsTodo.pop_front();
     942
     943        llSnapshotsAll.push_front(pSnapshot);
     944
     945        /* Process all children */
     946        SnapshotsList::const_iterator itBegin = pSnapshot->m->llChildren.begin();
     947        SnapshotsList::const_iterator itEnd = pSnapshot->m->llChildren.end();
     948        for (SnapshotsList::const_iterator it = itBegin; it != itEnd; ++it)
     949        {
     950            Snapshot *pChild = *it;
     951            pChild->m->pParent.setNull();
     952            llSnapshotsTodo.push_front(pChild);
     953        }
     954    }
     955
     956    /* Process all snapshots in enumeration order. */
     957    while (llSnapshotsAll.size() > 0)
     958    {
     959        /* This also guarantees that the refcount doesn't actually drop to 0
     960         * again while the uninit is already ongoing. */
     961        ComObjPtr<Snapshot> pSnapshot = llSnapshotsAll.front();
     962        llSnapshotsAll.pop_front();
     963
     964        rc = pSnapshot->m->pMachine->i_detachAllMedia(writeLock,
     965                                                      pSnapshot,
     966                                                      cleanupMode,
     967                                                      llMedia);
     968        if (SUCCEEDED(rc))
     969        {
     970            Utf8Str strFile;
     971
     972            // report the saved state file if it's not on the list yet
     973            strFile = pSnapshot->m->pMachine->mSSData->strStateFilePath;
     974            if (strFile.isNotEmpty())
    915975            {
    916                 fFound = true;
    917                 break;
     976                std::list<Utf8Str>::const_iterator itFound = find(llFilenames.begin(), llFilenames.end(), strFile);
     977
     978                if (itFound != llFilenames.end())
     979                    llFilenames.push_back(strFile);
    918980            }
    919         }
    920         if (!fFound)
    921             llFilenames.push_back(m->pMachine->mSSData->strStateFilePath);
    922     }
    923 
    924     Utf8Str strNVRAMFile = m->pMachine->mNvramStore->i_getNonVolatileStorageFile();
    925     if (strNVRAMFile.isNotEmpty() && RTFileExists(strNVRAMFile.c_str()))
    926         llFilenames.push_back(strNVRAMFile);
    927 
    928     i_beginSnapshotDelete();
    929     uninit();
     981
     982            strFile = pSnapshot->m->pMachine->mNvramStore->i_getNonVolatileStorageFile();
     983            if (strFile.isNotEmpty() && RTFileExists(strFile.c_str()))
     984                llFilenames.push_back(strFile);
     985        }
     986
     987        pSnapshot->m->pParent.setNull();
     988        pSnapshot->m->llChildren.clear();
     989        pSnapshot->uninit();
     990    }
    930991
    931992    return S_OK;
    932 }
    933 
    934 /**
    935  * Part of the cleanup engine of Machine::Unregister().
    936  *
    937  * This recursively removes all medium attachments from the snapshot's machine
    938  * and returns the snapshot's saved state file name, if any, and then calls
    939  * uninit() on "this" itself.
    940  *
    941  * This recurses into children first, so the given MediaList receives child
    942  * media first before their parents. If the caller wants to close all media,
    943  * they should go thru the list from the beginning to the end because media
    944  * cannot be closed if they have children.
    945  *
    946  * This calls uninit() on itself, so the snapshots tree (beginning with a machine's pFirstSnapshot) becomes invalid after this.
    947  * It does not alter the main machine's snapshot pointers (pFirstSnapshot, pCurrentSnapshot).
    948  *
    949  * Caller must hold the machine write lock (which protects the snapshots tree!)
    950  *
    951  * @param writeLock Machine write lock, which can get released temporarily here.
    952  * @param cleanupMode Cleanup mode; see Machine::detachAllMedia().
    953  * @param llMedia List of media returned to caller, depending on cleanupMode.
    954  * @param llFilenames
    955  * @return
    956  */
    957 HRESULT Snapshot::i_uninitRecursively(AutoWriteLock &writeLock,
    958                                       CleanupMode_T cleanupMode,
    959                                       MediaList &llMedia,
    960                                       std::list<Utf8Str> &llFilenames)
    961 {
    962     Assert(m->pMachine->isWriteLockOnCurrentThread());
    963 
    964     HRESULT rc = S_OK;
    965 
    966     // make a copy of the Guid for logging before we uninit ourselves
    967 #ifdef LOG_ENABLED
    968     Guid uuid = i_getId();
    969     Utf8Str name = i_getName();
    970     LogFlowThisFunc(("Entering for snapshot '%s' {%RTuuid}\n", name.c_str(), uuid.raw()));
    971 #endif
    972 
    973     // Recurse into children first so that the child media appear on the list
    974     // first; this way caller can close the media from the beginning to the end
    975     // because parent media can't be closed if they have children and
    976     // additionally it postpones the uninit() call until we no longer need
    977     // anything from the list. Oh, and remember that the child removes itself
    978     // from the list, so keep the iterator at the beginning.
    979     for (SnapshotsList::const_iterator it = m->llChildren.begin();
    980          it != m->llChildren.end();
    981          it = m->llChildren.begin())
    982     {
    983         Snapshot *pChild = *it;
    984         rc = pChild->i_uninitRecursively(writeLock, cleanupMode, llMedia, llFilenames);
    985         if (FAILED(rc))
    986             break;
    987     }
    988 
    989     if (SUCCEEDED(rc))
    990         rc = i_uninitOne(writeLock, cleanupMode, llMedia, llFilenames);
    991 
    992 #ifdef LOG_ENABLED
    993     LogFlowThisFunc(("Leaving for snapshot '%s' {%RTuuid}: %Rhrc\n", name.c_str(), uuid.raw(), rc));
    994 #endif
    995 
    996     return rc;
    997993}
    998994
     
    17311727    }
    17321728
     1729    LogRel(("Taking snapshot %s\n", task.m_strName.c_str()));
     1730
    17331731    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    17341732
     
    17581756        /* save settings to ensure current changes are committed and
    17591757         * hard disks are fixed up */
    1760         rc = i_saveSettings(NULL, alock);
     1758        rc = i_saveSettings(NULL, alock); /******************1 */
    17611759            // no need to check for whether VirtualBox.xml needs changing since
    17621760            // we can't have a machine XML rename pending at this point
     
    19341932         * machine state to the state it had at the beginning.
    19351933         */
    1936         rc = i_finishTakingSnapshot(task, alock, true /*aSuccess*/);
     1934        rc = i_finishTakingSnapshot(task, alock, true /*aSuccess*/); /*******************2+3 */
    19371935        // do not throw rc here because we can't call i_finishTakingSnapshot() twice
    19381936        LogFlowThisFunc(("i_finishTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, ::stringifyMachineState(mData->mMachineState)));
     
    19781976        {
    19791977            if (!task.m_fTakingSnapshotOnline)
    1980                 i_setMachineState(task.m_machineStateBackup);
     1978                i_setMachineState(task.m_machineStateBackup); /**************** 4 Machine::i_saveStateSettings*/
    19811979            else
    19821980            {
     
    20482046        }
    20492047    }
     2048    LogRel(("Finished taking snapshot %s\n", task.m_strName.c_str()));
    20502049    LogFlowThisFuncLeave();
    20512050}
     
    21122111            flSaveSettings |= SaveS_ResetCurStateModified;
    21132112
    2114         rc = i_saveSettings(NULL, alock, flSaveSettings);
     2113        rc = i_saveSettings(NULL, alock, flSaveSettings); /******************2 */
    21152114    }
    21162115
     
    21502149
    21512150    /* alock has been released already */
    2152     mParent->i_saveModifiedRegistries();
     2151    mParent->i_saveModifiedRegistries(); /**************3 */
    21532152
    21542153    alock.acquire();
  • trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp

    r94249 r94598  
    935935        const settings::Medium &xmlHD = *it;
    936936
    937         ComObjPtr<Medium> pHardDisk;
    938         rc = pHardDisk.createObject();
     937        rc = Medium::initFromSettings(this,
     938                                      DeviceType_HardDisk,
     939                                      uuidRegistry,
     940                                      strMachineFolder,
     941                                      xmlHD,
     942                                      treeLock,
     943                                      uIdsForNotify);
    939944        if (FAILED(rc)) return rc;
    940         ComObjPtr<Medium> pHardDiskActual(pHardDisk);
    941         rc = pHardDisk->initFromSettings(this,
    942                                          NULL,          // parent
    943                                          DeviceType_HardDisk,
    944                                          uuidRegistry,
    945                                          xmlHD,         // XML data; this recurses to processes the children
    946                                          strMachineFolder,
    947                                          treeLock,
    948                                          &pHardDiskActual /*never &pHardDisk!*/);
    949         if (SUCCEEDED(rc))
    950         {
    951             /** @todo r=bird: should we really do notifications for duplicates?
    952              *  ((Medium *)pHardDisk != (Medium *)pHardDiskActual)
    953              * The problem with that, though, is that for the children we don't quite know
    954              * which are duplicates and which aren't.   The above initFromSettings is
    955              * essentially poforming a merge operation now, so in the duplicate case, we may
    956              * just have added a new (grand)child.  Why don't we just pass uIdsForNotify
    957              * down to initFromSettings, that'll save us this extra walking? */
    958 
    959             uIdsForNotify.push_back(std::pair<Guid, DeviceType_T>(pHardDiskActual->i_getId(), DeviceType_HardDisk));
    960             // Add children IDs to notification using non-recursive children enumeration.
    961             std::vector<std::pair<MediaList::const_iterator, ComObjPtr<Medium> > > llEnumStack;
    962             const MediaList& mediaList = pHardDiskActual->i_getChildren();
    963             llEnumStack.push_back(std::pair<MediaList::const_iterator, ComObjPtr<Medium> >(mediaList.begin(), pHardDiskActual));
    964             while (!llEnumStack.empty())
    965             {
    966                 if (llEnumStack.back().first == llEnumStack.back().second->i_getChildren().end())
    967                 {
    968                     llEnumStack.pop_back();
    969                     if (!llEnumStack.empty())
    970                         ++llEnumStack.back().first;
    971                     continue;
    972                 }
    973                 uIdsForNotify.push_back(std::pair<Guid, DeviceType_T>((*llEnumStack.back().first)->i_getId(), DeviceType_HardDisk));
    974                 const MediaList& childMediaList = (*llEnumStack.back().first)->i_getChildren();
    975                 if (!childMediaList.empty())
    976                 {
    977                     llEnumStack.push_back(std::pair<MediaList::const_iterator, ComObjPtr<Medium> >(childMediaList.begin(),
    978                                                                                              *llEnumStack.back().first));
    979                     continue;
    980                 }
    981                 ++llEnumStack.back().first;
    982             }
    983         }
    984         // Avoid trouble with lock/refcount, before returning or not.
    985         treeLock.release();
    986         pHardDisk.setNull();
    987         pHardDiskActual.setNull();
    988         if (FAILED(rc)) return rc;
    989         treeLock.acquire();
    990945    }
    991946
     
    996951        const settings::Medium &xmlDvd = *it;
    997952
    998         ComObjPtr<Medium> pImage;
    999         rc = pImage.createObject();
    1000         if (FAILED(rc)) return rc;
    1001 
    1002         ComObjPtr<Medium> pImageActually = pImage;
    1003         rc = pImage->initFromSettings(this,
    1004                                       NULL,
     953        rc = Medium::initFromSettings(this,
    1005954                                      DeviceType_DVD,
    1006955                                      uuidRegistry,
     956                                      strMachineFolder,
    1007957                                      xmlDvd,
    1008                                       strMachineFolder,
    1009958                                      treeLock,
    1010                                       &pImageActually);
    1011         if (SUCCEEDED(rc))
    1012             uIdsForNotify.push_back(std::pair<Guid, DeviceType_T>(pImageActually->i_getId(), DeviceType_DVD));
    1013 
    1014         // Avoid trouble with lock/refcount, before returning or not.
    1015         treeLock.release();
    1016         pImage.setNull();
    1017         pImageActually.setNull();
     959                                      uIdsForNotify);
    1018960        if (FAILED(rc)) return rc;
    1019         treeLock.acquire();
    1020961    }
    1021962
     
    1026967        const settings::Medium &xmlFloppy = *it;
    1027968
    1028         ComObjPtr<Medium> pImage;
    1029         rc = pImage.createObject();
    1030         if (FAILED(rc)) return rc;
    1031 
    1032         ComObjPtr<Medium> pImageActually = pImage;
    1033         rc = pImage->initFromSettings(this,
    1034                                       NULL,
     969        rc = Medium::initFromSettings(this,
    1035970                                      DeviceType_Floppy,
    1036971                                      uuidRegistry,
     972                                      strMachineFolder,
    1037973                                      xmlFloppy,
    1038                                       strMachineFolder,
    1039974                                      treeLock,
    1040                                       &pImageActually);
    1041         if (SUCCEEDED(rc))
    1042             uIdsForNotify.push_back(std::pair<Guid, DeviceType_T>(pImage->i_getId(), DeviceType_Floppy));
    1043 
    1044         // Avoid trouble with lock/refcount, before returning or not.
    1045         treeLock.release();
    1046         pImage.setNull();
    1047         pImageActually.setNull();
     975                                      uIdsForNotify);
    1048976        if (FAILED(rc)) return rc;
    1049         treeLock.acquire();
    1050     }
    1051 
    1052     if (SUCCEEDED(rc))
    1053     {
    1054         for (std::list<std::pair<Guid, DeviceType_T> >::const_iterator itItem = uIdsForNotify.begin();
    1055              itItem != uIdsForNotify.end();
    1056              ++itItem)
    1057         {
    1058             i_onMediumRegistered(itItem->first, itItem->second, TRUE);
    1059         }
     977    }
     978
     979    for (std::list<std::pair<Guid, DeviceType_T> >::const_iterator itItem = uIdsForNotify.begin();
     980         itItem != uIdsForNotify.end();
     981         ++itItem)
     982    {
     983        i_onMediumRegistered(itItem->first, itItem->second, TRUE);
    1060984    }
    1061985
     
    52385162
    52395163/**
    5240  * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
    5241  * with children appearing before their parents.
    5242  * @param llMedia
    5243  * @param pMedium
    5244  */
    5245 void VirtualBox::i_pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
    5246 {
    5247     // recurse first, then add ourselves; this way children end up on the
    5248     // list before their parents
    5249 
    5250     const MediaList &llChildren = pMedium->i_getChildren();
    5251     for (MediaList::const_iterator it = llChildren.begin();
    5252          it != llChildren.end();
    5253          ++it)
    5254     {
    5255         Medium *pChild = *it;
    5256         i_pushMediumToListWithChildren(llMedia, pChild);
    5257     }
    5258 
    5259     Log(("Pushing medium %RTuuid\n", pMedium->i_getId().raw()));
    5260     llMedia.push_back(pMedium);
    5261 }
    5262 
    5263 /**
    52645164 * Unregisters all Medium objects which belong to the given machine registry.
    52655165 * Gets called from Machine::uninit() just before the machine object dies
     
    52935193            if (FAILED(medCaller.rc())) return medCaller.rc();
    52945194            AutoReadLock medlock(pMedium COMMA_LOCKVAL_SRC_POS);
    5295 
    5296             if (pMedium->i_isInRegistry(uuidMachine))
    5297                 // recursively with children first
    5298                 i_pushMediumToListWithChildren(llMedia2Close, pMedium);
     5195            LogRel(("Looking at medium %RTuuid\n", pMedium->i_getId().raw()));
     5196
     5197            /* If the medium is still in the registry then either some code is
     5198             * seriously buggy (unregistering a VM removes it automatically),
     5199             * or the reference to a Machine object is destroyed without ever
     5200             * being registered. The second condition checks if a medium is
     5201             * in no registry, which indicates (set by unregistering) that a
     5202             * medium is not used by any other VM and thus can be closed. */
     5203            Guid dummy;
     5204            if (   pMedium->i_isInRegistry(uuidMachine)
     5205                || !pMedium->i_getFirstRegistryMachineId(dummy))
     5206            {
     5207                /* Collect all medium objects into llMedia2Close,
     5208                 * in right order for closing. */
     5209                MediaList llMediaTodo;
     5210                llMediaTodo.push_back(pMedium);
     5211
     5212                while (llMediaTodo.size() > 0)
     5213                {
     5214                    ComObjPtr<Medium> pCurrent = llMediaTodo.front();
     5215                    llMediaTodo.pop_front();
     5216
     5217                    /* Add to front, order must be children then parent. */
     5218                    LogRel(("Pushing medium %RTuuid (front)\n", pCurrent->i_getId().raw()));
     5219                    llMedia2Close.push_front(pCurrent);
     5220
     5221                    /* process all children */
     5222                    MediaList::const_iterator itBegin = pCurrent->i_getChildren().begin();
     5223                    MediaList::const_iterator itEnd = pCurrent->i_getChildren().end();
     5224                    for (MediaList::const_iterator it2 = itBegin; it2 != itEnd; ++it2)
     5225                        llMediaTodo.push_back(*it2);
     5226                }
     5227            }
    52995228        }
    53005229    }
     
    53055234    {
    53065235        ComObjPtr<Medium> pMedium = *it;
    5307         Log(("Closing medium %RTuuid\n", pMedium->i_getId().raw()));
     5236        LogRel(("Closing medium %RTuuid\n", pMedium->i_getId().raw()));
    53085237        AutoCaller mac(pMedium);
    53095238        pMedium->i_close(mac);
     
    53195248 * Called from Machine::Unregister().
    53205249 * @param pMachine
     5250 * @param aCleanupMode  How to handle medium attachments. For
     5251 *      CleanupMode_UnregisterOnly the associated medium objects will be
     5252 *      closed when the Machine object is uninitialized, otherwise they will
     5253 *      go to the global registry if no better registry is found.
    53215254 * @param id  UUID of the machine. Must be passed by caller because machine may be dead by this time.
    53225255 * @return
    53235256 */
    53245257HRESULT VirtualBox::i_unregisterMachine(Machine *pMachine,
     5258                                        CleanupMode_T aCleanupMode,
    53255259                                        const Guid &id)
    53265260{
     
    53555289            AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
    53565290
    5357             if (pMedium->i_removeRegistryRecursive(id))
     5291            if (pMedium->i_removeRegistryAll(id))
    53585292            {
    53595293                // machine ID was found in base medium's registry list:
    53605294                // move this base image and all its children to another registry then
    53615295                // 1) first, find a better registry to add things to
    5362                 const Guid *puuidBetter = pMedium->i_getAnyMachineBackref();
     5296                const Guid *puuidBetter = pMedium->i_getAnyMachineBackref(id);
    53635297                if (puuidBetter)
    53645298                {
    53655299                    // 2) better registry found: then use that
    5366                     pMedium->i_addRegistryRecursive(*puuidBetter);
     5300                    pMedium->i_addRegistryAll(*puuidBetter);
    53675301                    // 3) and make sure the registry is saved below
    53685302                    mlock.release();
    53695303                    tlock.release();
    53705304                    i_markRegistryModified(*puuidBetter);
     5305                    tlock.acquire();
     5306                    mlock.acquire();
     5307                }
     5308                else if (aCleanupMode != CleanupMode_UnregisterOnly)
     5309                {
     5310                    pMedium->i_addRegistryAll(i_getGlobalRegistryId());
     5311                    mlock.release();
     5312                    tlock.release();
     5313                    i_markRegistryModified(i_getGlobalRegistryId());
    53715314                    tlock.acquire();
    53725315                    mlock.acquire();
  • trunk/src/VBox/Main/xml/Settings.cpp

    r94580 r94598  
    851851
    852852/**
    853  * Reads a media registry entry from the main VirtualBox.xml file and recurses
    854  * into children where applicable.
     853 * Reads a media registry entry from the main VirtualBox.xml file and
     854 * likewise for all children where applicable.
    855855 *
    856856 * @param t
    857  * @param depth
    858857 * @param elmMedium
    859858 * @param med
    860859 */
    861860void ConfigFileBase::readMedium(MediaType t,
    862                                 uint32_t depth,
    863                                 const xml::ElementNode &elmMedium,  // HardDisk node if root; if recursing,
    864                                                                     // child HardDisk node or DiffHardDisk node for pre-1.4
    865                                 Medium &med)                        // medium settings to fill out
    866 {
    867     if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
    868         throw ConfigFileError(this, &elmMedium, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
    869 
    870     // Do not inline this method call, as the purpose of having this separate
    871     // is to save on stack size. Less local variables are the key for reaching
    872     // deep recursion levels with small stack (XPCOM/g++ without optimization).
    873     readMediumOne(t, elmMedium, med);
    874 
    875     if (t != HardDisk)
    876         return;
    877 
    878     // recurse to handle children
    879     MediaList &llSettingsChildren = med.llChildren;
    880     xml::NodesLoop nl2(elmMedium, m->sv >= SettingsVersion_v1_4 ? "HardDisk" : "DiffHardDisk");
    881     const xml::ElementNode *pelmHDChild;
    882     while ((pelmHDChild = nl2.forAllNodes()))
    883     {
    884         // recurse with this element and put the child at the end of the list.
    885         // XPCOM has very small stack, avoid big local variables and use the
    886         // list element.
    887         llSettingsChildren.push_back(Medium::Empty);
    888         readMedium(t,
    889                    depth + 1,
    890                    *pelmHDChild,
    891                    llSettingsChildren.back());
     861                                const xml::ElementNode &elmMedium,
     862                                Medium &med)
     863{
     864    std::list<const xml::ElementNode *> llElementsTodo;
     865    llElementsTodo.push_back(&elmMedium);
     866    std::list<Medium *> llSettingsTodo;
     867    llSettingsTodo.push_back(&med);
     868    std::list<uint32_t> llDepthsTodo;
     869    llDepthsTodo.push_back(1);
     870
     871    while (llElementsTodo.size() > 0)
     872    {
     873        const xml::ElementNode *pElement = llElementsTodo.front();
     874        llElementsTodo.pop_front();
     875        Medium *pMed = llSettingsTodo.front();
     876        llSettingsTodo.pop_front();
     877        uint32_t depth = llDepthsTodo.front();
     878        llDepthsTodo.pop_front();
     879
     880        if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
     881            throw ConfigFileError(this, pElement, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
     882
     883        readMediumOne(t, *pElement, *pMed);
     884
     885        if (t != HardDisk)
     886            return;
     887
     888        // load all children
     889        xml::NodesLoop nl2(*pElement, m->sv >= SettingsVersion_v1_4 ? "HardDisk" : "DiffHardDisk");
     890        const xml::ElementNode *pelmHDChild;
     891        while ((pelmHDChild = nl2.forAllNodes()))
     892        {
     893            llElementsTodo.push_back(pelmHDChild);
     894            pMed->llChildren.push_back(Medium::Empty);
     895            llSettingsTodo.push_back(&pMed->llChildren.back());
     896            llDepthsTodo.push_back(depth + 1);
     897        }
    892898    }
    893899}
     
    930936            {
    931937                mr.llHardDisks.push_back(Medium::Empty);
    932                 readMedium(t, 1, *pelmMedium, mr.llHardDisks.back());
     938                readMedium(t, *pelmMedium, mr.llHardDisks.back());
    933939            }
    934940            else if (   t == DVDImage
     
    936942            {
    937943                mr.llDvdImages.push_back(Medium::Empty);
    938                 readMedium(t, 1, *pelmMedium, mr.llDvdImages.back());
     944                readMedium(t, *pelmMedium, mr.llDvdImages.back());
    939945            }
    940946            else if (   t == FloppyImage
     
    942948            {
    943949                mr.llFloppyImages.push_back(Medium::Empty);
    944                 readMedium(t, 1, *pelmMedium, mr.llFloppyImages.back());
     950                readMedium(t, *pelmMedium, mr.llFloppyImages.back());
    945951            }
    946952        }
     
    12651271/**
    12661272 * Creates a single \<HardDisk\> element for the given Medium structure
    1267  * and recurses to write the child hard disks underneath. Called from
    1268  * MainConfigFile::write().
     1273 * and all child hard disks underneath. Called from MainConfigFile::write().
    12691274 *
    12701275 * @param t
    1271  * @param depth
    12721276 * @param elmMedium
    1273  * @param mdm
     1277 * @param med
    12741278 */
    12751279void ConfigFileBase::buildMedium(MediaType t,
    1276                                  uint32_t depth,
    12771280                                 xml::ElementNode &elmMedium,
    1278                                  const Medium &mdm)
    1279 {
    1280     if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
    1281         throw ConfigFileError(this, &elmMedium, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
    1282 
    1283     xml::ElementNode *pelmMedium;
    1284 
    1285     if (t == HardDisk)
    1286         pelmMedium = elmMedium.createChild("HardDisk");
    1287     else
    1288         pelmMedium = elmMedium.createChild("Image");
    1289 
    1290     pelmMedium->setAttribute("uuid", mdm.uuid.toStringCurly());
    1291 
    1292     pelmMedium->setAttributePath("location", mdm.strLocation);
    1293 
    1294     if (t == HardDisk || RTStrICmp(mdm.strFormat.c_str(), "RAW"))
    1295         pelmMedium->setAttribute("format", mdm.strFormat);
    1296     if (   t == HardDisk
    1297         && mdm.fAutoReset)
    1298         pelmMedium->setAttribute("autoReset", mdm.fAutoReset);
    1299     if (mdm.strDescription.length())
    1300         pelmMedium->createChild("Description")->addContent(mdm.strDescription);
    1301 
    1302     for (StringsMap::const_iterator it = mdm.properties.begin();
    1303          it != mdm.properties.end();
    1304          ++it)
    1305     {
    1306         xml::ElementNode *pelmProp = pelmMedium->createChild("Property");
    1307         pelmProp->setAttribute("name", it->first);
    1308         pelmProp->setAttribute("value", it->second);
    1309     }
    1310 
    1311     // only for base hard disks, save the type
    1312     if (depth == 1)
    1313     {
    1314         // no need to save the usual DVD/floppy medium types
    1315         if (   (   t != DVDImage
    1316                 || (   mdm.hdType != MediumType_Writethrough // shouldn't happen
    1317                     && mdm.hdType != MediumType_Readonly))
    1318             && (   t != FloppyImage
    1319                 || mdm.hdType != MediumType_Writethrough))
    1320         {
    1321             const char *pcszType =
    1322                 mdm.hdType == MediumType_Normal ? "Normal" :
    1323                 mdm.hdType == MediumType_Immutable ? "Immutable" :
    1324                 mdm.hdType == MediumType_Writethrough ? "Writethrough" :
    1325                 mdm.hdType == MediumType_Shareable ? "Shareable" :
    1326                 mdm.hdType == MediumType_Readonly ? "Readonly" :
    1327                 mdm.hdType == MediumType_MultiAttach ? "MultiAttach" :
    1328                 "INVALID";
    1329             pelmMedium->setAttribute("type", pcszType);
    1330         }
    1331     }
    1332 
    1333     for (MediaList::const_iterator it = mdm.llChildren.begin();
    1334          it != mdm.llChildren.end();
    1335          ++it)
    1336     {
    1337         // recurse for children
    1338         buildMedium(t,              // device type
    1339                     depth + 1,      // depth
    1340                     *pelmMedium,    // parent
    1341                     *it);           // settings::Medium
     1281                                 const Medium &med)
     1282{
     1283    std::list<const Medium *> llSettingsTodo;
     1284    llSettingsTodo.push_back(&med);
     1285    std::list<xml::ElementNode *> llElementsTodo;
     1286    llElementsTodo.push_back(&elmMedium);
     1287    std::list<uint32_t> llDepthsTodo;
     1288    llDepthsTodo.push_back(1);
     1289
     1290    while (llSettingsTodo.size() > 0)
     1291    {
     1292        const Medium *pMed = llSettingsTodo.front();
     1293        llSettingsTodo.pop_front();
     1294        xml::ElementNode *pElement = llElementsTodo.front();
     1295        llElementsTodo.pop_front();
     1296        uint32_t depth = llDepthsTodo.front();
     1297        llDepthsTodo.pop_front();
     1298
     1299        if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
     1300            throw ConfigFileError(this, pElement, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
     1301
     1302        xml::ElementNode *pelmMedium;
     1303
     1304        if (t == HardDisk)
     1305            pelmMedium = pElement->createChild("HardDisk");
     1306        else
     1307            pelmMedium = pElement->createChild("Image");
     1308
     1309        pelmMedium->setAttribute("uuid", pMed->uuid.toStringCurly());
     1310
     1311        pelmMedium->setAttributePath("location", pMed->strLocation);
     1312
     1313        if (t == HardDisk || RTStrICmp(pMed->strFormat.c_str(), "RAW"))
     1314            pelmMedium->setAttribute("format", pMed->strFormat);
     1315        if (   t == HardDisk
     1316            && pMed->fAutoReset)
     1317            pelmMedium->setAttribute("autoReset", pMed->fAutoReset);
     1318        if (pMed->strDescription.length())
     1319            pelmMedium->createChild("Description")->addContent(pMed->strDescription);
     1320
     1321        for (StringsMap::const_iterator it = pMed->properties.begin();
     1322             it != pMed->properties.end();
     1323             ++it)
     1324        {
     1325            xml::ElementNode *pelmProp = pelmMedium->createChild("Property");
     1326            pelmProp->setAttribute("name", it->first);
     1327            pelmProp->setAttribute("value", it->second);
     1328        }
     1329
     1330        // only for base hard disks, save the type
     1331        if (depth == 1)
     1332        {
     1333            // no need to save the usual DVD/floppy medium types
     1334            if (   (   t != DVDImage
     1335                    || (   pMed->hdType != MediumType_Writethrough // shouldn't happen
     1336                        && pMed->hdType != MediumType_Readonly))
     1337                && (   t != FloppyImage
     1338                    || pMed->hdType != MediumType_Writethrough))
     1339            {
     1340                const char *pcszType =
     1341                    pMed->hdType == MediumType_Normal ? "Normal" :
     1342                    pMed->hdType == MediumType_Immutable ? "Immutable" :
     1343                    pMed->hdType == MediumType_Writethrough ? "Writethrough" :
     1344                    pMed->hdType == MediumType_Shareable ? "Shareable" :
     1345                    pMed->hdType == MediumType_Readonly ? "Readonly" :
     1346                    pMed->hdType == MediumType_MultiAttach ? "MultiAttach" :
     1347                    "INVALID";
     1348                pelmMedium->setAttribute("type", pcszType);
     1349            }
     1350        }
     1351
     1352        /* save all children */
     1353        MediaList::const_iterator itBegin = pMed->llChildren.begin();
     1354        MediaList::const_iterator itEnd = pMed->llChildren.end();
     1355        for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
     1356        {
     1357            llSettingsTodo.push_back(&*it);
     1358            llElementsTodo.push_back(pelmMedium);
     1359            llDepthsTodo.push_back(depth + 1);
     1360        }
    13421361    }
    13431362}
     
    13691388             ++it)
    13701389        {
    1371             buildMedium(HardDisk, 1, *pelmHardDisks, *it);
     1390            buildMedium(HardDisk, *pelmHardDisks, *it);
    13721391        }
    13731392    }
     
    13801399             ++it)
    13811400        {
    1382             buildMedium(DVDImage, 1, *pelmDVDImages, *it);
     1401            buildMedium(DVDImage, *pelmDVDImages, *it);
    13831402        }
    13841403    }
     
    13911410             ++it)
    13921411        {
    1393             buildMedium(FloppyImage, 1, *pelmFloppyImages, *it);
     1412            buildMedium(FloppyImage, *pelmFloppyImages, *it);
    13941413        }
    13951414    }
     
    25892608
    25902609    clearDocument();
     2610    LogRel(("Finished saving settings file \"%s\"\n", m->strFilename.c_str()));
    25912611}
    25922612
     
    57505770 * Called for reading the \<Teleporter\> element under \<Machine\>.
    57515771 */
    5752 void MachineConfigFile::readTeleporter(const xml::ElementNode *pElmTeleporter,
    5753                                        MachineUserData *pUserData)
    5754 {
    5755     pElmTeleporter->getAttributeValue("enabled", pUserData->fTeleporterEnabled);
    5756     pElmTeleporter->getAttributeValue("port", pUserData->uTeleporterPort);
    5757     pElmTeleporter->getAttributeValue("address", pUserData->strTeleporterAddress);
    5758     pElmTeleporter->getAttributeValue("password", pUserData->strTeleporterPassword);
    5759 
    5760     if (   pUserData->strTeleporterPassword.isNotEmpty()
    5761         && !VBoxIsPasswordHashed(&pUserData->strTeleporterPassword))
    5762         VBoxHashPassword(&pUserData->strTeleporterPassword);
     5772void MachineConfigFile::readTeleporter(const xml::ElementNode &elmTeleporter,
     5773                                       MachineUserData &userData)
     5774{
     5775    elmTeleporter.getAttributeValue("enabled", userData.fTeleporterEnabled);
     5776    elmTeleporter.getAttributeValue("port", userData.uTeleporterPort);
     5777    elmTeleporter.getAttributeValue("address", userData.strTeleporterAddress);
     5778    elmTeleporter.getAttributeValue("password", userData.strTeleporterPassword);
     5779
     5780    if (   userData.strTeleporterPassword.isNotEmpty()
     5781        && !VBoxIsPasswordHashed(&userData.strTeleporterPassword))
     5782        VBoxHashPassword(&userData.strTeleporterPassword);
    57635783}
    57645784
     
    57665786 * Called for reading the \<Debugging\> element under \<Machine\> or \<Snapshot\>.
    57675787 */
    5768 void MachineConfigFile::readDebugging(const xml::ElementNode *pElmDebugging, Debugging *pDbg)
    5769 {
    5770     if (!pElmDebugging || m->sv < SettingsVersion_v1_13)
     5788void MachineConfigFile::readDebugging(const xml::ElementNode &elmDebugging, Debugging &dbg)
     5789{
     5790    if (m->sv < SettingsVersion_v1_13)
    57715791        return;
    57725792
    5773     const xml::ElementNode * const pelmTracing = pElmDebugging->findChildElement("Tracing");
     5793    const xml::ElementNode *pelmTracing = elmDebugging.findChildElement("Tracing");
    57745794    if (pelmTracing)
    57755795    {
    5776         pelmTracing->getAttributeValue("enabled", pDbg->fTracingEnabled);
    5777         pelmTracing->getAttributeValue("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
    5778         pelmTracing->getAttributeValue("config", pDbg->strTracingConfig);
     5796        pelmTracing->getAttributeValue("enabled", dbg.fTracingEnabled);
     5797        pelmTracing->getAttributeValue("allowTracingToAccessVM", dbg.fAllowTracingToAccessVM);
     5798        pelmTracing->getAttributeValue("config", dbg.strTracingConfig);
    57795799    }
    57805800}
     
    57835803 * Called for reading the \<Autostart\> element under \<Machine\> or \<Snapshot\>.
    57845804 */
    5785 void MachineConfigFile::readAutostart(const xml::ElementNode *pElmAutostart, Autostart *pAutostart)
     5805void MachineConfigFile::readAutostart(const xml::ElementNode &elmAutostart, Autostart &autostrt)
    57865806{
    57875807    Utf8Str strAutostop;
    57885808
    5789     if (!pElmAutostart || m->sv < SettingsVersion_v1_13)
     5809    if (m->sv < SettingsVersion_v1_13)
    57905810        return;
    57915811
    5792     pElmAutostart->getAttributeValue("enabled", pAutostart->fAutostartEnabled);
    5793     pElmAutostart->getAttributeValue("delay", pAutostart->uAutostartDelay);
    5794     pElmAutostart->getAttributeValue("autostop", strAutostop);
     5812    elmAutostart.getAttributeValue("enabled", autostrt.fAutostartEnabled);
     5813    elmAutostart.getAttributeValue("delay", autostrt.uAutostartDelay);
     5814    elmAutostart.getAttributeValue("autostop", strAutostop);
    57955815    if (strAutostop == "Disabled")
    5796         pAutostart->enmAutostopType = AutostopType_Disabled;
     5816        autostrt.enmAutostopType = AutostopType_Disabled;
    57975817    else if (strAutostop == "SaveState")
    5798         pAutostart->enmAutostopType = AutostopType_SaveState;
     5818        autostrt.enmAutostopType = AutostopType_SaveState;
    57995819    else if (strAutostop == "PowerOff")
    5800         pAutostart->enmAutostopType = AutostopType_PowerOff;
     5820        autostrt.enmAutostopType = AutostopType_PowerOff;
    58015821    else if (strAutostop == "AcpiShutdown")
    5802         pAutostart->enmAutostopType = AutostopType_AcpiShutdown;
     5822        autostrt.enmAutostopType = AutostopType_AcpiShutdown;
    58035823    else
    5804         throw ConfigFileError(this, pElmAutostart, N_("Invalid value '%s' for Autostart/@autostop attribute"), strAutostop.c_str());
     5824        throw ConfigFileError(this, &elmAutostart, N_("Invalid value '%s' for Autostart/@autostop attribute"), strAutostop.c_str());
    58055825}
    58065826
     
    58085828 * Called for reading the \<Groups\> element under \<Machine\>.
    58095829 */
    5810 void MachineConfigFile::readGroups(const xml::ElementNode *pElmGroups, StringsList *pllGroups)
    5811 {
    5812     pllGroups->clear();
    5813     if (!pElmGroups || m->sv < SettingsVersion_v1_13)
    5814     {
    5815         pllGroups->push_back("/");
     5830void MachineConfigFile::readGroups(const xml::ElementNode &elmGroups, StringsList &llGroups)
     5831{
     5832    llGroups.clear();
     5833    if (m->sv < SettingsVersion_v1_13)
     5834    {
     5835        llGroups.push_back("/");
    58165836        return;
    58175837    }
    58185838
    5819     xml::NodesLoop nlGroups(*pElmGroups);
     5839    xml::NodesLoop nlGroups(elmGroups);
    58205840    const xml::ElementNode *pelmGroup;
    58215841    while ((pelmGroup = nlGroups.forAllNodes()))
     
    58265846            if (!pelmGroup->getAttributeValue("name", strGroup))
    58275847                throw ConfigFileError(this, pelmGroup, N_("Required Group/@name attribute is missing"));
    5828             pllGroups->push_back(strGroup);
     5848            llGroups.push_back(strGroup);
    58295849        }
    58305850    }
     
    58345854 * Called initially for the \<Snapshot\> element under \<Machine\>, if present,
    58355855 * to store the snapshot's data into the given Snapshot structure (which is
    5836  * then the one in the Machine struct). This might then recurse if
    5837  * a \<Snapshots\> (plural) element is found in the snapshot, which should
    5838  * contain a list of child snapshots; such lists are maintained in the
    5839  * Snapshot structure.
     5856 * then the one in the Machine struct). This might process further elements
     5857 * of the snapshot tree if a \<Snapshots\> (plural) element is found in the
     5858 * snapshot, which should contain a list of child snapshots; such lists are
     5859 * maintained in the Snapshot structure.
    58405860 *
    58415861 * @param curSnapshotUuid
    5842  * @param depth
    58435862 * @param elmSnapshot
    58445863 * @param snap
     
    58465865 */
    58475866bool MachineConfigFile::readSnapshot(const Guid &curSnapshotUuid,
    5848                                      uint32_t depth,
    58495867                                     const xml::ElementNode &elmSnapshot,
    58505868                                     Snapshot &snap)
    58515869{
    5852     if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
    5853         throw ConfigFileError(this, &elmSnapshot, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
    5854 
    5855     Utf8Str strTemp;
    5856 
    5857     if (!elmSnapshot.getAttributeValue("uuid", strTemp))
    5858         throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
    5859     parseUUID(snap.uuid, strTemp, &elmSnapshot);
    5860     bool foundCurrentSnapshot = (snap.uuid == curSnapshotUuid);
    5861 
    5862     if (!elmSnapshot.getAttributeValue("name", snap.strName))
    5863         throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
    5864 
    5865     // 3.1 dev builds added Description as an attribute, read it silently
    5866     // and write it back as an element
    5867     elmSnapshot.getAttributeValue("Description", snap.strDescription);
    5868 
    5869     if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
    5870         throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
    5871     parseTimestamp(snap.timestamp, strTemp, &elmSnapshot);
    5872 
    5873     elmSnapshot.getAttributeValuePath("stateFile", snap.strStateFile);      // online snapshots only
    5874 
    5875     // parse Hardware before the other elements because other things depend on it
    5876     const xml::ElementNode *pelmHardware;
    5877     if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
    5878         throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
    5879     readHardware(*pelmHardware, snap.hardware);
    5880 
    5881     xml::NodesLoop nlSnapshotChildren(elmSnapshot);
    5882     const xml::ElementNode *pelmSnapshotChild;
    5883     while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
    5884     {
    5885         if (pelmSnapshotChild->nameEquals("Description"))
    5886             snap.strDescription = pelmSnapshotChild->getValue();
    5887         else if (   m->sv < SettingsVersion_v1_7
    5888                  && pelmSnapshotChild->nameEquals("HardDiskAttachments"))
    5889             readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.hardware.storage);
    5890         else if (   m->sv >= SettingsVersion_v1_7
    5891                  && pelmSnapshotChild->nameEquals("StorageControllers"))
    5892             readStorageControllers(*pelmSnapshotChild, snap.hardware.storage);
    5893         else if (pelmSnapshotChild->nameEquals("Snapshots"))
    5894         {
    5895             xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
     5870    std::list<const xml::ElementNode *> llElementsTodo;
     5871    llElementsTodo.push_back(&elmSnapshot);
     5872    std::list<Snapshot *> llSettingsTodo;
     5873    llSettingsTodo.push_back(&snap);
     5874    std::list<uint32_t> llDepthsTodo;
     5875    llDepthsTodo.push_back(1);
     5876
     5877    bool foundCurrentSnapshot = false;
     5878
     5879    while (llElementsTodo.size() > 0)
     5880    {
     5881        const xml::ElementNode *pElement = llElementsTodo.front();
     5882        llElementsTodo.pop_front();
     5883        Snapshot *pSnap = llSettingsTodo.front();
     5884        llSettingsTodo.pop_front();
     5885        uint32_t depth = llDepthsTodo.front();
     5886        llDepthsTodo.pop_front();
     5887
     5888        if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
     5889            throw ConfigFileError(this, pElement, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
     5890
     5891        Utf8Str strTemp;
     5892        if (!pElement->getAttributeValue("uuid", strTemp))
     5893            throw ConfigFileError(this, pElement, N_("Required Snapshot/@uuid attribute is missing"));
     5894        parseUUID(pSnap->uuid, strTemp, pElement);
     5895        foundCurrentSnapshot |= (pSnap->uuid == curSnapshotUuid);
     5896
     5897        if (!pElement->getAttributeValue("name", pSnap->strName))
     5898            throw ConfigFileError(this, pElement, N_("Required Snapshot/@name attribute is missing"));
     5899
     5900        // 3.1 dev builds added Description as an attribute, read it silently
     5901        // and write it back as an element
     5902        pElement->getAttributeValue("Description", pSnap->strDescription);
     5903
     5904        if (!pElement->getAttributeValue("timeStamp", strTemp))
     5905            throw ConfigFileError(this, pElement, N_("Required Snapshot/@timeStamp attribute is missing"));
     5906        parseTimestamp(pSnap->timestamp, strTemp, pElement);
     5907
     5908        pElement->getAttributeValuePath("stateFile", pSnap->strStateFile);      // online snapshots only
     5909
     5910        // parse Hardware before the other elements because other things depend on it
     5911        const xml::ElementNode *pelmHardware;
     5912        if (!(pelmHardware = pElement->findChildElement("Hardware")))
     5913            throw ConfigFileError(this, pElement, N_("Required Snapshot/@Hardware element is missing"));
     5914        readHardware(*pelmHardware, pSnap->hardware);
     5915
     5916        const xml::ElementNode *pelmSnapshots = NULL;
     5917
     5918        xml::NodesLoop nlSnapshotChildren(*pElement);
     5919        const xml::ElementNode *pelmSnapshotChild;
     5920        while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
     5921        {
     5922            if (pelmSnapshotChild->nameEquals("Description"))
     5923                pSnap->strDescription = pelmSnapshotChild->getValue();
     5924            else if (   m->sv < SettingsVersion_v1_7
     5925                    && pelmSnapshotChild->nameEquals("HardDiskAttachments"))
     5926                readHardDiskAttachments_pre1_7(*pelmSnapshotChild, pSnap->hardware.storage);
     5927            else if (   m->sv >= SettingsVersion_v1_7
     5928                    && pelmSnapshotChild->nameEquals("StorageControllers"))
     5929                readStorageControllers(*pelmSnapshotChild, pSnap->hardware.storage);
     5930            else if (pelmSnapshotChild->nameEquals("Snapshots"))
     5931            {
     5932                if (pelmSnapshots)
     5933                    throw ConfigFileError(this, pelmSnapshotChild, N_("Just a single Snapshots element is allowed"));
     5934                pelmSnapshots = pelmSnapshotChild;
     5935            }
     5936        }
     5937
     5938        if (m->sv < SettingsVersion_v1_9)
     5939            // go through Hardware once more to repair the settings controller structures
     5940            // with data from old DVDDrive and FloppyDrive elements
     5941            readDVDAndFloppies_pre1_9(*pelmHardware, pSnap->hardware.storage);
     5942
     5943        const xml::ElementNode *pelmDebugging = elmSnapshot.findChildElement("Debugging");
     5944        if (pelmDebugging)
     5945            readDebugging(*pelmDebugging, pSnap->debugging);
     5946        const xml::ElementNode *pelmAutostart = elmSnapshot.findChildElement("Autostart");
     5947        if (pelmAutostart)
     5948            readAutostart(*pelmAutostart, pSnap->autostart);
     5949        // note: Groups exist only for Machine, not for Snapshot
     5950
     5951        // process all child snapshots
     5952        if (pelmSnapshots)
     5953        {
     5954            xml::NodesLoop nlChildSnapshots(*pelmSnapshots);
    58965955            const xml::ElementNode *pelmChildSnapshot;
    58975956            while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
     
    58995958                if (pelmChildSnapshot->nameEquals("Snapshot"))
    59005959                {
    5901                     // recurse with this element and put the child at the
    5902                     // end of the list. XPCOM has very small stack, avoid
    5903                     // big local variables and use the list element.
    5904                     snap.llChildSnapshots.push_back(Snapshot::Empty);
    5905                     bool found = readSnapshot(curSnapshotUuid, depth + 1, *pelmChildSnapshot, snap.llChildSnapshots.back());
    5906                     foundCurrentSnapshot = foundCurrentSnapshot || found;
     5960                    llElementsTodo.push_back(pelmChildSnapshot);
     5961                    pSnap->llChildSnapshots.push_back(Snapshot::Empty);
     5962                    llSettingsTodo.push_back(&pSnap->llChildSnapshots.back());
     5963                    llDepthsTodo.push_back(depth + 1);
    59075964                }
    59085965            }
    59095966        }
    59105967    }
    5911 
    5912     if (m->sv < SettingsVersion_v1_9)
    5913         // go through Hardware once more to repair the settings controller structures
    5914         // with data from old DVDDrive and FloppyDrive elements
    5915         readDVDAndFloppies_pre1_9(*pelmHardware, snap.hardware.storage);
    5916 
    5917     readDebugging(elmSnapshot.findChildElement("Debugging"), &snap.debugging);
    5918     readAutostart(elmSnapshot.findChildElement("Autostart"), &snap.autostart);
    5919     // note: Groups exist only for Machine, not for Snapshot
    59205968
    59215969    return foundCurrentSnapshot;
     
    60626110                    throw ConfigFileError(this, &elmMachine, N_("Snapshots present but required Machine/@currentSnapshot attribute is missing"));
    60636111                bool foundCurrentSnapshot = false;
    6064                 Snapshot snap;
    6065                 // this will recurse into child snapshots, if necessary
    6066                 foundCurrentSnapshot = readSnapshot(uuidCurrentSnapshot, 1, *pelmMachineChild, snap);
     6112                // Work directly with the target list, because otherwise
     6113                // the entire snapshot settings tree will need to be copied,
     6114                // and the usual STL implementation needs a lot of stack space.
     6115                llFirstSnapshot.push_back(Snapshot::Empty);
     6116                // this will also read all child snapshots
     6117                foundCurrentSnapshot = readSnapshot(uuidCurrentSnapshot, *pelmMachineChild, llFirstSnapshot.back());
    60676118                if (!foundCurrentSnapshot)
    60686119                    throw ConfigFileError(this, &elmMachine, N_("Snapshots present but none matches the UUID in the Machine/@currentSnapshot attribute"));
    6069                 llFirstSnapshot.push_back(snap);
    60706120            }
    60716121            else if (pelmMachineChild->nameEquals("Description"))
    60726122                machineUserData.strDescription = pelmMachineChild->getValue();
    60736123            else if (pelmMachineChild->nameEquals("Teleporter"))
    6074                 readTeleporter(pelmMachineChild, &machineUserData);
     6124                readTeleporter(*pelmMachineChild, machineUserData);
    60756125            else if (pelmMachineChild->nameEquals("MediaRegistry"))
    60766126                readMediaRegistry(*pelmMachineChild, mediaRegistry);
    60776127            else if (pelmMachineChild->nameEquals("Debugging"))
    6078                 readDebugging(pelmMachineChild, &debugging);
     6128                readDebugging(*pelmMachineChild, debugging);
    60796129            else if (pelmMachineChild->nameEquals("Autostart"))
    6080                 readAutostart(pelmMachineChild, &autostart);
     6130                readAutostart(*pelmMachineChild, autostart);
    60816131            else if (pelmMachineChild->nameEquals("Groups"))
    6082                 readGroups(pelmMachineChild, &machineUserData.llGroups);
     6132                readGroups(*pelmMachineChild, machineUserData.llGroups);
    60836133        }
    60846134
     
    76047654 * keys under that. Called for both the \<Machine\> node and for snapshots.
    76057655 *
    7606  * @param pElmParent    Pointer to the parent element.
    7607  * @param pDbg          Pointer to the debugging settings.
    7608  */
    7609 void MachineConfigFile::buildDebuggingXML(xml::ElementNode *pElmParent, const Debugging *pDbg)
    7610 {
    7611     if (m->sv < SettingsVersion_v1_13 || pDbg->areDefaultSettings())
     7656 * @param elmParent     Parent element.
     7657 * @param dbg           Debugging settings.
     7658 */
     7659void MachineConfigFile::buildDebuggingXML(xml::ElementNode &elmParent, const Debugging &dbg)
     7660{
     7661    if (m->sv < SettingsVersion_v1_13 || dbg.areDefaultSettings())
    76127662        return;
    76137663
    7614     xml::ElementNode *pElmDebugging = pElmParent->createChild("Debugging");
     7664    xml::ElementNode *pElmDebugging = elmParent.createChild("Debugging");
    76157665    xml::ElementNode *pElmTracing   = pElmDebugging->createChild("Tracing");
    7616     pElmTracing->setAttribute("enabled", pDbg->fTracingEnabled);
    7617     pElmTracing->setAttribute("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
    7618     pElmTracing->setAttribute("config", pDbg->strTracingConfig);
     7666    pElmTracing->setAttribute("enabled", dbg.fTracingEnabled);
     7667    pElmTracing->setAttribute("allowTracingToAccessVM", dbg.fAllowTracingToAccessVM);
     7668    pElmTracing->setAttribute("config", dbg.strTracingConfig);
    76197669}
    76207670
     
    76237673 * keys under that. Called for both the \<Machine\> node and for snapshots.
    76247674 *
    7625  * @param pElmParent    Pointer to the parent element.
    7626  * @param pAutostart    Pointer to the autostart settings.
    7627  */
    7628 void MachineConfigFile::buildAutostartXML(xml::ElementNode *pElmParent, const Autostart *pAutostart)
     7675 * @param elmParent     Parent element.
     7676 * @param autostrt      Autostart settings.
     7677 */
     7678void MachineConfigFile::buildAutostartXML(xml::ElementNode &elmParent, const Autostart &autostrt)
    76297679{
    76307680    const char *pcszAutostop = NULL;
    76317681
    7632     if (m->sv < SettingsVersion_v1_13 || pAutostart->areDefaultSettings())
     7682    if (m->sv < SettingsVersion_v1_13 || autostrt.areDefaultSettings())
    76337683        return;
    76347684
    7635     xml::ElementNode *pElmAutostart = pElmParent->createChild("Autostart");
    7636     pElmAutostart->setAttribute("enabled", pAutostart->fAutostartEnabled);
    7637     pElmAutostart->setAttribute("delay", pAutostart->uAutostartDelay);
    7638 
    7639     switch (pAutostart->enmAutostopType)
     7685    xml::ElementNode *pElmAutostart = elmParent.createChild("Autostart");
     7686    pElmAutostart->setAttribute("enabled", autostrt.fAutostartEnabled);
     7687    pElmAutostart->setAttribute("delay", autostrt.uAutostartDelay);
     7688
     7689    switch (autostrt.enmAutostopType)
    76407690    {
    76417691        case AutostopType_Disabled:     pcszAutostop = "Disabled";     break;
     
    76527702 * keys under that. Called for the \<Machine\> node only.
    76537703 *
    7654  * @param pElmParent    Pointer to the parent element.
    7655  * @param pllGroups     Pointer to the groups list.
    7656  */
    7657 void MachineConfigFile::buildGroupsXML(xml::ElementNode *pElmParent, const StringsList *pllGroups)
    7658 {
    7659     if (   m->sv < SettingsVersion_v1_13 || pllGroups->size() == 0
    7660         || (pllGroups->size() == 1 && pllGroups->front() == "/"))
     7704 * @param elmParent     Parent element.
     7705 * @param llGroups      Groups list.
     7706 */
     7707void MachineConfigFile::buildGroupsXML(xml::ElementNode &elmParent, const StringsList &llGroups)
     7708{
     7709    if (   m->sv < SettingsVersion_v1_13 || llGroups.size() == 0
     7710        || (llGroups.size() == 1 && llGroups.front() == "/"))
    76617711        return;
    76627712
    7663     xml::ElementNode *pElmGroups = pElmParent->createChild("Groups");
    7664     for (StringsList::const_iterator it = pllGroups->begin();
    7665          it != pllGroups->end();
     7713    xml::ElementNode *pElmGroups = elmParent.createChild("Groups");
     7714    for (StringsList::const_iterator it = llGroups.begin();
     7715         it != llGroups.end();
    76667716         ++it)
    76677717    {
     
    76737723
    76747724/**
    7675  * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
    7676  * for the root snapshot of a machine, if present; elmParent then points to the \<Snapshots\> node under the
    7677  * \<Machine\> node to which \<Snapshot\> must be added. This may then recurse for child snapshots.
     7725 * Writes a single snapshot into the DOM tree. Initially this gets called from
     7726 * MachineConfigFile::write() for the root snapshot of a machine, if present;
     7727 * elmParent then points to the \<Snapshots\> node under the \<Machine\> node
     7728 * to which \<Snapshot\> must be added. This may then continue processing the
     7729 * child snapshots.
    76787730 *
    7679  * @param depth
    76807731 * @param elmParent
    76817732 * @param snap
    76827733 */
    7683 void MachineConfigFile::buildSnapshotXML(uint32_t depth,
    7684                                          xml::ElementNode &elmParent,
     7734void MachineConfigFile::buildSnapshotXML(xml::ElementNode &elmParent,
    76857735                                         const Snapshot &snap)
    76867736{
    7687     if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
    7688         throw ConfigFileError(this, NULL, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
    7689 
    7690     xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
    7691 
    7692     pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
    7693     pelmSnapshot->setAttribute("name", snap.strName);
    7694     pelmSnapshot->setAttribute("timeStamp", stringifyTimestamp(snap.timestamp));
    7695 
    7696     if (snap.strStateFile.length())
    7697         pelmSnapshot->setAttributePath("stateFile", snap.strStateFile);
    7698 
    7699     if (snap.strDescription.length())
    7700         pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
    7701 
    7702     // We only skip removable media for OVF, but OVF never includes snapshots.
    7703     buildHardwareXML(*pelmSnapshot, snap.hardware, 0 /* fl */, NULL /* pllElementsWithUuidAttributes */);
    7704     buildDebuggingXML(pelmSnapshot, &snap.debugging);
    7705     buildAutostartXML(pelmSnapshot, &snap.autostart);
    7706     // note: Groups exist only for Machine, not for Snapshot
    7707 
    7708     if (snap.llChildSnapshots.size())
    7709     {
    7710         xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
    7711         for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
    7712              it != snap.llChildSnapshots.end();
    7713              ++it)
    7714         {
    7715             const Snapshot &child = *it;
    7716             buildSnapshotXML(depth + 1, *pelmChildren, child);
     7737    std::list<const Snapshot *> llSettingsTodo;
     7738    llSettingsTodo.push_back(&snap);
     7739    std::list<xml::ElementNode *> llElementsTodo;
     7740    llElementsTodo.push_back(&elmParent);
     7741    std::list<uint32_t> llDepthsTodo;
     7742    llDepthsTodo.push_back(1);
     7743
     7744    while (llSettingsTodo.size() > 0)
     7745    {
     7746        const Snapshot *pSnap = llSettingsTodo.front();
     7747        llSettingsTodo.pop_front();
     7748        xml::ElementNode *pElement = llElementsTodo.front();
     7749        llElementsTodo.pop_front();
     7750        uint32_t depth = llDepthsTodo.front();
     7751        llDepthsTodo.pop_front();
     7752
     7753        if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
     7754            throw ConfigFileError(this, NULL, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
     7755
     7756        xml::ElementNode *pelmSnapshot = pElement->createChild("Snapshot");
     7757
     7758        pelmSnapshot->setAttribute("uuid", pSnap->uuid.toStringCurly());
     7759        pelmSnapshot->setAttribute("name", pSnap->strName);
     7760        pelmSnapshot->setAttribute("timeStamp", stringifyTimestamp(pSnap->timestamp));
     7761
     7762        if (pSnap->strStateFile.length())
     7763            pelmSnapshot->setAttributePath("stateFile", pSnap->strStateFile);
     7764
     7765        if (pSnap->strDescription.length())
     7766            pelmSnapshot->createChild("Description")->addContent(pSnap->strDescription);
     7767
     7768        // We only skip removable media for OVF, but OVF never includes snapshots.
     7769        buildHardwareXML(*pelmSnapshot, pSnap->hardware, 0 /* fl */, NULL /* pllElementsWithUuidAttributes */);
     7770        buildDebuggingXML(*pelmSnapshot, pSnap->debugging);
     7771        buildAutostartXML(*pelmSnapshot, pSnap->autostart);
     7772        // note: Groups exist only for Machine, not for Snapshot
     7773
     7774        if (pSnap->llChildSnapshots.size())
     7775        {
     7776            xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
     7777            for (SnapshotsList::const_iterator it = pSnap->llChildSnapshots.begin();
     7778                it != pSnap->llChildSnapshots.end();
     7779                ++it)
     7780            {
     7781                llSettingsTodo.push_back(&*it);
     7782                llElementsTodo.push_back(pelmChildren);
     7783                llDepthsTodo.push_back(depth + 1);
     7784            }
    77177785        }
    77187786    }
     
    78507918    if (    (fl & BuildMachineXML_IncludeSnapshots)
    78517919         && llFirstSnapshot.size())
    7852         buildSnapshotXML(1, elmMachine, llFirstSnapshot.front());
     7920        buildSnapshotXML(elmMachine, llFirstSnapshot.front());
    78537921
    78547922    buildHardwareXML(elmMachine, hardwareMachine, fl, pllElementsWithUuidAttributes);
    7855     buildDebuggingXML(&elmMachine, &debugging);
    7856     buildAutostartXML(&elmMachine, &autostart);
    7857     buildGroupsXML(&elmMachine, &machineUserData.llGroups);
     7923    buildDebuggingXML(elmMachine, debugging);
     7924    buildAutostartXML(elmMachine, autostart);
     7925    buildGroupsXML(elmMachine, machineUserData.llGroups);
    78587926}
    78597927
     
    86508718        m->fFileExists = true;
    86518719        clearDocument();
     8720        LogRel(("Finished saving settings file \"%s\"\n", m->strFilename.c_str()));
    86528721    }
    86538722    catch (...)
    86548723    {
    86558724        clearDocument();
     8725        LogRel(("Finished saving settings file \"%s\" with failure\n", m->strFilename.c_str()));
    86568726        throw;
    86578727    }
  • trunk/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py

    r93115 r94598  
    3434import os
    3535import sys
     36import random
    3637
    3738# Only the main script needs to modify the path.
     
    7374
    7475        try:
     76            oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox()
    7577            oVM = self.oTstDrv.createTestVM('test-medium', 1, None, 4)
    7678            assert oVM is not None
     
    7981            fRc = True
    8082            oSession = self.oTstDrv.openSession(oVM)
    81             for i in range(1, 301):
     83            cImages = 38 #00
     84            for i in range(1, cImages + 1):
    8285                sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'Test' + str(i) + '.vdi')
    8386                if i == 1:
     
    9396            fRc = fRc and oSession.saveSettings()
    9497            fRc = oSession.close() and fRc
    95 
    96             # unregister and re-register to test loading of settings
     98            ## @todo r=klaus: count known hard disk images, should be cImages
     99
     100            # unregister, making sure the images are closed
    97101            sSettingsFile = oVM.settingsFilePath
    98             reporter.log('unregistering VM')
    99             oVM.unregister(vboxcon.CleanupMode_DetachAllReturnNone)
    100             oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox()
     102            fDetachAll = random.choice([False, True])
     103            if fDetachAll:
     104                reporter.log('unregistering VM, DetachAll style')
     105            else:
     106                reporter.log('unregistering VM, UnregisterOnly style')
     107            self.oTstDrv.forgetTestMachine(oVM)
     108            if fDetachAll:
     109                aoHDs = oVM.unregister(vboxcon.CleanupMode_DetachAllReturnHardDisksOnly)
     110                for oHD in aoHDs:
     111                    oHD.close()
     112                aoHDs = None
     113            else:
     114                oVM.unregister(vboxcon.CleanupMode_UnregisterOnly)
     115            oVM = None
     116           
     117            # If there is no base image (expected) then there are no leftover
     118            # child images either. Can be changed later once the todos above
     119            # and below are resolved.
     120            cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
     121            reporter.log('API reports %i base images' % (cBaseImages))
     122            fRc = fRc and cBaseImages == 0
     123
     124            # re-register to test loading of settings
    101125            reporter.log('opening VM %s, testing config reading' % (sSettingsFile))
    102126            oVM = oVBox.openMachine(sSettingsFile)
     127            ## @todo r=klaus: count known hard disk images, should be cImages
     128
     129            reporter.log('unregistering VM')
     130            oVM.unregister(vboxcon.CleanupMode_UnregisterOnly)
     131            oVM = None
     132
     133            cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
     134            reporter.log('API reports %i base images' % (cBaseImages))
     135            fRc = fRc and cBaseImages == 0
    103136
    104137            assert fRc is True
     
    115148
    116149        try:
     150            oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox()
    117151            oVM = self.oTstDrv.createTestVM('test-snap', 1, None, 4)
    118152            assert oVM is not None
     
    126160
    127161            # take 250 snapshots (snapshot tree depth limit)
    128             for i in range(1, 251):
     162            cSnapshots = 13 #00
     163            for i in range(1, cSnapshots + 1):
    129164                fRc = fRc and oSession.takeSnapshot('Snapshot ' + str(i))
    130165            fRc = oSession.close() and fRc
    131 
    132             # unregister and re-register to test loading of settings
     166            oSession = None
     167            reporter.log('API reports %i snapshots' % (oVM.snapshotCount))
     168            fRc = fRc and oVM.snapshotCount == cSnapshots
     169
     170            assert fRc is True
     171
     172            # unregister, making sure the images are closed
    133173            sSettingsFile = oVM.settingsFilePath
    134             reporter.log('unregistering VM')
    135             oVM.unregister(vboxcon.CleanupMode_DetachAllReturnNone)
    136             oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox()
     174            fDetachAll = random.choice([False, True])
     175            if fDetachAll:
     176                reporter.log('unregistering VM, DetachAll style')
     177            else:
     178                reporter.log('unregistering VM, UnregisterOnly style')
     179            self.oTstDrv.forgetTestMachine(oVM)
     180            if fDetachAll:
     181                aoHDs = oVM.unregister(vboxcon.CleanupMode_DetachAllReturnHardDisksOnly)
     182                for oHD in aoHDs:
     183                    oHD.close()
     184                aoHDs = None
     185            else:
     186                oVM.unregister(vboxcon.CleanupMode_UnregisterOnly)
     187            oVM = None
     188
     189            # If there is no base image (expected) then there are no leftover
     190            # child images either. Can be changed later once the todos above
     191            # and below are resolved.
     192            cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
     193            reporter.log('API reports %i base images' % (cBaseImages))
     194            fRc = fRc and cBaseImages == 0
     195
     196            # re-register to test loading of settings
    137197            reporter.log('opening VM %s, testing config reading' % (sSettingsFile))
    138198            oVM = oVBox.openMachine(sSettingsFile)
     199            reporter.log('API reports %i snapshots' % (oVM.snapshotCount))
     200            fRc = fRc and oVM.snapshotCount == cSnapshots
     201
     202            reporter.log('unregistering VM')
     203            oVM.unregister(vboxcon.CleanupMode_UnregisterOnly)
     204            oVM = None
     205
     206            cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
     207            reporter.log('API reports %i base images' % (cBaseImages))
     208            fRc = fRc and cBaseImages == 0
    139209
    140210            assert fRc is True
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