Changeset 94598 in vbox
- Timestamp:
- Apr 13, 2022 9:50:00 PM (3 years ago)
- svn:sync-xref-src-repo-rev:
- 150959
- Location:
- trunk
- Files:
-
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/VBox/settings.h
r93115 r94598 59 59 * XPCOM has a relatively low stack size for its workers, and we have 60 60 * 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. 62 63 */ 63 64 #define SETTINGS_MEDIUM_DEPTH_MAX 300 … … 68 69 * to avoid crashes due to exceeding the limit both on reading and 69 70 * 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. 72 73 */ 73 74 #define SETTINGS_SNAPSHOT_DEPTH_MAX 250 … … 257 258 USBDeviceFiltersList &ll); 258 259 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); 260 261 void readMediaRegistry(const xml::ElementNode &elmMediaRegistry, MediaRegistry &mr); 261 262 void readNATForwardRulesMap(const xml::ElementNode &elmParent, NATRulesMap &mapRules); … … 271 272 bool fHostMode); 272 273 void buildMedium(MediaType t, 273 uint32_t depth,274 274 xml::ElementNode &elmMedium, 275 const Medium &m dm);275 const Medium &med); 276 276 void buildMediaRegistry(xml::ElementNode &elmParent, 277 277 const MediaRegistry &mr); … … 1409 1409 void readStorageControllers(const xml::ElementNode &elmStorageControllers, Storage &strg); 1410 1410 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); 1416 1416 void convertOldOSType_pre1_5(com::Utf8Str &str); 1417 1417 void readMachine(const xml::ElementNode &elmMachine); … … 1423 1423 bool fSkipRemovableMedia, 1424 1424 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); 1429 1429 1430 1430 void bumpSettingsVersionIfNeeded(); -
trunk/src/VBox/Main/include/MachineImpl.h
r93891 r94598 24 24 #include "AuthLibrary.h" 25 25 #include "VirtualBoxBase.h" 26 #include "SnapshotImpl.h"27 26 #include "ProgressImpl.h" 28 27 #include "VRDEServerImpl.h" … … 645 644 const Guid *puuidRegistry); 646 645 HRESULT i_loadSnapshot(const settings::Snapshot &data, 647 const Guid &aCurSnapshotId, 648 Snapshot *aParentSnapshot); 646 const Guid &aCurSnapshotId); 649 647 HRESULT i_loadHardware(const Guid *puuidRegistry, 650 648 const Guid *puuidSnapshot, -
trunk/src/VBox/Main/include/MediumImpl.h
r93115 r94598 77 77 DeviceType_T aDeviceType, 78 78 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); 89 88 90 89 // initializer for host floppy/DVD … … 122 121 bool i_addRegistryNoCallerCheck(const Guid &id); 123 122 /* handles caller/locking itself, caller is responsible for tree lock */ 124 bool i_addRegistry Recursive(const Guid &id);123 bool i_addRegistryAll(const Guid &id); 125 124 /* handles caller/locking itself */ 126 125 bool i_removeRegistry(const Guid& id); 127 126 /* handles caller/locking itself, caller is responsible for tree lock */ 128 bool i_removeRegistry Recursive(const Guid& id);127 bool i_removeRegistryAll(const Guid& id); 129 128 bool i_isInRegistry(const Guid& id); 130 129 bool i_getFirstRegistryMachineId(Guid &uuid) const; … … 140 139 141 140 const Guid* i_getFirstMachineBackrefId() const; 142 const Guid* i_getAnyMachineBackref( ) const;141 const Guid* i_getAnyMachineBackref(const Guid &aId) const; 143 142 const Guid* i_getFirstMachineBackrefSnapshotId() const; 144 143 size_t i_getMachineBackRefCount() const; -
trunk/src/VBox/Main/include/SnapshotImpl.h
r93115 r94598 74 74 ULONG i_getChildrenCount(); 75 75 ULONG i_getAllChildrenCount(); 76 ULONG i_getAllChildrenCountImpl();77 76 78 77 const ComObjPtr<SnapshotMachine>& i_getSnapshotMachine() const; … … 98 97 const Utf8Str &strNewPath); 99 98 99 HRESULT i_saveSnapshotOne(settings::Snapshot &data) const; 100 100 HRESULT i_saveSnapshot(settings::Snapshot &data) const; 101 HRESULT i_saveSnapshotImpl(settings::Snapshot &data) const;102 HRESULT i_saveSnapshotImplOne(settings::Snapshot &data) const;103 101 104 HRESULT i_uninit One(AutoWriteLock &writeLock,102 HRESULT i_uninitAll(AutoWriteLock &writeLock, 105 103 CleanupMode_T cleanupMode, 106 104 MediaList &llMedia, 107 105 std::list<Utf8Str> &llFilenames); 108 HRESULT i_uninitRecursively(AutoWriteLock &writeLock,109 CleanupMode_T cleanupMode,110 MediaList &llMedia,111 std::list<Utf8Str> &llFilenames);112 106 113 107 -
trunk/src/VBox/Main/include/VirtualBoxImpl.h
r94249 r94598 271 271 AutoWriteLock &mediaTreeLock, bool fCalledFromMediumInit = false); 272 272 HRESULT i_unregisterMedium(Medium *pMedium); 273 void i_pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium);274 273 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); 276 275 void i_rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir, 277 276 const Utf8Str &strNewConfigDir); -
trunk/src/VBox/Main/src-server/MachineImpl.cpp
r94470 r94598 29 29 #include "VirtualBoxImpl.h" 30 30 #include "MachineImpl.h" 31 #include "SnapshotImpl.h" 31 32 #include "ClientToken.h" 32 33 #include "ProgressImpl.h" … … 4949 4950 uninit(); 4950 4951 4951 mParent->i_unregisterMachine(this, id);4952 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id); 4952 4953 // calls VirtualBox::i_saveSettings() 4953 4954 … … 4986 4987 if (mData->mFirstSnapshot) 4987 4988 { 4988 // add the media from the medium attachments of the snapshots to llMedia4989 // as well, after the "main" machine media; Snapshot::uninitRecursively()4990 // calls Machine::detachAllMedia() for the snapshot machine, recursing4991 // into the children first4989 // 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. 4992 4993 4993 4994 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this … … 5001 5002 5002 5003 // GO! 5003 pFirstSnapshot->i_uninit Recursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);5004 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete); 5004 5005 5005 5006 mData->mMachineState = oldState; … … 5034 5035 (*it).queryInterfaceTo(aMedia[i].asOutParam()); 5035 5036 5036 mParent->i_unregisterMachine(this, id);5037 mParent->i_unregisterMachine(this, aCleanupMode, id); 5037 5038 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries() 5039 5040 autoCaller.release(); 5041 uninit(); 5038 5042 5039 5043 return S_OK; … … 8402 8406 if (!i_isSessionMachine() && !i_isSnapshotMachine()) 8403 8407 { 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) 8405 8409 if (mData->mFirstSnapshot) 8406 8410 { 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() 8410 8413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 8411 8414 mData->mFirstSnapshot->uninit(); … … 8653 8656 // there must be only one root snapshot 8654 8657 Assert(cRootSnapshots == 1); 8655 8656 8658 const settings::Snapshot &snap = config.llFirstSnapshot.front(); 8657 8659 8658 8660 rc = i_loadSnapshot(snap, 8659 config.uuidCurrentSnapshot, 8660 NULL); // no parent == first snapshot 8661 config.uuidCurrentSnapshot); 8661 8662 if (FAILED(rc)) return rc; 8662 8663 } … … 8699 8700 8700 8701 /** 8701 * Recursively loads all snapshots starting from the given.8702 * Loads all snapshots starting from the given settings. 8702 8703 * 8703 8704 * @param data snapshot settings. 8704 8705 * @param aCurSnapshotId Current snapshot ID from the settings file. 8705 * @param aParentSnapshot Parent snapshot.8706 8706 */ 8707 8707 HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data, 8708 const Guid &aCurSnapshotId, 8709 Snapshot *aParentSnapshot) 8708 const Guid &aCurSnapshotId) 8710 8709 { 8711 8710 AssertReturn(!i_isSnapshotMachine(), E_FAIL); … … 8714 8713 HRESULT rc = S_OK; 8715 8714 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 ¤t->debugging, 8747 ¤t->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 } 8775 8786 } 8776 8787 … … 10654 10665 if (pMedium != pBase) 10655 10666 { 10656 /* Tree lock needed by Medium::addRegistry when recursing. */10667 /* Tree lock needed by Medium::addRegistryAll. */ 10657 10668 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 10658 if (fCanHaveOwnMediaRegistry && p Medium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))10669 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId())) 10659 10670 { 10660 10671 treeLock.release(); … … 10662 10673 treeLock.acquire(); 10663 10674 } 10664 if (pBase->i_addRegistry Recursive(uuid))10675 if (pBase->i_addRegistryAll(uuid)) 10665 10676 { 10666 10677 treeLock.release(); -
trunk/src/VBox/Main/src-server/MachineImplMoveVM.cpp
r93115 r94598 26 26 27 27 #include "MachineImplMoveVM.h" 28 #include "SnapshotImpl.h" 28 29 #include "MediumFormatImpl.h" 29 30 #include "VirtualBoxImpl.h" -
trunk/src/VBox/Main/src-server/MediumImpl.cpp
r93115 r94598 1213 1213 DeviceType_T aDeviceType, 1214 1214 const Guid &uuidMachineRegistry, 1215 const settings::Medium &data,1216 const Utf8Str &strMachineFolder)1215 const Utf8Str &strMachineFolder, 1216 const settings::Medium &data) 1217 1217 { 1218 1218 HRESULT rc; … … 1356 1356 * 1357 1357 * @param aVirtualBox VirtualBox object. 1358 * @param aParent Parent medium disk or NULL for a root (base) medium.1359 1358 * @param aDeviceType Device type of the medium. 1360 1359 * @param uuidMachineRegistry The registry to which this medium should be added 1361 1360 * (global registry UUID or machine UUID). 1362 * @param data Configuration settings.1363 1361 * @param strMachineFolder The machine folder with which to resolve relative 1364 1362 * paths; if empty, then we use the VirtualBox home directory 1363 * @param data Configuration settings. 1365 1364 * @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. 1372 1369 */ 1370 /* static */ 1373 1371 HRESULT Medium::initFromSettings(VirtualBox *aVirtualBox, 1374 Medium *aParent,1375 1372 DeviceType_T aDeviceType, 1376 1373 const Guid &uuidMachineRegistry, 1374 const Utf8Str &strMachineFolder, 1377 1375 const settings::Medium &data, 1378 const Utf8Str &strMachineFolder,1379 1376 AutoWriteLock &mediaTreeLock, 1380 ComObjPtr<Medium> *ppRegistered) 1381 { 1382 using namespace settings; 1383 1377 std::list<std::pair<Guid, DeviceType_T> > &uIdsForNotify) 1378 { 1384 1379 Assert(aVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread()); 1385 1380 AssertReturn(aVirtualBox, E_INVALIDARG); 1386 1381 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(); 1464 1486 mediaTreeLock.acquire(); 1487 } 1465 1488 1466 1489 return rc; … … 1533 1556 * Called either from FinalRelease() or by the parent when it gets destroyed. 1534 1557 * 1535 * @note All children of this medium get uninitialized by calling their1536 * uninit() methods.1558 * @note All children of this medium get uninitialized, too, in a stack 1559 * friendly manner. 1537 1560 */ 1538 1561 void Medium::uninit() … … 1552 1575 return; 1553 1576 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. */ 1556 1578 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread()); 1557 1579 1558 1580 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()); 1559 1598 #ifdef DEBUG 1560 if (!m->backRefs.empty())1561 i_dumpBackRefs();1599 if (!pMedium->m->backRefs.empty()) 1600 pMedium->i_dumpBackRefs(); 1562 1601 #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 1583 1636 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 } 1603 1638 } 1604 1639 … … 3622 3657 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox); 3623 3658 3624 /* canClose() needs the tree lock */3659 /* i_canClose() needs the tree lock */ 3625 3660 AutoMultiWriteLock2 multilock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL, 3626 3661 this->lockHandle() … … 4223 4258 * medium should be registered. The UUID can either be a machine UUID, 4224 4259 * to add a machine registry, or the global registry UUID as returned by 4225 * VirtualBox::getGlobalRegistryId(). This recurses overall children.4260 * VirtualBox::getGlobalRegistryId(). Thisis applied to all children. 4226 4261 * 4227 4262 * Note that for hard disks, this method does nothing if the medium is … … 4235 4270 * @return true if the registry was added; false if the given id was already on the list. 4236 4271 */ 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); 4272 bool 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); 4252 4294 } 4253 4295 … … 4289 4331 /** 4290 4332 * 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. 4292 4334 * 4293 4335 * @note the caller must hold the media tree lock for reading. … … 4296 4338 * @return true if the UUID was found or false if not. 4297 4339 */ 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); 4340 bool 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); 4313 4362 } 4314 4363 … … 4597 4646 * 4598 4647 * Must have caller + locking, *and* caller must hold the media tree lock! 4648 * @param aId Id to ignore when looking for backrefs. 4599 4649 * @return 4600 4650 */ 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; 4651 const 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); 4615 4677 } 4616 4678 … … 4936 4998 /** 4937 4999 * 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. 4939 5001 * 4940 5002 * @param data Settings struct to be updated. … … 4952 5014 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4953 5015 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(¤t->llChildren.back()); 4972 5041 } 4973 5042 } … … 5402 5471 try 5403 5472 { 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 */ 5405 5474 AutoWriteLock treelock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 5406 5475 -
trunk/src/VBox/Main/src-server/SnapshotImpl.cpp
r93410 r94598 157 157 * Since this manipulates the snapshots tree, the caller must hold the 158 158 * 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. 159 162 */ 160 163 void Snapshot::uninit() … … 162 165 LogFlowThisFunc(("\n")); 163 166 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 } 204 235 } 205 236 … … 278 309 /** 279 310 * Internal helper that removes "this" from the list of children of its 280 * parent. Used in uninit() and otherplaces when reparenting is necessary.311 * parent. Used in places when reparenting is necessary. 281 312 * 282 313 * The caller must hold the machine lock in write mode (which protects the snapshots tree)! … … 488 519 /** 489 520 * Returns the number of direct child snapshots, without grandchildren. 490 * Does not recurse.491 521 * @return 492 522 */ … … 503 533 504 534 /** 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. 507 536 * @return 508 537 */ 509 ULONG Snapshot::i_getAllChildrenCount Impl()538 ULONG Snapshot::i_getAllChildrenCount() 510 539 { 511 540 AutoCaller autoCaller(this); 512 541 AssertComRC(autoCaller.rc()); 513 542 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 * @return529 */530 ULONG Snapshot::i_getAllChildrenCount()531 {532 AutoCaller autoCaller(this);533 AssertComRC(autoCaller.rc());534 535 543 // snapshots tree is protected by machine lock 536 544 AutoReadLock alock(m->pMachine COMMA_LOCKVAL_SRC_POS); 537 545 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; 539 566 } 540 567 … … 720 747 const Utf8Str &path = m->pMachine->mSSData->strStateFilePath; 721 748 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) 724 761 if (path == strPath) 725 return true; // no need to recurse then726 727 // but otherwise we must check children728 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))735 762 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); 736 769 } 737 770 … … 805 838 * @return 806 839 */ 807 HRESULT Snapshot::i_saveSnapshot ImplOne(settings::Snapshot &data) const840 HRESULT Snapshot::i_saveSnapshotOne(settings::Snapshot &data) const 808 841 { 809 842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); … … 827 860 828 861 /** 829 * Internal implementation for Snapshot::saveSnapshot (below). Caller has830 * requested the snapshots tree (machine) lock.831 *832 * @param data Target for saving snapshot settings.833 * @return834 */835 HRESULT Snapshot::i_saveSnapshotImpl(settings::Snapshot &data) const836 {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 the847 // stack footprint, avoiding local settings objects on the stack which848 // need a lot of stack space. There can be VMs with deeply nested849 // 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 /**864 862 * Saves the given snapshot and all its children. 865 863 * It is assumed that the given node is empty. … … 872 870 AutoReadLock alock(m->pMachine COMMA_LOCKVAL_SRC_POS); 873 871 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(¤t->llChildSnapshots.back()); 896 } 897 } 898 899 return S_OK; 875 900 } 876 901 … … 879 904 * 880 905 * 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). 883 915 * 884 916 * Caller must hold the machine write lock (which protects the snapshots tree!) … … 890 922 * @return 891 923 */ 892 HRESULT Snapshot::i_uninit One(AutoWriteLock &writeLock,924 HRESULT Snapshot::i_uninitAll(AutoWriteLock &writeLock, 893 925 CleanupMode_T cleanupMode, 894 926 MediaList &llMedia, 895 927 std::list<Utf8Str> &llFilenames) 896 928 { 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()) 915 975 { 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); 918 980 } 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 } 930 991 931 992 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 machine938 * and returns the snapshot's saved state file name, if any, and then calls939 * uninit() on "this" itself.940 *941 * This recurses into children first, so the given MediaList receives child942 * 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 media944 * 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 llFilenames955 * @return956 */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 ourselves967 #ifdef LOG_ENABLED968 Guid uuid = i_getId();969 Utf8Str name = i_getName();970 LogFlowThisFunc(("Entering for snapshot '%s' {%RTuuid}\n", name.c_str(), uuid.raw()));971 #endif972 973 // Recurse into children first so that the child media appear on the list974 // first; this way caller can close the media from the beginning to the end975 // because parent media can't be closed if they have children and976 // additionally it postpones the uninit() call until we no longer need977 // anything from the list. Oh, and remember that the child removes itself978 // 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_ENABLED993 LogFlowThisFunc(("Leaving for snapshot '%s' {%RTuuid}: %Rhrc\n", name.c_str(), uuid.raw(), rc));994 #endif995 996 return rc;997 993 } 998 994 … … 1731 1727 } 1732 1728 1729 LogRel(("Taking snapshot %s\n", task.m_strName.c_str())); 1730 1733 1731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1734 1732 … … 1758 1756 /* save settings to ensure current changes are committed and 1759 1757 * hard disks are fixed up */ 1760 rc = i_saveSettings(NULL, alock); 1758 rc = i_saveSettings(NULL, alock); /******************1 */ 1761 1759 // no need to check for whether VirtualBox.xml needs changing since 1762 1760 // we can't have a machine XML rename pending at this point … … 1934 1932 * machine state to the state it had at the beginning. 1935 1933 */ 1936 rc = i_finishTakingSnapshot(task, alock, true /*aSuccess*/); 1934 rc = i_finishTakingSnapshot(task, alock, true /*aSuccess*/); /*******************2+3 */ 1937 1935 // do not throw rc here because we can't call i_finishTakingSnapshot() twice 1938 1936 LogFlowThisFunc(("i_finishTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, ::stringifyMachineState(mData->mMachineState))); … … 1978 1976 { 1979 1977 if (!task.m_fTakingSnapshotOnline) 1980 i_setMachineState(task.m_machineStateBackup); 1978 i_setMachineState(task.m_machineStateBackup); /**************** 4 Machine::i_saveStateSettings*/ 1981 1979 else 1982 1980 { … … 2048 2046 } 2049 2047 } 2048 LogRel(("Finished taking snapshot %s\n", task.m_strName.c_str())); 2050 2049 LogFlowThisFuncLeave(); 2051 2050 } … … 2112 2111 flSaveSettings |= SaveS_ResetCurStateModified; 2113 2112 2114 rc = i_saveSettings(NULL, alock, flSaveSettings); 2113 rc = i_saveSettings(NULL, alock, flSaveSettings); /******************2 */ 2115 2114 } 2116 2115 … … 2150 2149 2151 2150 /* alock has been released already */ 2152 mParent->i_saveModifiedRegistries(); 2151 mParent->i_saveModifiedRegistries(); /**************3 */ 2153 2152 2154 2153 alock.acquire(); -
trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp
r94249 r94598 935 935 const settings::Medium &xmlHD = *it; 936 936 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); 939 944 if (FAILED(rc)) return rc; 940 ComObjPtr<Medium> pHardDiskActual(pHardDisk);941 rc = pHardDisk->initFromSettings(this,942 NULL, // parent943 DeviceType_HardDisk,944 uuidRegistry,945 xmlHD, // XML data; this recurses to processes the children946 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 know954 * which are duplicates and which aren't. The above initFromSettings is955 * essentially poforming a merge operation now, so in the duplicate case, we may956 * just have added a new (grand)child. Why don't we just pass uIdsForNotify957 * 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();990 945 } 991 946 … … 996 951 const settings::Medium &xmlDvd = *it; 997 952 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, 1005 954 DeviceType_DVD, 1006 955 uuidRegistry, 956 strMachineFolder, 1007 957 xmlDvd, 1008 strMachineFolder,1009 958 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); 1018 960 if (FAILED(rc)) return rc; 1019 treeLock.acquire();1020 961 } 1021 962 … … 1026 967 const settings::Medium &xmlFloppy = *it; 1027 968 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, 1035 970 DeviceType_Floppy, 1036 971 uuidRegistry, 972 strMachineFolder, 1037 973 xmlFloppy, 1038 strMachineFolder,1039 974 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); 1048 976 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); 1060 984 } 1061 985 … … 5238 5162 5239 5163 /** 5240 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,5241 * with children appearing before their parents.5242 * @param llMedia5243 * @param pMedium5244 */5245 void VirtualBox::i_pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)5246 {5247 // recurse first, then add ourselves; this way children end up on the5248 // list before their parents5249 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 /**5264 5164 * Unregisters all Medium objects which belong to the given machine registry. 5265 5165 * Gets called from Machine::uninit() just before the machine object dies … … 5293 5193 if (FAILED(medCaller.rc())) return medCaller.rc(); 5294 5194 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 } 5299 5228 } 5300 5229 } … … 5305 5234 { 5306 5235 ComObjPtr<Medium> pMedium = *it; 5307 Log (("Closing medium %RTuuid\n", pMedium->i_getId().raw()));5236 LogRel(("Closing medium %RTuuid\n", pMedium->i_getId().raw())); 5308 5237 AutoCaller mac(pMedium); 5309 5238 pMedium->i_close(mac); … … 5319 5248 * Called from Machine::Unregister(). 5320 5249 * @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. 5321 5254 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time. 5322 5255 * @return 5323 5256 */ 5324 5257 HRESULT VirtualBox::i_unregisterMachine(Machine *pMachine, 5258 CleanupMode_T aCleanupMode, 5325 5259 const Guid &id) 5326 5260 { … … 5355 5289 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS); 5356 5290 5357 if (pMedium->i_removeRegistry Recursive(id))5291 if (pMedium->i_removeRegistryAll(id)) 5358 5292 { 5359 5293 // machine ID was found in base medium's registry list: 5360 5294 // move this base image and all its children to another registry then 5361 5295 // 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); 5363 5297 if (puuidBetter) 5364 5298 { 5365 5299 // 2) better registry found: then use that 5366 pMedium->i_addRegistry Recursive(*puuidBetter);5300 pMedium->i_addRegistryAll(*puuidBetter); 5367 5301 // 3) and make sure the registry is saved below 5368 5302 mlock.release(); 5369 5303 tlock.release(); 5370 5304 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()); 5371 5314 tlock.acquire(); 5372 5315 mlock.acquire(); -
trunk/src/VBox/Main/xml/Settings.cpp
r94580 r94598 851 851 852 852 /** 853 * Reads a media registry entry from the main VirtualBox.xml file and recurses854 * intochildren where applicable.853 * Reads a media registry entry from the main VirtualBox.xml file and 854 * likewise for all children where applicable. 855 855 * 856 856 * @param t 857 * @param depth858 857 * @param elmMedium 859 858 * @param med 860 859 */ 861 860 void 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 } 892 898 } 893 899 } … … 930 936 { 931 937 mr.llHardDisks.push_back(Medium::Empty); 932 readMedium(t, 1,*pelmMedium, mr.llHardDisks.back());938 readMedium(t, *pelmMedium, mr.llHardDisks.back()); 933 939 } 934 940 else if ( t == DVDImage … … 936 942 { 937 943 mr.llDvdImages.push_back(Medium::Empty); 938 readMedium(t, 1,*pelmMedium, mr.llDvdImages.back());944 readMedium(t, *pelmMedium, mr.llDvdImages.back()); 939 945 } 940 946 else if ( t == FloppyImage … … 942 948 { 943 949 mr.llFloppyImages.push_back(Medium::Empty); 944 readMedium(t, 1,*pelmMedium, mr.llFloppyImages.back());950 readMedium(t, *pelmMedium, mr.llFloppyImages.back()); 945 951 } 946 952 } … … 1265 1271 /** 1266 1272 * 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(). 1269 1274 * 1270 1275 * @param t 1271 * @param depth1272 1276 * @param elmMedium 1273 * @param m dm1277 * @param med 1274 1278 */ 1275 1279 void ConfigFileBase::buildMedium(MediaType t, 1276 uint32_t depth,1277 1280 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 } 1342 1361 } 1343 1362 } … … 1369 1388 ++it) 1370 1389 { 1371 buildMedium(HardDisk, 1,*pelmHardDisks, *it);1390 buildMedium(HardDisk, *pelmHardDisks, *it); 1372 1391 } 1373 1392 } … … 1380 1399 ++it) 1381 1400 { 1382 buildMedium(DVDImage, 1,*pelmDVDImages, *it);1401 buildMedium(DVDImage, *pelmDVDImages, *it); 1383 1402 } 1384 1403 } … … 1391 1410 ++it) 1392 1411 { 1393 buildMedium(FloppyImage, 1,*pelmFloppyImages, *it);1412 buildMedium(FloppyImage, *pelmFloppyImages, *it); 1394 1413 } 1395 1414 } … … 2589 2608 2590 2609 clearDocument(); 2610 LogRel(("Finished saving settings file \"%s\"\n", m->strFilename.c_str())); 2591 2611 } 2592 2612 … … 5750 5770 * Called for reading the \<Teleporter\> element under \<Machine\>. 5751 5771 */ 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);5772 void 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); 5763 5783 } 5764 5784 … … 5766 5786 * Called for reading the \<Debugging\> element under \<Machine\> or \<Snapshot\>. 5767 5787 */ 5768 void MachineConfigFile::readDebugging(const xml::ElementNode *pElmDebugging, Debugging *pDbg)5769 { 5770 if ( !pElmDebugging ||m->sv < SettingsVersion_v1_13)5788 void MachineConfigFile::readDebugging(const xml::ElementNode &elmDebugging, Debugging &dbg) 5789 { 5790 if (m->sv < SettingsVersion_v1_13) 5771 5791 return; 5772 5792 5773 const xml::ElementNode * const pelmTracing = pElmDebugging->findChildElement("Tracing");5793 const xml::ElementNode *pelmTracing = elmDebugging.findChildElement("Tracing"); 5774 5794 if (pelmTracing) 5775 5795 { 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); 5779 5799 } 5780 5800 } … … 5783 5803 * Called for reading the \<Autostart\> element under \<Machine\> or \<Snapshot\>. 5784 5804 */ 5785 void MachineConfigFile::readAutostart(const xml::ElementNode *pElmAutostart, Autostart *pAutostart)5805 void MachineConfigFile::readAutostart(const xml::ElementNode &elmAutostart, Autostart &autostrt) 5786 5806 { 5787 5807 Utf8Str strAutostop; 5788 5808 5789 if ( !pElmAutostart ||m->sv < SettingsVersion_v1_13)5809 if (m->sv < SettingsVersion_v1_13) 5790 5810 return; 5791 5811 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); 5795 5815 if (strAutostop == "Disabled") 5796 pAutostart->enmAutostopType = AutostopType_Disabled;5816 autostrt.enmAutostopType = AutostopType_Disabled; 5797 5817 else if (strAutostop == "SaveState") 5798 pAutostart->enmAutostopType = AutostopType_SaveState;5818 autostrt.enmAutostopType = AutostopType_SaveState; 5799 5819 else if (strAutostop == "PowerOff") 5800 pAutostart->enmAutostopType = AutostopType_PowerOff;5820 autostrt.enmAutostopType = AutostopType_PowerOff; 5801 5821 else if (strAutostop == "AcpiShutdown") 5802 pAutostart->enmAutostopType = AutostopType_AcpiShutdown;5822 autostrt.enmAutostopType = AutostopType_AcpiShutdown; 5803 5823 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()); 5805 5825 } 5806 5826 … … 5808 5828 * Called for reading the \<Groups\> element under \<Machine\>. 5809 5829 */ 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("/");5830 void MachineConfigFile::readGroups(const xml::ElementNode &elmGroups, StringsList &llGroups) 5831 { 5832 llGroups.clear(); 5833 if (m->sv < SettingsVersion_v1_13) 5834 { 5835 llGroups.push_back("/"); 5816 5836 return; 5817 5837 } 5818 5838 5819 xml::NodesLoop nlGroups( *pElmGroups);5839 xml::NodesLoop nlGroups(elmGroups); 5820 5840 const xml::ElementNode *pelmGroup; 5821 5841 while ((pelmGroup = nlGroups.forAllNodes())) … … 5826 5846 if (!pelmGroup->getAttributeValue("name", strGroup)) 5827 5847 throw ConfigFileError(this, pelmGroup, N_("Required Group/@name attribute is missing")); 5828 pllGroups->push_back(strGroup);5848 llGroups.push_back(strGroup); 5829 5849 } 5830 5850 } … … 5834 5854 * Called initially for the \<Snapshot\> element under \<Machine\>, if present, 5835 5855 * 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 if5837 * a \<Snapshots\> (plural) element is found in the snapshot, which should5838 * contain a list of child snapshots; such lists are maintained in the5839 * 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. 5840 5860 * 5841 5861 * @param curSnapshotUuid 5842 * @param depth5843 5862 * @param elmSnapshot 5844 5863 * @param snap … … 5846 5865 */ 5847 5866 bool MachineConfigFile::readSnapshot(const Guid &curSnapshotUuid, 5848 uint32_t depth,5849 5867 const xml::ElementNode &elmSnapshot, 5850 5868 Snapshot &snap) 5851 5869 { 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); 5896 5955 const xml::ElementNode *pelmChildSnapshot; 5897 5956 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes())) … … 5899 5958 if (pelmChildSnapshot->nameEquals("Snapshot")) 5900 5959 { 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); 5907 5964 } 5908 5965 } 5909 5966 } 5910 5967 } 5911 5912 if (m->sv < SettingsVersion_v1_9)5913 // go through Hardware once more to repair the settings controller structures5914 // with data from old DVDDrive and FloppyDrive elements5915 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 Snapshot5920 5968 5921 5969 return foundCurrentSnapshot; … … 6062 6110 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but required Machine/@currentSnapshot attribute is missing")); 6063 6111 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()); 6067 6118 if (!foundCurrentSnapshot) 6068 6119 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but none matches the UUID in the Machine/@currentSnapshot attribute")); 6069 llFirstSnapshot.push_back(snap);6070 6120 } 6071 6121 else if (pelmMachineChild->nameEquals("Description")) 6072 6122 machineUserData.strDescription = pelmMachineChild->getValue(); 6073 6123 else if (pelmMachineChild->nameEquals("Teleporter")) 6074 readTeleporter( pelmMachineChild, &machineUserData);6124 readTeleporter(*pelmMachineChild, machineUserData); 6075 6125 else if (pelmMachineChild->nameEquals("MediaRegistry")) 6076 6126 readMediaRegistry(*pelmMachineChild, mediaRegistry); 6077 6127 else if (pelmMachineChild->nameEquals("Debugging")) 6078 readDebugging( pelmMachineChild, &debugging);6128 readDebugging(*pelmMachineChild, debugging); 6079 6129 else if (pelmMachineChild->nameEquals("Autostart")) 6080 readAutostart( pelmMachineChild, &autostart);6130 readAutostart(*pelmMachineChild, autostart); 6081 6131 else if (pelmMachineChild->nameEquals("Groups")) 6082 readGroups( pelmMachineChild, &machineUserData.llGroups);6132 readGroups(*pelmMachineChild, machineUserData.llGroups); 6083 6133 } 6084 6134 … … 7604 7654 * keys under that. Called for both the \<Machine\> node and for snapshots. 7605 7655 * 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 */ 7659 void MachineConfigFile::buildDebuggingXML(xml::ElementNode &elmParent, const Debugging &dbg) 7660 { 7661 if (m->sv < SettingsVersion_v1_13 || dbg.areDefaultSettings()) 7612 7662 return; 7613 7663 7614 xml::ElementNode *pElmDebugging = pElmParent->createChild("Debugging");7664 xml::ElementNode *pElmDebugging = elmParent.createChild("Debugging"); 7615 7665 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); 7619 7669 } 7620 7670 … … 7623 7673 * keys under that. Called for both the \<Machine\> node and for snapshots. 7624 7674 * 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 */ 7678 void MachineConfigFile::buildAutostartXML(xml::ElementNode &elmParent, const Autostart &autostrt) 7629 7679 { 7630 7680 const char *pcszAutostop = NULL; 7631 7681 7632 if (m->sv < SettingsVersion_v1_13 || pAutostart->areDefaultSettings())7682 if (m->sv < SettingsVersion_v1_13 || autostrt.areDefaultSettings()) 7633 7683 return; 7634 7684 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) 7640 7690 { 7641 7691 case AutostopType_Disabled: pcszAutostop = "Disabled"; break; … … 7652 7702 * keys under that. Called for the \<Machine\> node only. 7653 7703 * 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() == 07660 || ( pllGroups->size() == 1 && pllGroups->front() == "/"))7704 * @param elmParent Parent element. 7705 * @param llGroups Groups list. 7706 */ 7707 void 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() == "/")) 7661 7711 return; 7662 7712 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(); 7666 7716 ++it) 7667 7717 { … … 7673 7723 7674 7724 /** 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. 7678 7730 * 7679 * @param depth7680 7731 * @param elmParent 7681 7732 * @param snap 7682 7733 */ 7683 void MachineConfigFile::buildSnapshotXML(uint32_t depth, 7684 xml::ElementNode &elmParent, 7734 void MachineConfigFile::buildSnapshotXML(xml::ElementNode &elmParent, 7685 7735 const Snapshot &snap) 7686 7736 { 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 } 7717 7785 } 7718 7786 } … … 7850 7918 if ( (fl & BuildMachineXML_IncludeSnapshots) 7851 7919 && llFirstSnapshot.size()) 7852 buildSnapshotXML( 1,elmMachine, llFirstSnapshot.front());7920 buildSnapshotXML(elmMachine, llFirstSnapshot.front()); 7853 7921 7854 7922 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); 7858 7926 } 7859 7927 … … 8650 8718 m->fFileExists = true; 8651 8719 clearDocument(); 8720 LogRel(("Finished saving settings file \"%s\"\n", m->strFilename.c_str())); 8652 8721 } 8653 8722 catch (...) 8654 8723 { 8655 8724 clearDocument(); 8725 LogRel(("Finished saving settings file \"%s\" with failure\n", m->strFilename.c_str())); 8656 8726 throw; 8657 8727 } -
trunk/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py
r93115 r94598 34 34 import os 35 35 import sys 36 import random 36 37 37 38 # Only the main script needs to modify the path. … … 73 74 74 75 try: 76 oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox() 75 77 oVM = self.oTstDrv.createTestVM('test-medium', 1, None, 4) 76 78 assert oVM is not None … … 79 81 fRc = True 80 82 oSession = self.oTstDrv.openSession(oVM) 81 for i in range(1, 301): 83 cImages = 38 #00 84 for i in range(1, cImages + 1): 82 85 sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'Test' + str(i) + '.vdi') 83 86 if i == 1: … … 93 96 fRc = fRc and oSession.saveSettings() 94 97 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 97 101 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 101 125 reporter.log('opening VM %s, testing config reading' % (sSettingsFile)) 102 126 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 103 136 104 137 assert fRc is True … … 115 148 116 149 try: 150 oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox() 117 151 oVM = self.oTstDrv.createTestVM('test-snap', 1, None, 4) 118 152 assert oVM is not None … … 126 160 127 161 # 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): 129 164 fRc = fRc and oSession.takeSnapshot('Snapshot ' + str(i)) 130 165 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 133 173 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 137 197 reporter.log('opening VM %s, testing config reading' % (sSettingsFile)) 138 198 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 139 209 140 210 assert fRc is True
Note:
See TracChangeset
for help on using the changeset viewer.