VirtualBox

Changeset 54948 in vbox


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

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

Location:
trunk
Files:
10 edited

Legend:

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

    r54584 r54948  
    1818
    1919/*
    20  * Copyright (C) 2007-2014 Oracle Corporation
     20 * Copyright (C) 2007-2015 Oracle Corporation
    2121 *
    2222 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    5252
    5353/**
     54 * Maximum depth of a medium tree, to prevent stack overflows.
     55 * XPCOM has a relatively low stack size for its workers, and we have
     56 * to avoid crashes due to exceeding the limit both on reading and
     57 * writing config files.
     58 */
     59#define SETTINGS_MEDIUM_DEPTH_MAX 300
     60
     61/**
    5462 * Maximum depth of the snapshot tree, to prevent stack overflows.
    5563 * XPCOM has a relatively low stack size for its workers, and we have
    5664 * to avoid crashes due to exceeding the limit both on reading and
    57  * writing config files.
     65 * writing config files. The bottleneck is reading config files with
     66 * deep snapshot nesting, as libxml2 needs quite some stack space,
     67 * so with the current stack size the margin isn't big.
    5868 */
    5969#define SETTINGS_SNAPSHOT_DEPTH_MAX 250
     
    144154};
    145155
     156extern const struct Medium g_MediumEmpty;
     157
    146158/**
    147159 * A media registry. Starting with VirtualBox 3.3, this can appear in both the
     
    229241    ~ConfigFileBase();
    230242
     243    typedef enum {Error, HardDisk, DVDImage, FloppyImage} MediaType;
     244
     245    static const char *stringifyMediaType(MediaType t);
    231246    void parseUUID(com::Guid &guid,
    232247                   const com::Utf8Str &strUUID) const;
    233248    void parseTimestamp(RTTIMESPEC &timestamp,
    234249                        const com::Utf8Str &str) const;
    235 
    236     com::Utf8Str makeString(const RTTIMESPEC &tm);
     250    com::Utf8Str stringifyTimestamp(const RTTIMESPEC &tm) const;
    237251
    238252    void readExtraData(const xml::ElementNode &elmExtraData,
     
    240254    void readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters,
    241255                              USBDeviceFiltersList &ll);
    242     typedef enum {Error, HardDisk, DVDImage, FloppyImage} MediaType;
    243     void readMedium(MediaType t, const xml::ElementNode &elmMedium, MediaList &llMedia);
     256    void readMediumOne(MediaType t, const xml::ElementNode &elmMedium, Medium &med);
     257    void readMedium(MediaType t, uint32_t depth, const xml::ElementNode &elmMedium, Medium &med);
    244258    void readMediaRegistry(const xml::ElementNode &elmMediaRegistry, MediaRegistry &mr);
    245259    void readNATForwardRuleList(const xml::ElementNode  &elmParent, NATRuleList &llRules);
     
    253267                               const USBDeviceFiltersList &ll,
    254268                               bool fHostMode);
    255     void buildMedium(xml::ElementNode &elmMedium,
    256                      DeviceType_T devType,
    257                      const Medium &m,
    258                      uint32_t level);
     269    void buildMedium(MediaType t,
     270                     uint32_t depth,
     271                     xml::ElementNode &elmMedium,
     272                     const Medium &mdm);
    259273    void buildMediaRegistry(xml::ElementNode &elmParent,
    260274                            const MediaRegistry &mr);
     
    11511165struct Snapshot
    11521166{
     1167    Snapshot()
     1168    {
     1169        RTTimeSpecSetNano(&timestamp, 0);
     1170    }
     1171
    11531172    bool operator==(const Snapshot &s) const;
    11541173
     
    12251244    com::Utf8Str            ovIcon;
    12261245};
     1246
     1247extern const struct Snapshot g_SnapshotEmpty;
    12271248
    12281249/**
  • trunk/src/VBox/Main/include/MediumImpl.h

    r54885 r54948  
    11/* $Id$ */
    2 
    32/** @file
    4  *
    53 * VirtualBox COM class implementation
    64 */
     
    7270
    7371    // initializer used when loading settings
     72    HRESULT initOne(Medium *aParent,
     73                    DeviceType_T aDeviceType,
     74                    const Guid &uuidMachineRegistry,
     75                    const settings::Medium &data,
     76                    const Utf8Str &strMachineFolder);
    7477    HRESULT init(VirtualBox *aVirtualBox,
    7578                 Medium *aParent,
     
    7780                 const Guid &uuidMachineRegistry,
    7881                 const settings::Medium &data,
    79                  const Utf8Str &strMachineFolder);
     82                 const Utf8Str &strMachineFolder,
     83                 AutoWriteLock &mediaTreeLock);
    8084
    8185    // initializer for host floppy/DVD
     
    109113
    110114    /* handles caller/locking itself */
    111     bool i_addRegistry(const Guid& id, bool fRecurse);
     115    bool i_addRegistry(const Guid &id);
     116    /* handles caller/locking itself, caller is responsible for tree lock */
     117    bool i_addRegistryRecursive(const Guid &id);
    112118    /* handles caller/locking itself */
    113     bool i_removeRegistry(const Guid& id, bool fRecurse);
     119    bool i_removeRegistry(const Guid& id);
     120    /* handles caller/locking itself, caller is responsible for tree lock */
     121    bool i_removeRegistryRecursive(const Guid& id);
    114122    bool i_isInRegistry(const Guid& id);
    115123    bool i_getFirstRegistryMachineId(Guid &uuid) const;
     
    135143    HRESULT i_updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath);
    136144
     145    /* handles caller/locking itself */
    137146    ComObjPtr<Medium> i_getBase(uint32_t *aLevel = NULL);
     147    /* handles caller/locking itself */
     148    uint32_t i_getDepth();
    138149
    139150    bool i_isReadOnly();
    140151    void i_updateId(const Guid &id);
    141152
     153    void i_saveSettingsOne(settings::Medium &data,
     154                           const Utf8Str &strHardDiskFolder);
    142155    HRESULT i_saveSettings(settings::Medium &data,
    143156                           const Utf8Str &strHardDiskFolder);
  • trunk/src/VBox/Main/include/SnapshotImpl.h

    r50504 r54948  
    11/* $Id$ */
    2 
    32/** @file
    4  *
    53 * VirtualBox COM class implementation
    64 */
    75
    86/*
    9  * Copyright (C) 2006-2013 Oracle Corporation
     7 * Copyright (C) 2006-2015 Oracle Corporation
    108 *
    119 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    9290                                Snapshot *pSnapshotToIgnore);
    9391
    94     HRESULT i_saveSnapshot(settings::Snapshot &data, bool aAttrsOnly);
    95     HRESULT i_saveSnapshotImpl(settings::Snapshot &data, bool aAttrsOnly);
     92    HRESULT i_saveSnapshot(settings::Snapshot &data) const;
     93    HRESULT i_saveSnapshotImpl(settings::Snapshot &data) const;
     94    HRESULT i_saveSnapshotImplOne(settings::Snapshot &data) const;
    9695
     96    HRESULT i_uninitOne(AutoWriteLock &writeLock,
     97                        CleanupMode_T cleanupMode,
     98                        MediaList &llMedia,
     99                        std::list<Utf8Str> &llFilenames);
    97100    HRESULT i_uninitRecursively(AutoWriteLock &writeLock,
    98101                                CleanupMode_T cleanupMode,
  • trunk/src/VBox/Main/include/VirtualBoxImpl.h

    r54438 r54948  
    9393    HRESULT initMachines();
    9494    HRESULT initMedia(const Guid &uuidMachineRegistry,
    95                       const settings::MediaRegistry mediaRegistry,
     95                      const settings::MediaRegistry &mediaRegistry,
    9696                      const Utf8Str &strMachineFolder);
    9797    void uninit();
     
    230230    HRESULT i_saveSettings();
    231231    void i_markRegistryModified(const Guid &uuid);
     232    void i_unmarkRegistryModified(const Guid &uuid);
    232233    void i_saveModifiedRegistries();
    233234    static const com::Utf8Str &i_getVersionNormalized();
  • trunk/src/VBox/Main/src-server/MachineImpl.cpp

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

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

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

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

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

    r54911 r54948  
    5555
    5656/*
    57  * Copyright (C) 2007-2014 Oracle Corporation
     57 * Copyright (C) 2007-2015 Oracle Corporation
    5858 *
    5959 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    120120/** VirtualBox XML settings full version string ("x.y-platform") */
    121121#define VBOX_XML_VERSION_FULL   VBOX_XML_VERSION "-" VBOX_XML_PLATFORM
     122
     123const struct Snapshot settings::g_SnapshotEmpty; /* default ctor is OK */
     124const struct Medium settings::g_MediumEmpty; /* default ctor is OK */
    122125
    123126////////////////////////////////////////////////////////////////////////////////
     
    377380
    378381/**
     382 * Helper function to convert a MediaType enum value into string from.
     383 * @param t
     384 */
     385/*static*/
     386const char *ConfigFileBase::stringifyMediaType(MediaType t)
     387{
     388    switch (t)
     389    {
     390        case HardDisk:
     391            return "hard disk";
     392        case DVDImage:
     393            return "DVD";
     394        case FloppyImage:
     395            return "floppy";
     396        default:
     397            AssertMsgFailed(("media type %d\n", t));
     398            return "UNKNOWN";
     399    }
     400}
     401
     402/**
    379403 * Helper function that parses a UUID in string form into
    380404 * a com::Guid item. Accepts UUIDs both with and without
     
    466490 * @return
    467491 */
    468 com::Utf8Str ConfigFileBase::makeString(const RTTIMESPEC &stamp)
     492com::Utf8Str ConfigFileBase::stringifyTimestamp(const RTTIMESPEC &stamp) const
    469493{
    470494    RTTIME time;
     
    566590 * @param t
    567591 * @param elmMedium
    568  * @param llMedia
    569  */
    570 void ConfigFileBase::readMedium(MediaType t,
    571                                 const xml::ElementNode &elmMedium,  // HardDisk node if root; if recursing,
    572                                                                     // child HardDisk node or DiffHardDisk node for pre-1.4
    573                                 MediaList &llMedia)     // list to append medium to (root disk or child list)
     592 * @param med
     593 */
     594void ConfigFileBase::readMediumOne(MediaType t,
     595                                   const xml::ElementNode &elmMedium,
     596                                   Medium &med)
    574597{
    575598    // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
    576     settings::Medium med;
     599
    577600    Utf8Str strUUID;
    578601    if (!elmMedium.getAttributeValue("uuid", strUUID))
     
    726749    elmMedium.getAttributeValue("Description", med.strDescription);       // optional
    727750
    728     // recurse to handle children
    729     xml::NodesLoop nl2(elmMedium);
     751    // handle medium properties
     752    xml::NodesLoop nl2(elmMedium, "Property");
    730753    const xml::ElementNode *pelmHDChild;
    731754    while ((pelmHDChild = nl2.forAllNodes()))
    732755    {
    733         if (    t == HardDisk
    734              && (    pelmHDChild->nameEquals("HardDisk")
    735                   || (    (m->sv < SettingsVersion_v1_4)
    736                        && (pelmHDChild->nameEquals("DiffHardDisk"))
    737                      )
    738                 )
    739            )
    740             // recurse with this element and push the child onto our current children list
    741             readMedium(t,
    742                         *pelmHDChild,
    743                         med.llChildren);
    744         else if (pelmHDChild->nameEquals("Property"))
    745         {
    746             Utf8Str strPropName, strPropValue;
    747             if (   pelmHDChild->getAttributeValue("name", strPropName)
    748                 && pelmHDChild->getAttributeValue("value", strPropValue) )
    749                 med.properties[strPropName] = strPropValue;
    750             else
    751                 throw ConfigFileError(this, pelmHDChild, N_("Required HardDisk/Property/@name or @value attribute is missing"));
    752         }
    753     }
    754 
    755     llMedia.push_back(med);
     756        Utf8Str strPropName, strPropValue;
     757        if (   pelmHDChild->getAttributeValue("name", strPropName)
     758            && pelmHDChild->getAttributeValue("value", strPropValue) )
     759            med.properties[strPropName] = strPropValue;
     760        else
     761            throw ConfigFileError(this, pelmHDChild, N_("Required HardDisk/Property/@name or @value attribute is missing"));
     762    }
     763}
     764
     765/**
     766 * Reads a media registry entry from the main VirtualBox.xml file and recurses
     767 * into children where applicable.
     768 *
     769 * @param t
     770 * @param depth
     771 * @param elmMedium
     772 * @param med
     773 */
     774void ConfigFileBase::readMedium(MediaType t,
     775                                uint32_t depth,
     776                                const xml::ElementNode &elmMedium,  // HardDisk node if root; if recursing,
     777                                                                    // child HardDisk node or DiffHardDisk node for pre-1.4
     778                                Medium &med)                        // medium settings to fill out
     779{
     780    if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
     781        throw ConfigFileError(this, &elmMedium, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
     782
     783    // Do not inline this method call, as the purpose of having this separate
     784    // is to save on stack size. Less local variables are the key for reaching
     785    // deep recursion levels with small stack (XPCOM/g++ without optimization).
     786    readMediumOne(t, elmMedium, med);
     787
     788    if (t != HardDisk)
     789        return;
     790
     791    // recurse to handle children
     792    MediaList &llSettingsChildren = med.llChildren;
     793    xml::NodesLoop nl2(elmMedium, m->sv >= SettingsVersion_v1_4 ? "HardDisk" : "DiffHardDisk");
     794    const xml::ElementNode *pelmHDChild;
     795    while ((pelmHDChild = nl2.forAllNodes()))
     796    {
     797        // recurse with this element and put the child at the end of the list.
     798        // XPCOM has very small stack, avoid big local variables and use the
     799        // list element.
     800        llSettingsChildren.push_back(g_MediumEmpty);
     801        readMedium(t,
     802                   depth + 1,
     803                   *pelmHDChild,
     804                   llSettingsChildren.back());
     805    }
    756806}
    757807
     
    788838        while ((pelmMedium = nl2.forAllNodes()))
    789839        {
    790             if (    t == HardDisk
    791                  && (pelmMedium->nameEquals("HardDisk"))
    792                )
    793                 readMedium(t,
    794                            *pelmMedium,
    795                            mr.llHardDisks);      // list to append hard disk data to: the root list
    796             else if (    t == DVDImage
    797                       && (pelmMedium->nameEquals("Image"))
    798                     )
    799                 readMedium(t,
    800                            *pelmMedium,
    801                            mr.llDvdImages);      // list to append dvd images to: the root list
    802             else if (    t == FloppyImage
    803                       && (pelmMedium->nameEquals("Image"))
    804                     )
    805                 readMedium(t,
    806                            *pelmMedium,
    807                            mr.llFloppyImages);      // list to append floppy images to: the root list
     840            if (   t == HardDisk
     841                && (pelmMedium->nameEquals("HardDisk")))
     842            {
     843                mr.llHardDisks.push_back(g_MediumEmpty);
     844                readMedium(t, 1, *pelmMedium, mr.llHardDisks.back());
     845            }
     846            else if (   t == DVDImage
     847                     && (pelmMedium->nameEquals("Image")))
     848            {
     849                mr.llDvdImages.push_back(g_MediumEmpty);
     850                readMedium(t, 1, *pelmMedium, mr.llDvdImages.back());
     851            }
     852            else if (   t == FloppyImage
     853                     && (pelmMedium->nameEquals("Image")))
     854            {
     855                mr.llFloppyImages.push_back(g_MediumEmpty);
     856                readMedium(t, 1, *pelmMedium, mr.llFloppyImages.back());
     857            }
    808858        }
    809859    }
     
    10861136 * MainConfigFile::write().
    10871137 *
     1138 * @param t
     1139 * @param depth
    10881140 * @param elmMedium
    1089  * @param m
    1090  * @param level
    1091  */
    1092 void ConfigFileBase::buildMedium(xml::ElementNode &elmMedium,
    1093                                  DeviceType_T devType,
    1094                                  const Medium &mdm,
    1095                                  uint32_t level)          // 0 for "root" call, incremented with each recursion
    1096 {
     1141 * @param mdm
     1142 */
     1143void ConfigFileBase::buildMedium(MediaType t,
     1144                                 uint32_t depth,
     1145                                 xml::ElementNode &elmMedium,
     1146                                 const Medium &mdm)
     1147{
     1148    if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
     1149        throw ConfigFileError(this, &elmMedium, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
     1150
    10971151    xml::ElementNode *pelmMedium;
    10981152
    1099     if (devType == DeviceType_HardDisk)
     1153    if (t == HardDisk)
    11001154        pelmMedium = elmMedium.createChild("HardDisk");
    11011155    else
     
    11061160    pelmMedium->setAttributePath("location", mdm.strLocation);
    11071161
    1108     if (devType == DeviceType_HardDisk || RTStrICmp(mdm.strFormat.c_str(), "RAW"))
     1162    if (t == HardDisk || RTStrICmp(mdm.strFormat.c_str(), "RAW"))
    11091163        pelmMedium->setAttribute("format", mdm.strFormat);
    1110     if (   devType == DeviceType_HardDisk
     1164    if (   t == HardDisk
    11111165        && mdm.fAutoReset)
    11121166        pelmMedium->setAttribute("autoReset", mdm.fAutoReset);
     
    11241178
    11251179    // only for base hard disks, save the type
    1126     if (level == 0)
     1180    if (depth == 1)
    11271181    {
    11281182        // no need to save the usual DVD/floppy medium types
    1129         if (   (   devType != DeviceType_DVD
     1183        if (   (   t != DVDImage
    11301184                || (   mdm.hdType != MediumType_Writethrough // shouldn't happen
    11311185                    && mdm.hdType != MediumType_Readonly))
    1132             && (   devType != DeviceType_Floppy
     1186            && (   t != FloppyImage
    11331187                || mdm.hdType != MediumType_Writethrough))
    11341188        {
     
    11501204    {
    11511205        // recurse for children
    1152         buildMedium(*pelmMedium, // parent
    1153                     devType,     // device type
    1154                     *it,         // settings::Medium
    1155                     ++level);    // recursion level
     1206        buildMedium(t,              // device type
     1207                    depth + 1,      // depth
     1208                    *pelmMedium,    // parent
     1209                    *it);           // settings::Medium
    11561210    }
    11571211}
     
    11781232         ++it)
    11791233    {
    1180         buildMedium(*pelmHardDisks, DeviceType_HardDisk, *it, 0);
     1234        buildMedium(HardDisk, 1, *pelmHardDisks, *it);
    11811235    }
    11821236
     
    11861240         ++it)
    11871241    {
    1188         buildMedium(*pelmDVDImages, DeviceType_DVD, *it, 0);
     1242        buildMedium(DVDImage, 1, *pelmDVDImages, *it);
    11891243    }
    11901244
     
    11941248         ++it)
    11951249    {
    1196         buildMedium(*pelmFloppyImages, DeviceType_Floppy, *it, 0);
     1250        buildMedium(FloppyImage, 1, *pelmFloppyImages, *it);
    11971251    }
    11981252}
     
    37673821{
    37683822    if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
    3769         throw ConfigFileError(this, &elmSnapshot, N_("Maximum snapshot tree depth of %u exceeded"), depth);
     3823        throw ConfigFileError(this, &elmSnapshot, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
    37703824
    37713825    Utf8Str strTemp;
     
    38143868                if (pelmChildSnapshot->nameEquals("Snapshot"))
    38153869                {
    3816                     // Use the heap to reduce the stack footprint. Each
    3817                     // recursion needs over 1K, and there can be VMs with
    3818                     // deeply nested snapshots. The stack can be quite
    3819                     // small, especially with XPCOM.
    3820                     Snapshot *child = new Snapshot();
    3821                     bool found = readSnapshot(curSnapshotUuid, depth + 1, *pelmChildSnapshot, *child);
     3870                    // recurse with this element and put the child at the
     3871                    // end of the list. XPCOM has very small stack, avoid
     3872                    // big local variables and use the list element.
     3873                    snap.llChildSnapshots.push_back(g_SnapshotEmpty);
     3874                    bool found = readSnapshot(curSnapshotUuid, depth + 1, *pelmChildSnapshot, snap.llChildSnapshots.back());
    38223875                    foundCurrentSnapshot = foundCurrentSnapshot || found;
    3823                     snap.llChildSnapshots.push_back(*child);
    3824                     delete child;
    38253876                }
    38263877            }
     
    51495200    pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
    51505201    pelmSnapshot->setAttribute("name", snap.strName);
    5151     pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
     5202    pelmSnapshot->setAttribute("timeStamp", stringifyTimestamp(snap.timestamp));
    51525203
    51535204    if (snap.strStateFile.length())
     
    52565307    if (!fCurrentStateModified)
    52575308        elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
    5258     elmMachine.setAttribute("lastStateChange", makeString(timeLastStateChange));
     5309    elmMachine.setAttribute("lastStateChange", stringifyTimestamp(timeLastStateChange));
    52595310    if (fAborted)
    52605311        elmMachine.setAttribute("aborted", fAborted);
Note: See TracChangeset for help on using the changeset viewer.

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