Changeset 54948 in vbox for trunk/src/VBox/Main/src-server
- Timestamp:
- Mar 25, 2015 4:56:48 PM (10 years ago)
- svn:sync-xref-src-repo-rev:
- 99196
- Location:
- trunk/src/VBox/Main/src-server
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-server/MachineImpl.cpp
r54927 r54948 4273 4273 } 4274 4274 4275 /* Save modified registries, but skip this machine as it's the caller's 4276 * job to save its settings like all other settings changes. */ 4277 mParent->i_unmarkRegistryModified(i_getId()); 4275 4278 mParent->i_saveModifiedRegistries(); 4276 4279 … … 4353 4356 alock.release(); 4354 4357 4358 /* Save modified registries, but skip this machine as it's the caller's 4359 * job to save its settings like all other settings changes. */ 4360 mParent->i_unmarkRegistryModified(i_getId()); 4355 4361 mParent->i_saveModifiedRegistries(); 4356 4362 … … 4794 4800 multiLock.release(); 4795 4801 4802 /* Save modified registries, but skip this machine as it's the caller's 4803 * job to save its settings like all other settings changes. */ 4804 mParent->i_unmarkRegistryModified(i_getId()); 4796 4805 mParent->i_saveModifiedRegistries(); 4797 4806 … … 5118 5127 if (cSnapshots) 5119 5128 { 5120 // autoCleanup must be true here, or we would have failed above5121 5122 5129 // add the media from the medium attachments of the snapshots to llMedia 5123 5130 // as well, after the "main" machine media; Snapshot::uninitRecursively() … … 9278 9285 if (puuidRegistry) 9279 9286 // caller wants registry ID to be set on all attached media (OVF import case) 9280 medium->i_addRegistry(*puuidRegistry , false /* fRecurse */);9287 medium->i_addRegistry(*puuidRegistry); 9281 9288 } 9282 9289 … … 9963 9970 if (mData->mFirstSnapshot) 9964 9971 { 9965 settings::Snapshot snapNew; 9966 config.llFirstSnapshot.push_back(snapNew); 9967 9968 // get reference to the fresh copy of the snapshot on the list and 9969 // work on that copy directly to avoid excessive copying later 9970 settings::Snapshot &snap = config.llFirstSnapshot.front(); 9971 9972 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/); 9972 // the settings use a list for "the first snapshot" 9973 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty); 9974 9975 // get reference to the snapshot on the list and work on that 9976 // element straight in the list to avoid excessive copying later 9977 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back()); 9973 9978 if (FAILED(rc)) throw rc; 9974 9979 } … … 10473 10478 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID 10474 10479 10475 if (pMedium->i_addRegistry(uuid , false /* fRecurse */))10480 if (pMedium->i_addRegistry(uuid)) 10476 10481 mParent->i_markRegistryModified(uuid); 10477 10482 … … 10480 10485 if (pMedium != pBase) 10481 10486 { 10482 if (pBase->i_addRegistry(uuid, true /* fRecurse */)) 10487 /* Tree lock needed by Medium::addRegistry when recursing. */ 10488 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 10489 if (pBase->i_addRegistryRecursive(uuid)) 10490 { 10491 treeLock.release(); 10483 10492 mParent->i_markRegistryModified(uuid); 10493 } 10484 10494 } 10485 10495 } -
trunk/src/VBox/Main/src-server/MachineImplCloneVM.cpp
r54645 r54948 1342 1342 mlock.acquire(); 1343 1343 } 1344 pMedium->i_addRegistry(uuid , false /* fRecurse */);1344 pMedium->i_addRegistry(uuid); 1345 1345 } 1346 1346 /* Check if a snapshot folder is necessary and if so doesn't already -
trunk/src/VBox/Main/src-server/MediumImpl.cpp
r54945 r54948 1213 1213 /** 1214 1214 * Initializes the medium object by loading its data from the given settings 1215 * node. In this mode, the medium will always be opened read/write.1215 * node. The medium will always be opened read/write. 1216 1216 * 1217 1217 * In this case, since we're loading from a registry, uuidMachineRegistry is … … 1219 1219 * loading from a per-machine registry. 1220 1220 * 1221 * @param aVirtualBox VirtualBox object.1222 1221 * @param aParent Parent medium disk or NULL for a root (base) medium. 1223 1222 * @param aDeviceType Device type of the medium. 1224 1223 * @param uuidMachineRegistry The registry to which this medium should be 1225 1224 * added (global registry UUID or machine UUID). 1226 * @param aNodeConfiguration settings.1225 * @param data Configuration settings. 1227 1226 * @param strMachineFolder The machine folder with which to resolve relative paths; 1228 1227 * if empty, then we use the VirtualBox home directory … … 1230 1229 * @note Locks the medium tree for writing. 1231 1230 */ 1232 HRESULT Medium::init(VirtualBox *aVirtualBox, 1233 Medium *aParent, 1234 DeviceType_T aDeviceType, 1235 const Guid &uuidMachineRegistry, 1236 const settings::Medium &data, 1237 const Utf8Str &strMachineFolder) 1238 { 1239 using namespace settings; 1240 1241 AssertReturn(aVirtualBox, E_INVALIDARG); 1242 1243 /* Enclose the state transition NotReady->InInit->Ready */ 1244 AutoInitSpan autoInitSpan(this); 1245 AssertReturn(autoInitSpan.isOk(), E_FAIL); 1246 1247 HRESULT rc = S_OK; 1248 1249 unconst(m->pVirtualBox) = aVirtualBox; 1231 HRESULT Medium::initOne(Medium *aParent, 1232 DeviceType_T aDeviceType, 1233 const Guid &uuidMachineRegistry, 1234 const settings::Medium &data, 1235 const Utf8Str &strMachineFolder) 1236 { 1237 HRESULT rc; 1250 1238 1251 1239 if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero()) … … 1258 1246 // differencing medium: add to parent 1259 1247 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 1260 m->pParent = aParent;1261 aParent->m->llChildren.push_back(this);1248 // no need to check maximum depth as settings reading did it 1249 i_setParent(aParent); 1262 1250 } 1263 1251 … … 1369 1357 m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw())); 1370 1358 1359 return S_OK; 1360 } 1361 1362 /** 1363 * Initializes the medium object and its children by loading its data from the 1364 * given settings node. The medium will always be opened read/write. 1365 * 1366 * In this case, since we're loading from a registry, uuidMachineRegistry is 1367 * always set: it's either the global registry UUID or a machine UUID when 1368 * loading from a per-machine registry. 1369 * 1370 * @param aVirtualBox VirtualBox object. 1371 * @param aParent Parent medium disk or NULL for a root (base) medium. 1372 * @param aDeviceType Device type of the medium. 1373 * @param uuidMachineRegistry The registry to which this medium should be added (global registry UUID or machine UUID). 1374 * @param data Configuration settings. 1375 * @param strMachineFolder The machine folder with which to resolve relative paths; if empty, then we use the VirtualBox home directory 1376 * 1377 * @note Locks the medium tree for writing. 1378 */ 1379 HRESULT Medium::init(VirtualBox *aVirtualBox, 1380 Medium *aParent, 1381 DeviceType_T aDeviceType, 1382 const Guid &uuidMachineRegistry, 1383 const settings::Medium &data, 1384 const Utf8Str &strMachineFolder, 1385 AutoWriteLock &mediaTreeLock) 1386 { 1387 using namespace settings; 1388 1389 Assert(aVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread()); 1390 AssertReturn(aVirtualBox, E_INVALIDARG); 1391 1392 /* Enclose the state transition NotReady->InInit->Ready */ 1393 AutoInitSpan autoInitSpan(this); 1394 AssertReturn(autoInitSpan.isOk(), E_FAIL); 1395 1396 unconst(m->pVirtualBox) = aVirtualBox; 1397 1398 // Do not inline this method call, as the purpose of having this separate 1399 // is to save on stack size. Less local variables are the key for reaching 1400 // deep recursion levels with small stack (XPCOM/g++ without optimization). 1401 HRESULT rc = initOne(aParent, aDeviceType, uuidMachineRegistry, data, strMachineFolder); 1402 1403 1371 1404 /* Don't call Medium::i_queryInfo for registered media to prevent the calling 1372 1405 * thread (i.e. the VirtualBox server startup thread) from an unexpected … … 1390 1423 uuidMachineRegistry, 1391 1424 med, // child data 1392 strMachineFolder); 1425 strMachineFolder, 1426 mediaTreeLock); 1393 1427 if (FAILED(rc)) break; 1394 1428 1395 AutoWriteLock treeLock(aVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 1396 1397 rc = m->pVirtualBox->i_registerMedium(pMedium, &pMedium, treeLock); 1429 rc = m->pVirtualBox->i_registerMedium(pMedium, &pMedium, mediaTreeLock); 1398 1430 if (FAILED(rc)) break; 1399 1431 } … … 3073 3105 m->strLocationFull.c_str(), m->backRefs.size()); 3074 3106 3075 if ( m->llChildren.size() != 0)3107 if (i_getChildren().size() != 0) 3076 3108 return setError(VBOX_E_INVALID_OBJECT_STATE, 3077 3109 tr("Cannot encrypt medium '%s' because it has %d children"), 3078 m->strLocationFull.c_str(), m->llChildren.size());3110 m->strLocationFull.c_str(), i_getChildren().size()); 3079 3111 3080 3112 /* Build the medium lock list. */ … … 3127 3159 break; 3128 3160 } 3129 else if (pMedium-> m->llChildren.size() > 1)3161 else if (pMedium->i_getChildren().size() > 1) 3130 3162 { 3131 3163 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 3132 3164 tr("Cannot encrypt medium '%s' because it has %d children"), 3133 pMedium->m->strLocationFull.c_str(), pMedium-> m->llChildren.size());3165 pMedium->m->strLocationFull.c_str(), pMedium->i_getChildren().size()); 3134 3166 break; 3135 3167 } … … 3473 3505 * See getFirstRegistryMachineId() for details. 3474 3506 * 3475 * If fRecurse == true, then the media tree lock must be held for reading.3476 *3477 3507 * @param id 3478 * @param fRecurse If true, recurses into child media to make sure the whole tree has registries in sync.3479 3508 * @return true if the registry was added; false if the given id was already on the list. 3480 3509 */ 3481 bool Medium::i_addRegistry(const Guid& id , bool fRecurse)3510 bool Medium::i_addRegistry(const Guid& id) 3482 3511 { 3483 3512 AutoCaller autoCaller(this); … … 3511 3540 m->llRegistryIDs.push_back(id); 3512 3541 3513 if (fRecurse)3514 {3515 // Get private list of children and release medium lock straight away.3516 MediaList llChildren(m->llChildren);3517 alock.release();3518 3519 for (MediaList::iterator it = llChildren.begin();3520 it != llChildren.end();3521 ++it)3522 {3523 Medium *pChild = *it;3524 fAdd |= pChild->i_addRegistry(id, true);3525 }3526 }3527 3528 3542 return fAdd; 3529 3543 } 3530 3544 3531 3545 /** 3532 * Removes the given UUID from the list of media registry UUIDs. Returns true 3533 * if found or false if not. 3534 * 3535 * If fRecurse == true, then the media tree lock must be held for reading. 3546 * This adds the given UUID to the list of media registries in which this 3547 * medium should be registered. The UUID can either be a machine UUID, 3548 * to add a machine registry, or the global registry UUID as returned by 3549 * VirtualBox::getGlobalRegistryId(). This recurses over all children. 3550 * 3551 * Note that for hard disks, this method does nothing if the medium is 3552 * already in another registry to avoid having hard disks in more than 3553 * one registry, which causes trouble with keeping diff images in sync. 3554 * See getFirstRegistryMachineId() for details. 3555 * 3556 * @note the caller must hold the media tree lock for reading. 3536 3557 * 3537 3558 * @param id 3538 * @param fRecurse If true, recurses into child media to make sure the whole tree has registries in sync. 3539 * @return 3540 */ 3541 bool Medium::i_removeRegistry(const Guid& id, bool fRecurse) 3559 * @return true if the registry was added; false if the given id was already on the list. 3560 */ 3561 bool Medium::i_addRegistryRecursive(const Guid &id) 3562 { 3563 AutoCaller autoCaller(this); 3564 if (FAILED(autoCaller.rc())) 3565 return false; 3566 3567 bool fAdd = i_addRegistry(id); 3568 3569 // protected by the medium tree lock held by our original caller 3570 for (MediaList::const_iterator it = i_getChildren().begin(); 3571 it != i_getChildren().end(); 3572 ++it) 3573 { 3574 Medium *pChild = *it; 3575 fAdd |= pChild->i_addRegistryRecursive(id); 3576 } 3577 3578 return fAdd; 3579 } 3580 3581 /** 3582 * Removes the given UUID from the list of media registry UUIDs of this medium. 3583 * 3584 * @param id 3585 * @return true if the UUID was found or false if not. 3586 */ 3587 bool Medium::i_removeRegistry(const Guid &id) 3542 3588 { 3543 3589 AutoCaller autoCaller(this); … … 3548 3594 bool fRemove = false; 3549 3595 3596 // @todo r=klaus eliminate this code, replace it by using find. 3550 3597 for (GuidList::iterator it = m->llRegistryIDs.begin(); 3551 3598 it != m->llRegistryIDs.end(); … … 3554 3601 if ((*it) == id) 3555 3602 { 3603 // getting away with this as the iterator isn't used after 3556 3604 m->llRegistryIDs.erase(it); 3557 3605 fRemove = true; … … 3560 3608 } 3561 3609 3562 if (fRecurse) 3563 { 3564 // Get private list of children and release medium lock straight away. 3565 MediaList llChildren(m->llChildren); 3566 alock.release(); 3567 3568 for (MediaList::iterator it = llChildren.begin(); 3569 it != llChildren.end(); 3570 ++it) 3571 { 3572 Medium *pChild = *it; 3573 fRemove |= pChild->i_removeRegistry(id, true); 3574 } 3610 return fRemove; 3611 } 3612 3613 /** 3614 * Removes the given UUID from the list of media registry UUIDs, for this 3615 * medium and all its children recursively. 3616 * 3617 * @note the caller must hold the media tree lock for reading. 3618 * 3619 * @param id 3620 * @return true if the UUID was found or false if not. 3621 */ 3622 bool Medium::i_removeRegistryRecursive(const Guid &id) 3623 { 3624 AutoCaller autoCaller(this); 3625 if (FAILED(autoCaller.rc())) 3626 return false; 3627 3628 bool fRemove = i_removeRegistry(id); 3629 3630 // protected by the medium tree lock held by our original caller 3631 for (MediaList::const_iterator it = i_getChildren().begin(); 3632 it != i_getChildren().end(); 3633 ++it) 3634 { 3635 Medium *pChild = *it; 3636 fRemove |= pChild->i_removeRegistryRecursive(id); 3575 3637 } 3576 3638 … … 3586 3648 * @return 3587 3649 */ 3588 bool Medium::i_isInRegistry(const Guid& id) 3589 { 3650 bool Medium::i_isInRegistry(const Guid &id) 3651 { 3652 // @todo r=klaus eliminate this code, replace it by using find. 3590 3653 for (GuidList::const_iterator it = m->llRegistryIDs.begin(); 3591 3654 it != m->llRegistryIDs.end(); … … 3832 3895 return &m->backRefs.front().machineId; 3833 3896 3834 for (MediaList:: iterator it = m->llChildren.begin();3835 it != m->llChildren.end();3897 for (MediaList::const_iterator it = i_getChildren().begin(); 3898 it != i_getChildren().end(); 3836 3899 ++it) 3837 3900 { … … 3981 4044 3982 4045 return pBase; 4046 } 4047 4048 /** 4049 * Returns the depth of this medium in the media chain. 4050 * 4051 * @note Locks medium tree for reading. 4052 */ 4053 uint32_t Medium::i_getDepth() 4054 { 4055 /* it is possible that some previous/concurrent uninit has already cleared 4056 * the pVirtualBox reference, and in this case we don't need to continue */ 4057 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox); 4058 if (!pVirtualBox) 4059 return 1; 4060 4061 /* we access mParent */ 4062 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4063 4064 uint32_t cDepth = 0; 4065 ComObjPtr<Medium> pMedium(this); 4066 while (!pMedium.isNull()) 4067 { 4068 AutoCaller autoCaller(this); 4069 AssertReturn(autoCaller.isOk(), cDepth + 1); 4070 4071 pMedium = pMedium->m->pParent; 4072 cDepth++; 4073 } 4074 4075 return cDepth; 3983 4076 } 3984 4077 … … 4041 4134 4042 4135 /** 4043 * Saves medium data by appending a new child node to the given 4044 * parent XML settings node. 4136 * Saves the settings of one medium. 4137 * 4138 * @note Caller MUST take care of the medium tree lock and caller. 4045 4139 * 4046 4140 * @param data Settings struct to be updated. 4047 4141 * @param strHardDiskFolder Folder for which paths should be relative. 4048 * 4049 * @note Locks this object, medium tree and children for reading. 4050 */ 4051 HRESULT Medium::i_saveSettings(settings::Medium &data, 4052 const Utf8Str &strHardDiskFolder) 4053 { 4054 AutoCaller autoCaller(this); 4055 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4056 4057 /* we access mParent */ 4058 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4059 4142 */ 4143 void Medium::i_saveSettingsOne(settings::Medium &data, const Utf8Str &strHardDiskFolder) 4144 { 4060 4145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4061 4146 … … 4117 4202 if (m->pParent.isNull()) 4118 4203 data.hdType = m->type; 4204 } 4205 4206 /** 4207 * Saves medium data by putting it into the provided data structure. 4208 * Recurses over all children to save their settings, too. 4209 * 4210 * @param data Settings struct to be updated. 4211 * @param strHardDiskFolder Folder for which paths should be relative. 4212 * 4213 * @note Locks this object, medium tree and children for reading. 4214 */ 4215 HRESULT Medium::i_saveSettings(settings::Medium &data, 4216 const Utf8Str &strHardDiskFolder) 4217 { 4218 AutoCaller autoCaller(this); 4219 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4220 4221 /* we access mParent */ 4222 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4223 4224 i_saveSettingsOne(data, strHardDiskFolder); 4119 4225 4120 4226 /* save all children */ 4227 settings::MediaList &llSettingsChildren = data.llChildren; 4121 4228 for (MediaList::const_iterator it = i_getChildren().begin(); 4122 4229 it != i_getChildren().end(); 4123 4230 ++it) 4124 4231 { 4125 settings::Medium med; 4126 HRESULT rc = (*it)->i_saveSettings(med, strHardDiskFolder); 4127 AssertComRCReturnRC(rc); 4128 data.llChildren.push_back(med); 4232 // Use the element straight in the list to reduce both unnecessary 4233 // deep copying (when unwinding the recursion the entire medium 4234 // settings sub-tree is copied) and the stack footprint (the settings 4235 // need almost 1K, and there can be VMs with long image chains. 4236 llSettingsChildren.push_back(settings::g_MediumEmpty); 4237 HRESULT rc = (*it)->i_saveSettings(llSettingsChildren.back(), strHardDiskFolder); 4238 if (FAILED(rc)) 4239 { 4240 llSettingsChildren.pop_back(); 4241 return rc; 4242 } 4129 4243 } 4130 4244 … … 6120 6234 if (m->pParent) 6121 6235 i_deparent(); 6236 6237 if (!pParent.isNull()) 6238 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX) 6239 { 6240 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS); 6241 throw setError(VBOX_E_INVALID_OBJECT_STATE, 6242 tr("Cannot open differencing image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"), 6243 pParent->m->strLocationFull.c_str()); 6244 } 6122 6245 i_setParent(pParent); 6123 6246 … … 6353 6476 { 6354 6477 // re-associate with the parent as we are still relatives in the registry 6355 m->pParent = pParentBackup; 6356 m->pParent->m->llChildren.push_back(this); 6478 i_setParent(pParentBackup); 6357 6479 } 6358 6480 } … … 7346 7468 try 7347 7469 { 7470 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX) 7471 { 7472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7473 throw setError(VBOX_E_INVALID_OBJECT_STATE, 7474 tr("Cannot create differencing image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"), 7475 m->strLocationFull.c_str()); 7476 } 7477 7348 7478 /* Lock both in {parent,child} order. */ 7349 7479 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS); … … 7467 7597 Assert(pTarget->m->pParent.isNull()); 7468 7598 7469 /* associate the child with the parent */ 7470 pTarget->m->pParent = this; 7471 m->llChildren.push_back(pTarget); 7472 7473 /** @todo r=klaus neither target nor base() are locked, 7474 * potential race! */ 7599 /* associate child with the parent, maximum depth was checked above */ 7600 pTarget->i_setParent(this); 7601 7475 7602 /* diffs for immutable media are auto-reset by default */ 7476 pTarget->m->autoReset = (i_getBase()->m->type == MediumType_Immutable); 7603 bool fAutoReset; 7604 { 7605 ComObjPtr<Medium> pBase = i_getBase(); 7606 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS); 7607 fAutoReset = (pBase->m->type == MediumType_Immutable); 7608 } 7609 { 7610 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS); 7611 pTarget->m->autoReset = fAutoReset; 7612 } 7477 7613 7478 7614 /* register with mVirtualBox as the last step and move to … … 7548 7684 try 7549 7685 { 7686 if (!task.mParentForTarget.isNull()) 7687 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX) 7688 { 7689 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS); 7690 throw setError(VBOX_E_INVALID_OBJECT_STATE, 7691 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"), 7692 task.mParentForTarget->m->strLocationFull.c_str()); 7693 } 7694 7550 7695 PVBOXHDD hdd; 7551 7696 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd); … … 7702 7847 AssertComRC(rc2); 7703 7848 7704 /* then, reparent it and disconnect the deleted branch at 7705 * both ends (chain->parent() is source's parent)*/7849 /* then, reparent it and disconnect the deleted branch at both ends 7850 * (chain->parent() is source's parent). Depth check above. */ 7706 7851 pTarget->i_deparent(); 7707 pTarget->m->pParent = task.mParentForTarget; 7708 if (pTarget->m->pParent) 7709 { 7710 pTarget->m->pParent->m->llChildren.push_back(pTarget); 7852 pTarget->i_setParent(task.mParentForTarget); 7853 if (task.mParentForTarget) 7711 7854 i_deparent(); 7712 }7713 7855 7714 7856 /* then, register again */ … … 7743 7885 7744 7886 pMedium->i_deparent(); // removes pMedium from source 7887 // no depth check, reduces depth 7745 7888 pMedium->i_setParent(pTarget); 7746 7889 } … … 7859 8002 try 7860 8003 { 8004 if (!pParent.isNull()) 8005 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX) 8006 { 8007 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS); 8008 throw setError(VBOX_E_INVALID_OBJECT_STATE, 8009 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"), 8010 pParent->m->strLocationFull.c_str()); 8011 } 8012 7861 8013 /* Lock all in {parent,child} order. The lock is also used as a 7862 8014 * signal from the task initiator (which releases it only after … … 7983 8135 } 7984 8136 7985 /* * @todo r=klaus target isn't locked, race getting the state*/8137 /* target isn't locked, but no changing data is accessed */ 7986 8138 if (task.midxSrcImageSame == UINT32_MAX) 7987 8139 { … … 8053 8205 if (pParent) 8054 8206 { 8055 /* associate the clone with the parent and deassociate 8056 * from VirtualBox */ 8057 pTarget->m->pParent = pParent; 8058 pParent->m->llChildren.push_back(pTarget); 8207 /* Associate the clone with the parent and deassociate 8208 * from VirtualBox. Depth check above. */ 8209 pTarget->i_setParent(pParent); 8059 8210 8060 8211 /* register with mVirtualBox as the last step and move to … … 8692 8843 try 8693 8844 { 8845 if (!pParent.isNull()) 8846 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX) 8847 { 8848 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS); 8849 throw setError(VBOX_E_INVALID_OBJECT_STATE, 8850 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"), 8851 pParent->m->strLocationFull.c_str()); 8852 } 8853 8694 8854 /* Lock all in {parent,child} order. The lock is also used as a 8695 8855 * signal from the task initiator (which releases it only after … … 8799 8959 } 8800 8960 8801 /** @todo r=klaus target isn't locked, race getting the state */8802 8961 vrc = VDCopy(hdd, 8803 8962 VD_LAST_IMAGE, … … 8848 9007 if (pParent) 8849 9008 { 8850 /* associate the imported medium with the parent and deassociate 8851 * from VirtualBox */ 8852 m->pParent = pParent; 8853 pParent->m->llChildren.push_back(this); 9009 /* Associate the imported medium with the parent and deassociate 9010 * from VirtualBox. Depth check above. */ 9011 i_setParent(pParent); 8854 9012 8855 9013 /* register with mVirtualBox as the last step and move to -
trunk/src/VBox/Main/src-server/SnapshotImpl.cpp
r54438 r54948 1 1 /* $Id$ */ 2 2 /** @file 3 *4 3 * COM class implementation for Snapshot and SnapshotMachine in VBoxSVC. 5 4 */ … … 175 174 m->llChildren.clear(); // this unsets all the ComPtrs and probably calls delete 176 175 176 // since there is no guarantee anyone holds a reference to us except the 177 // list of children in our parent, make sure that the reference count 178 // will not drop to 0 before we've declared ourselves as uninitialized, 179 // otherwise there will be another uninit call which causes a self-deadlock 180 // because this uninit isn't complete yet. 181 ComObjPtr<Snapshot> pSnapshot(this); 177 182 if (m->pParent) 178 183 i_deparent(); … … 186 191 delete m; 187 192 m = NULL; 193 194 autoUninitSpan.setSucceeded(); 195 // see above, now the refcount may reach 0 196 pSnapshot.setNull(); 188 197 } 189 198 … … 725 734 726 735 /** 727 * Internal implementation for Snapshot::saveSnapshot (below). Caller has 728 * requested the snapshots tree (machine) lock. 729 * 730 * @param aNode 731 * @param aAttrsOnly 736 * Saves the settings attributes of one snapshot. 737 * 738 * @param data Target for saving snapshot settings. 732 739 * @return 733 740 */ 734 HRESULT Snapshot::i_saveSnapshotImpl (settings::Snapshot &data, bool aAttrsOnly)741 HRESULT Snapshot::i_saveSnapshotImplOne(settings::Snapshot &data) const 735 742 { 736 743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); … … 740 747 data.timestamp = m->timeStamp; 741 748 data.strDescription = m->strDescription; 742 743 if (aAttrsOnly)744 return S_OK;745 749 746 750 // state file (only if this snapshot is online) … … 756 760 if (FAILED(rc)) return rc; 757 761 758 alock.release(); 759 760 data.llChildSnapshots.clear(); 761 762 if (m->llChildren.size()) 763 { 764 for (SnapshotsList::const_iterator it = m->llChildren.begin(); 765 it != m->llChildren.end(); 766 ++it) 762 return S_OK; 763 } 764 765 /** 766 * Internal implementation for Snapshot::saveSnapshot (below). Caller has 767 * requested the snapshots tree (machine) lock. 768 * 769 * @param data Target for saving snapshot settings. 770 * @return 771 */ 772 HRESULT Snapshot::i_saveSnapshotImpl(settings::Snapshot &data) const 773 { 774 HRESULT rc = i_saveSnapshotImplOne(data); 775 if (FAILED(rc)) 776 return rc; 777 778 settings::SnapshotsList &llSettingsChildren = data.llChildSnapshots; 779 for (SnapshotsList::const_iterator it = m->llChildren.begin(); 780 it != m->llChildren.end(); 781 ++it) 782 { 783 // Use the heap (indirectly through the list container) to reduce the 784 // stack footprint, avoiding local settings objects on the stack which 785 // need a lot of stack space. There can be VMs with deeply nested 786 // snapshots. The stack can be quite small, especially with XPCOM. 787 llSettingsChildren.push_back(settings::g_SnapshotEmpty); 788 Snapshot *pSnap = *it; 789 rc = pSnap->i_saveSnapshotImpl(llSettingsChildren.back()); 790 if (FAILED(rc)) 767 791 { 768 // Use the heap to reduce the stack footprint. Each recursion needs 769 // over 1K, and there can be VMs with deeply nested snapshots. The 770 // stack can be quite small, especially with XPCOM. 771 772 settings::Snapshot *snap = new settings::Snapshot(); 773 rc = (*it)->i_saveSnapshotImpl(*snap, aAttrsOnly); 774 if (FAILED(rc)) 775 { 776 delete snap; 777 return rc; 778 } 779 data.llChildSnapshots.push_back(*snap); 780 delete snap; 792 llSettingsChildren.pop_back(); 793 return rc; 781 794 } 782 795 } … … 786 799 787 800 /** 788 * Saves the given snapshot and all its children (unless \a aAttrsOnly is true). 789 * It is assumed that the given node is empty (unless \a aAttrsOnly is true). 790 * 791 * @param aNode <Snapshot> node to save the snapshot to. 792 * @param aSnapshot Snapshot to save. 793 * @param aAttrsOnly If true, only update user-changeable attrs. 794 */ 795 HRESULT Snapshot::i_saveSnapshot(settings::Snapshot &data, bool aAttrsOnly) 801 * Saves the given snapshot and all its children. 802 * It is assumed that the given node is empty. 803 * 804 * @param data Target for saving snapshot settings. 805 */ 806 HRESULT Snapshot::i_saveSnapshot(settings::Snapshot &data) const 796 807 { 797 808 // snapshots tree is protected by machine lock 798 809 AutoReadLock alock(m->pMachine COMMA_LOCKVAL_SRC_POS); 799 810 800 return i_saveSnapshotImpl(data , aAttrsOnly);811 return i_saveSnapshotImpl(data); 801 812 } 802 813 … … 804 815 * Part of the cleanup engine of Machine::Unregister(). 805 816 * 806 * This recursively removes all medium attachments from the snapshot's machine 807 * and returns the snapshot's saved state file name, if any, and then calls 808 * uninit() on "this" itself. 809 * 810 * This recurses into children first, so the given MediaList receives child 811 * media first before their parents. If the caller wants to close all media, 812 * they should go thru the list from the beginning to the end because media 813 * cannot be closed if they have children. 814 * 815 * This calls uninit() on itself, so the snapshots tree (beginning with a machine's pFirstSnapshot) becomes invalid after this. 816 * It does not alter the main machine's snapshot pointers (pFirstSnapshot, pCurrentSnapshot). 817 * This removes all medium attachments from the snapshot's machine and returns 818 * the snapshot's saved state file name, if any, and then calls uninit() on 819 * "this" itself. 817 820 * 818 821 * Caller must hold the machine write lock (which protects the snapshots tree!) … … 824 827 * @return 825 828 */ 826 HRESULT Snapshot::i_uninitRecursively(AutoWriteLock &writeLock, 827 CleanupMode_T cleanupMode, 828 MediaList &llMedia, 829 std::list<Utf8Str> &llFilenames) 830 { 831 Assert(m->pMachine->isWriteLockOnCurrentThread()); 832 833 HRESULT rc = S_OK; 834 835 // make a copy of the Guid for logging before we uninit ourselves 836 #ifdef LOG_ENABLED 837 Guid uuid = i_getId(); 838 Utf8Str name = i_getName(); 839 LogFlowThisFunc(("Entering for snapshot '%s' {%RTuuid}\n", name.c_str(), uuid.raw())); 840 #endif 841 842 // recurse into children first so that the child media appear on 843 // the list first; this way caller can close the media from the 844 // beginning to the end because parent media can't be closed if 845 // they have children 846 847 // make a copy of the children list since uninit() modifies it 848 SnapshotsList llChildrenCopy(m->llChildren); 849 for (SnapshotsList::iterator it = llChildrenCopy.begin(); 850 it != llChildrenCopy.end(); 851 ++it) 852 { 853 Snapshot *pChild = *it; 854 rc = pChild->i_uninitRecursively(writeLock, cleanupMode, llMedia, llFilenames); 855 if (FAILED(rc)) 856 return rc; 857 } 858 829 HRESULT Snapshot::i_uninitOne(AutoWriteLock &writeLock, 830 CleanupMode_T cleanupMode, 831 MediaList &llMedia, 832 std::list<Utf8Str> &llFilenames) 833 { 859 834 // now call detachAllMedia on the snapshot machine 860 rc = m->pMachine->i_detachAllMedia(writeLock,861 this /* pSnapshot */,862 cleanupMode,863 llMedia);835 HRESULT rc = m->pMachine->i_detachAllMedia(writeLock, 836 this /* pSnapshot */, 837 cleanupMode, 838 llMedia); 864 839 if (FAILED(rc)) 865 840 return rc; … … 884 859 } 885 860 886 this->i_beginSnapshotDelete(); 887 this->uninit(); 888 861 i_beginSnapshotDelete(); 862 uninit(); 863 864 return S_OK; 865 } 866 867 /** 868 * Part of the cleanup engine of Machine::Unregister(). 869 * 870 * This recursively removes all medium attachments from the snapshot's machine 871 * and returns the snapshot's saved state file name, if any, and then calls 872 * uninit() on "this" itself. 873 * 874 * This recurses into children first, so the given MediaList receives child 875 * media first before their parents. If the caller wants to close all media, 876 * they should go thru the list from the beginning to the end because media 877 * cannot be closed if they have children. 878 * 879 * This calls uninit() on itself, so the snapshots tree (beginning with a machine's pFirstSnapshot) becomes invalid after this. 880 * It does not alter the main machine's snapshot pointers (pFirstSnapshot, pCurrentSnapshot). 881 * 882 * Caller must hold the machine write lock (which protects the snapshots tree!) 883 * 884 * @param writeLock Machine write lock, which can get released temporarily here. 885 * @param cleanupMode Cleanup mode; see Machine::detachAllMedia(). 886 * @param llMedia List of media returned to caller, depending on cleanupMode. 887 * @param llFilenames 888 * @return 889 */ 890 HRESULT Snapshot::i_uninitRecursively(AutoWriteLock &writeLock, 891 CleanupMode_T cleanupMode, 892 MediaList &llMedia, 893 std::list<Utf8Str> &llFilenames) 894 { 895 Assert(m->pMachine->isWriteLockOnCurrentThread()); 896 897 HRESULT rc = S_OK; 898 899 // make a copy of the Guid for logging before we uninit ourselves 889 900 #ifdef LOG_ENABLED 890 LogFlowThisFunc(("Leaving for snapshot '%s' {%RTuuid}\n", name.c_str(), uuid.raw())); 901 Guid uuid = i_getId(); 902 Utf8Str name = i_getName(); 903 LogFlowThisFunc(("Entering for snapshot '%s' {%RTuuid}\n", name.c_str(), uuid.raw())); 891 904 #endif 892 905 893 return S_OK; 906 // Recurse into children first so that the child media appear on the list 907 // first; this way caller can close the media from the beginning to the end 908 // because parent media can't be closed if they have children and 909 // additionally it postpones the uninit() call until we no longer need 910 // anything from the list. Oh, and remember that the child removes itself 911 // from the list, so keep the iterator at the beginning. 912 for (SnapshotsList::const_iterator it = m->llChildren.begin(); 913 it != m->llChildren.end(); 914 it = m->llChildren.begin()) 915 { 916 Snapshot *pChild = *it; 917 rc = pChild->i_uninitRecursively(writeLock, cleanupMode, llMedia, llFilenames); 918 if (FAILED(rc)) 919 break; 920 } 921 922 if (SUCCEEDED(rc)) 923 rc = i_uninitOne(writeLock, cleanupMode, llMedia, llFilenames); 924 925 #ifdef LOG_ENABLED 926 LogFlowThisFunc(("Leaving for snapshot '%s' {%RTuuid}: %Rhrc\n", name.c_str(), uuid.raw(), rc)); 927 #endif 928 929 return rc; 894 930 } 895 931 -
trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp
r54438 r54948 650 650 */ 651 651 HRESULT VirtualBox::initMedia(const Guid &uuidRegistry, 652 const settings::MediaRegistry mediaRegistry,652 const settings::MediaRegistry &mediaRegistry, 653 653 const Utf8Str &strMachineFolder) 654 654 { … … 674 674 uuidRegistry, 675 675 xmlHD, // XML data; this recurses to processes the children 676 strMachineFolder); 676 strMachineFolder, 677 treeLock); 677 678 if (FAILED(rc)) return rc; 678 679 … … 694 695 uuidRegistry, 695 696 xmlDvd, 696 strMachineFolder); 697 strMachineFolder, 698 treeLock); 697 699 if (FAILED(rc)) return rc; 698 700 … … 714 716 uuidRegistry, 715 717 xmlFloppy, 716 strMachineFolder); 718 strMachineFolder, 719 treeLock); 717 720 if (FAILED(rc)) return rc; 718 721 … … 4014 4017 if (pMedium->i_isInRegistry(uuidRegistry)) 4015 4018 { 4016 settings::Medium med; 4017 rc = pMedium->i_saveSettings(med, strMachineFolder); // this recurses into child hard disks 4018 if (FAILED(rc)) throw rc; 4019 llTarget.push_back(med); 4019 llTarget.push_back(settings::g_MediumEmpty); 4020 rc = pMedium->i_saveSettings(llTarget.back(), strMachineFolder); // this recurses into child hard disks 4021 if (FAILED(rc)) 4022 { 4023 llTarget.pop_back(); 4024 throw rc; 4025 } 4020 4026 } 4021 4027 } … … 4482 4488 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS); 4483 4489 4484 if (pMedium->i_removeRegistry (id, true /* fRecurse */))4490 if (pMedium->i_removeRegistryRecursive(id)) 4485 4491 { 4486 4492 // machine ID was found in base medium's registry list: … … 4491 4497 { 4492 4498 // 2) better registry found: then use that 4493 pMedium->i_addRegistry (*puuidBetter, true /* fRecurse */);4499 pMedium->i_addRegistryRecursive(*puuidBetter); 4494 4500 // 3) and make sure the registry is saved below 4495 4501 mlock.release(); … … 4497 4503 i_markRegistryModified(*puuidBetter); 4498 4504 tlock.acquire(); 4499 mlock. release();4505 mlock.acquire(); 4500 4506 } 4501 4507 } … … 4533 4539 if (SUCCEEDED(machineCaller.rc())) 4534 4540 ASMAtomicIncU64(&pMachine->uRegistryNeedsSaving); 4541 } 4542 } 4543 } 4544 4545 /** 4546 * Marks the registry for @a uuid as unmodified, so that it's not saved in 4547 * a later call to saveModifiedRegistries(). 4548 * 4549 * @param uuid 4550 */ 4551 void VirtualBox::i_unmarkRegistryModified(const Guid &uuid) 4552 { 4553 uint64_t uOld; 4554 if (uuid == i_getGlobalRegistryId()) 4555 { 4556 for (;;) 4557 { 4558 uOld = ASMAtomicReadU64(&m->uRegistryNeedsSaving); 4559 if (!uOld) 4560 break; 4561 if (ASMAtomicCmpXchgU64(&m->uRegistryNeedsSaving, 0, uOld)) 4562 break; 4563 ASMNopPause(); 4564 } 4565 } 4566 else 4567 { 4568 ComObjPtr<Machine> pMachine; 4569 HRESULT rc = i_findMachine(uuid, 4570 false /* fPermitInaccessible */, 4571 false /* aSetError */, 4572 &pMachine); 4573 if (SUCCEEDED(rc)) 4574 { 4575 AutoCaller machineCaller(pMachine); 4576 if (SUCCEEDED(machineCaller.rc())) 4577 { 4578 for (;;) 4579 { 4580 uOld = ASMAtomicReadU64(&pMachine->uRegistryNeedsSaving); 4581 if (!uOld) 4582 break; 4583 if (ASMAtomicCmpXchgU64(&pMachine->uRegistryNeedsSaving, 0, uOld)) 4584 break; 4585 ASMNopPause(); 4586 } 4587 } 4535 4588 } 4536 4589 }
Note:
See TracChangeset
for help on using the changeset viewer.