Changeset 28401 in vbox
- Timestamp:
- Apr 16, 2010 9:14:54 AM (15 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/MachineImpl.cpp
r28352 r28401 37 37 #endif 38 38 39 #include "Logging.h" 39 40 #include "VirtualBoxImpl.h" 40 41 #include "MachineImpl.h" … … 42 43 #include "MediumAttachmentImpl.h" 43 44 #include "MediumImpl.h" 45 #include "MediumLock.h" 44 46 #include "USBControllerImpl.h" 45 47 #include "HostImpl.h" … … 55 57 56 58 #include "AutoCaller.h" 57 #include "Logging.h"58 59 #include "Performance.h" 59 60 … … 2076 2077 AutoCaller autoCaller(this); 2077 2078 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2078 MultiResult rc 2079 MultiResult rc(S_OK); 2079 2080 2080 2081 # ifdef VBOX_WITH_USB … … 3141 3142 if (FAILED(rc)) return rc; 3142 3143 3143 /* make sure the hard disk is not modified before createDiffStorage() */ 3144 rc = medium->LockRead(NULL); 3144 /* Apply the normal locking logic to the entire chain. */ 3145 MediumLockList *pMediumLockList(new MediumLockList()); 3146 rc = diff->createMediumLockList(true, medium, *pMediumLockList); 3145 3147 if (FAILED(rc)) return rc; 3148 rc = pMediumLockList->Lock(); 3149 if (FAILED(rc)) 3150 return setError(rc, 3151 tr("Could not lock medium when creating diff '%s'"), 3152 diff->getLocationFull().c_str()); 3146 3153 3147 3154 /* will leave the lock before the potentially lengthy operation, so … … 3153 3160 alock.leave(); 3154 3161 3155 rc = medium->createDiffStorageAndWait(diff, MediumVariant_Standard, &fNeedsSaveSettings); 3162 rc = medium->createDiffStorage(diff, MediumVariant_Standard, 3163 pMediumLockList, NULL /* aProgress */, 3164 true /* aWait */, &fNeedsSaveSettings); 3156 3165 3157 3166 alock.enter(); … … 3160 3169 setMachineState(oldState); 3161 3170 3162 medium->UnlockRead(NULL); 3171 /* Unlock the media and free the associated memory. */ 3172 delete pMediumLockList; 3163 3173 3164 3174 if (FAILED(rc)) return rc; … … 3257 3267 alock.leave(); 3258 3268 3259 rc = oldmedium->deleteStorageAndWait(NULL /*aProgress*/, &fNeedsSaveSettings); 3269 rc = oldmedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/, 3270 &fNeedsSaveSettings); 3260 3271 3261 3272 alock.enter(); … … 4597 4608 4598 4609 PSSMHANDLE pSSM; 4599 int rc = SSMR3Open(pStateFilePath->raw(), 0 /*fFlags*/, &pSSM);4600 if (RT_SUCCESS( rc))4610 int vrc = SSMR3Open(pStateFilePath->raw(), 0 /*fFlags*/, &pSSM); 4611 if (RT_SUCCESS(vrc)) 4601 4612 { 4602 4613 uint32_t uVersion; 4603 rc = SSMR3Seek(pSSM, "DisplayScreenshot", 1100 /*iInstance*/, &uVersion);4604 if (RT_SUCCESS( rc))4614 vrc = SSMR3Seek(pSSM, "DisplayScreenshot", 1100 /*iInstance*/, &uVersion); 4615 if (RT_SUCCESS(vrc)) 4605 4616 { 4606 4617 if (uVersion == sSSMDisplayScreenshotVer) 4607 4618 { 4608 4619 uint32_t cBlocks; 4609 rc = SSMR3GetU32(pSSM, &cBlocks);4610 AssertRCReturn( rc,rc);4620 vrc = SSMR3GetU32(pSSM, &cBlocks); 4621 AssertRCReturn(vrc, vrc); 4611 4622 4612 4623 for (uint32_t i = 0; i < cBlocks; i++) 4613 4624 { 4614 4625 uint32_t cbBlock; 4615 rc = SSMR3GetU32(pSSM, &cbBlock);4616 AssertRCBreak( rc);4626 vrc = SSMR3GetU32(pSSM, &cbBlock); 4627 AssertRCBreak(vrc); 4617 4628 4618 4629 uint32_t typeOfBlock; 4619 rc = SSMR3GetU32(pSSM, &typeOfBlock);4620 AssertRCBreak( rc);4630 vrc = SSMR3GetU32(pSSM, &typeOfBlock); 4631 AssertRCBreak(vrc); 4621 4632 4622 4633 LogFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock)); … … 4630 4641 if (pu8Data == NULL) 4631 4642 { 4632 rc = VERR_NO_MEMORY;4643 vrc = VERR_NO_MEMORY; 4633 4644 break; 4634 4645 } 4635 4646 4636 rc = SSMR3GetU32(pSSM, &u32Width);4637 AssertRCBreak( rc);4638 rc = SSMR3GetU32(pSSM, &u32Height);4639 AssertRCBreak( rc);4640 rc = SSMR3GetMem(pSSM, pu8Data, cbData);4641 AssertRCBreak( rc);4647 vrc = SSMR3GetU32(pSSM, &u32Width); 4648 AssertRCBreak(vrc); 4649 vrc = SSMR3GetU32(pSSM, &u32Height); 4650 AssertRCBreak(vrc); 4651 vrc = SSMR3GetMem(pSSM, pu8Data, cbData); 4652 AssertRCBreak(vrc); 4642 4653 } 4643 4654 else 4644 4655 { 4645 4656 /* No saved state data. */ 4646 rc = VERR_NOT_SUPPORTED;4657 vrc = VERR_NOT_SUPPORTED; 4647 4658 } 4648 4659 … … 4656 4667 if (cbBlock > 2 * sizeof (uint32_t)) 4657 4668 { 4658 rc = SSMR3Skip(pSSM, cbBlock);4659 AssertRCBreak( rc);4669 vrc = SSMR3Skip(pSSM, cbBlock); 4670 AssertRCBreak(vrc); 4660 4671 } 4661 4672 } … … 4664 4675 else 4665 4676 { 4666 rc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;4677 vrc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION; 4667 4678 } 4668 4679 } … … 4671 4682 } 4672 4683 4673 if (RT_SUCCESS( rc))4684 if (RT_SUCCESS(vrc)) 4674 4685 { 4675 4686 if (u32Type == 0 && cbData % 4 != 0) 4676 4687 { 4677 4688 /* Bitmap is 32bpp, so data is invalid. */ 4678 rc = VERR_SSM_UNEXPECTED_DATA;4679 } 4680 } 4681 4682 if (RT_SUCCESS( rc))4689 vrc = VERR_SSM_UNEXPECTED_DATA; 4690 } 4691 } 4692 4693 if (RT_SUCCESS(vrc)) 4683 4694 { 4684 4695 *ppu8Data = pu8Data; … … 4689 4700 } 4690 4701 4691 LogFlowFunc((" rc %Rrc\n",rc));4692 return rc;4702 LogFlowFunc(("vrc %Rrc\n", vrc)); 4703 return vrc; 4693 4704 } 4694 4705 … … 6334 6345 * the VM after successfully re-checking the accessibility state. Note 6335 6346 * that in case of normal Machine or SnapshotMachine uninitialization (as 6336 * a result of unregistering or d iscarding the snapshot), outdated hard6347 * a result of unregistering or deleting the snapshot), outdated hard 6337 6348 * disk attachments will already be uninitialized and deleted, so this 6338 6349 * code will not affect them. */ … … 8136 8147 * machine and a new set of attachments to refer to created disks. 8137 8148 * 8138 * Used when taking a snapshot or when d iscarding the current state.8149 * Used when taking a snapshot or when deleting the current state. 8139 8150 * 8140 8151 * This method assumes that mMediaData contains the original hard disk attachments … … 8189 8200 HRESULT rc = S_OK; 8190 8201 8191 MediaList lockedMedia; 8202 MediumLockListMap lockedMediaOffline; 8203 MediumLockListMap *lockedMediaMap; 8204 if (aOnline) 8205 lockedMediaMap = &mData->mSession.mLockedMedia; 8206 else 8207 lockedMediaMap = &lockedMediaOffline; 8192 8208 8193 8209 try … … 8204 8220 if (pAtt->getType() == DeviceType_HardDisk) 8205 8221 { 8206 Medium* pHD = pAtt->getMedium(); 8207 Assert(pHD); 8208 rc = pHD->LockRead(NULL); 8209 if (FAILED(rc)) throw rc; 8210 lockedMedia.push_back(pHD); 8222 Medium* pMedium = pAtt->getMedium(); 8223 Assert(pMedium); 8224 8225 MediumLockList *pMediumLockList(new MediumLockList()); 8226 rc = pMedium->createMediumLockList(false, NULL, 8227 *pMediumLockList); 8228 if (FAILED(rc)) 8229 { 8230 delete pMediumLockList; 8231 throw rc; 8232 } 8233 rc = lockedMediaMap->Insert(pAtt, pMediumLockList); 8234 if (FAILED(rc)) 8235 { 8236 throw setError(rc, 8237 tr("Collecting locking information for all attached media failed")); 8238 } 8211 8239 } 8240 } 8241 8242 /* Now lock all media. If this fails, nothing is locked. */ 8243 rc = lockedMediaMap->Lock(); 8244 if (FAILED(rc)) 8245 { 8246 throw setError(rc, 8247 tr("Locking of attached media failed")); 8212 8248 } 8213 8249 } … … 8229 8265 8230 8266 DeviceType_T devType = pAtt->getType(); 8231 Medium* medium = pAtt->getMedium();8267 Medium* pMedium = pAtt->getMedium(); 8232 8268 8233 8269 if ( devType != DeviceType_HardDisk 8234 || medium == NULL8235 || medium->getType() != MediumType_Normal)8270 || pMedium == NULL 8271 || pMedium->getType() != MediumType_Normal) 8236 8272 { 8237 8273 /* copy the attachment as is */ … … 8242 8278 if (devType == DeviceType_HardDisk) 8243 8279 { 8244 if ( medium == NULL)8280 if (pMedium == NULL) 8245 8281 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")), 8246 8282 aWeight); // weight 8247 8283 else 8248 8284 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"), 8249 medium->getBase()->getName().raw()),8285 pMedium->getBase()->getName().raw()), 8250 8286 aWeight); // weight 8251 8287 } … … 8257 8293 /* need a diff */ 8258 8294 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"), 8259 medium->getBase()->getName().raw()),8295 pMedium->getBase()->getName().raw()), 8260 8296 aWeight); // weight 8261 8297 … … 8263 8299 diff.createObject(); 8264 8300 rc = diff->init(mParent, 8265 medium->preferredDiffFormat().raw(),8301 pMedium->preferredDiffFormat().raw(), 8266 8302 BstrFmt("%ls"RTPATH_SLASH_STR, 8267 8303 mUserData->mSnapshotFolderFull.raw()).raw(), … … 8269 8305 if (FAILED(rc)) throw rc; 8270 8306 8271 /* leave the lock before the potentially lengthy operation */8272 alock.leave();8273 8274 rc = medium->createDiffStorageAndWait(diff,8275 MediumVariant_Standard,8276 pfNeedsSaveSettings);8277 8278 8307 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before 8279 8308 * the push_back? Looks like we're going to leave medium with the 8280 8309 * wrong kind of lock (general issue with if we fail anywhere at all) 8281 8310 * and an orphaned VDI in the snapshots folder. */ 8282 // at this point, the old image is still locked for writing, but instead 8283 // we need the new diff image locked for writing and lock the previously 8284 // current one for reading only 8311 8312 /* update the appropriate lock list */ 8313 rc = lockedMediaMap->Unlock(); 8314 AssertComRCThrowRC(rc); 8315 MediumLockList *pMediumLockList; 8316 rc = lockedMediaMap->Get(pAtt, pMediumLockList); 8317 AssertComRCThrowRC(rc); 8285 8318 if (aOnline) 8286 8319 { 8287 diff->LockWrite(NULL); 8288 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(diff), true)); 8289 medium->UnlockWrite(NULL); 8290 medium->LockRead(NULL); 8291 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(medium), false)); 8320 rc = pMediumLockList->Update(pMedium, false); 8321 AssertComRCThrowRC(rc); 8292 8322 } 8293 8323 rc = lockedMediaMap->Lock(); 8324 AssertComRCThrowRC(rc); 8325 8326 /* leave the lock before the potentially lengthy operation */ 8327 alock.leave(); 8328 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard, 8329 pMediumLockList, 8330 NULL /* aProgress */, 8331 true /* aWait */, 8332 pfNeedsSaveSettings); 8333 alock.enter(); 8294 8334 if (FAILED(rc)) throw rc; 8295 8335 8296 alock.enter(); 8336 rc = lockedMediaMap->Unlock(); 8337 AssertComRCThrowRC(rc); 8338 rc = pMediumLockList->Append(diff, true); 8339 AssertComRCThrowRC(rc); 8340 rc = lockedMediaMap->Lock(); 8341 AssertComRCThrowRC(rc); 8297 8342 8298 8343 rc = diff->attachTo(mData->mUuid); … … 8311 8356 if (FAILED(rc)) throw rc; 8312 8357 8358 rc = lockedMediaMap->ReplaceKey(pAtt, attachment); 8359 AssertComRCThrowRC(rc); 8313 8360 mMediaData->mAttachments.push_back(attachment); 8314 8361 } … … 8321 8368 ErrorInfoKeeper eik; 8322 8369 8323 for (MediaList::const_iterator it = lockedMedia.begin(); 8324 it != lockedMedia.end(); 8325 ++it) 8326 { 8327 HRESULT rc2 = (*it)->UnlockRead(NULL); 8328 AssertComRC(rc2); 8329 } 8370 rc = lockedMediaMap->Clear(); 8371 AssertComRC(rc); 8330 8372 } 8331 8373 … … 8427 8469 ComObjPtr<Medium> hd = (*it)->getMedium(); 8428 8470 8429 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings); 8430 #if 1 /* HACK ALERT: Just make it kind of work */ /** @todo Fix this hack properly. The LockWrite / UnlockWrite / LockRead changes aren't undone! */ 8431 if (rc == VBOX_E_INVALID_OBJECT_STATE) 8432 { 8433 LogFlowFunc(("Applying unlock hack on '%s'! FIXME!\n", (*it)->getLogName())); 8434 hd->UnlockWrite(NULL); 8435 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings); 8436 } 8437 #endif 8471 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/, 8472 pfNeedsSaveSettings); 8438 8473 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() )); 8439 8474 mrc = rc; … … 8563 8598 8564 8599 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 8600 bool fMediaNeedsLocking = false; 8565 8601 8566 8602 /* enumerate new attachments */ … … 8581 8617 8582 8618 /** @todo convert all this Machine-based voodoo to MediumAttachment 8583 * based commit logic. */8619 * based commit logic. */ 8584 8620 if (fImplicit) 8585 8621 { … … 8592 8628 ) 8593 8629 { 8594 rc = pMedium->LockWrite(NULL); 8630 ComObjPtr<Medium> parent = pMedium->getParent(); 8631 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS); 8632 8633 /* update the appropriate lock list */ 8634 MediumLockList *pMediumLockList; 8635 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList); 8595 8636 AssertComRC(rc); 8596 8597 mData->mSession.mLockedMedia.push_back( 8598 Data::Session::LockedMedia::value_type( 8599 ComPtr<IMedium>(pMedium), true)); 8600 8601 /* also, relock the old hard disk which is a base for the 8602 * new diff for reading if the VM is online */ 8603 8604 ComObjPtr<Medium> parent = pMedium->getParent(); 8605 /* make the relock atomic */ 8606 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS); 8607 rc = parent->UnlockWrite(NULL); 8608 AssertComRC(rc); 8609 rc = parent->LockRead(NULL); 8610 AssertComRC(rc); 8611 8612 /* XXX actually we should replace the old entry in that 8613 * vector (write lock => read lock) but this would take 8614 * some effort. So lets just ignore the error code in 8615 * SessionMachine::unlockMedia(). */ 8616 mData->mSession.mLockedMedia.push_back( 8617 Data::Session::LockedMedia::value_type ( 8618 ComPtr<IMedium>(parent), false)); 8637 if (pMediumLockList) 8638 { 8639 /* unlock if there's a need to change the locking */ 8640 if (!fMediaNeedsLocking) 8641 { 8642 rc = mData->mSession.mLockedMedia.Unlock(); 8643 AssertComRC(rc); 8644 fMediaNeedsLocking = true; 8645 } 8646 rc = pMediumLockList->Update(parent, false); 8647 AssertComRC(rc); 8648 rc = pMediumLockList->Append(pMedium, true); 8649 AssertComRC(rc); 8650 } 8619 8651 } 8620 8652 … … 8643 8675 8644 8676 /* enumerate remaining old attachments and de-associate from the 8645 8677 * current machine state */ 8646 8678 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); 8647 8648 8679 it != oldAtts.end(); 8680 ++it) 8649 8681 { 8650 8682 MediumAttachment *pAttach = *it; … … 8652 8684 8653 8685 /* Detach only hard disks, since DVD/floppy media is detached 8654 8686 * instantly in MountMedium. */ 8655 8687 if (pAttach->getType() == DeviceType_HardDisk && pMedium) 8656 8688 { … … 8661 8693 AssertComRC(rc); 8662 8694 8663 if ( aOnline 8664 && pAttach->getType() == DeviceType_HardDisk) 8695 if (aOnline) 8665 8696 { 8666 /* unlock since not used anymore */ 8667 MediumState_T state; 8668 rc = pMedium->UnlockWrite(&state); 8669 /* the disk may be alredy relocked for reading above */ 8670 Assert(SUCCEEDED(rc) || state == MediumState_LockedRead); 8697 /* unlock since medium is not used anymore */ 8698 MediumLockList *pMediumLockList; 8699 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList); 8700 AssertComRC(rc); 8701 if (pMediumLockList) 8702 { 8703 rc = mData->mSession.mLockedMedia.Remove(pAttach); 8704 AssertComRC(rc); 8705 } 8671 8706 } 8672 8707 } 8708 } 8709 8710 /* take media locks again so that the locking state is consistent */ 8711 if (fMediaNeedsLocking) 8712 { 8713 Assert(aOnline); 8714 rc = mData->mSession.mLockedMedia.Lock(); 8715 AssertComRC(rc); 8673 8716 } 8674 8717 … … 10774 10817 || mData->mMachineState == MachineState_Restoring 10775 10818 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL); 10776 10777 try 10778 { 10779 HRESULT rc = S_OK; 10780 10781 ErrorInfoKeeper eik(true /* aIsNull */); 10782 MultiResult mrc(S_OK); 10783 10784 /* Lock all medium objects attached to the VM. 10785 * Get status for inaccessible media as well. */ 10786 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 10787 it != mMediaData->mAttachments.end(); 10788 ++it) 10789 { 10790 DeviceType_T devType = (*it)->getType(); 10791 ComObjPtr<Medium> medium = (*it)->getMedium(); 10792 10793 bool first = true; 10794 10795 /** @todo split out the media locking, and put it into 10796 * MediumImpl.cpp, as it needs this functionality too. */ 10797 while (!medium.isNull()) 10819 /* bail out if trying to lock things with already set up locking */ 10820 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL); 10821 10822 MultiResult mrc(S_OK); 10823 10824 /* Collect locking information for all medium objects attached to the VM. */ 10825 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 10826 it != mMediaData->mAttachments.end(); 10827 ++it) 10828 { 10829 MediumAttachment* pAtt = *it; 10830 DeviceType_T devType = pAtt->getType(); 10831 Medium *pMedium = pAtt->getMedium(); 10832 10833 MediumLockList *pMediumLockList(new MediumLockList()); 10834 // There can be attachments without a medium (floppy/dvd), and thus 10835 // it's impossible to create a medium lock list. It still makes sense 10836 // to have the empty medium lock list in the map in case a medium is 10837 // attached later. 10838 if (pMedium != NULL) 10839 { 10840 mrc = pMedium->createMediumLockList(devType != DeviceType_DVD, 10841 NULL, *pMediumLockList); 10842 if (FAILED(mrc)) 10798 10843 { 10799 MediumState_T mediumState = medium->getState(); 10800 10801 /* accessibility check must be first, otherwise locking 10802 * interferes with getting the medium state. */ 10803 if (mediumState == MediumState_Inaccessible) 10804 { 10805 rc = medium->RefreshState(&mediumState); 10806 if (FAILED(rc)) throw rc; 10807 10808 if (mediumState == MediumState_Inaccessible) 10809 { 10810 Bstr error; 10811 rc = medium->COMGETTER(LastAccessError)(error.asOutParam()); 10812 if (FAILED(rc)) throw rc; 10813 10814 Bstr loc; 10815 rc = medium->COMGETTER(Location)(loc.asOutParam()); 10816 if (FAILED(rc)) throw rc; 10817 10818 /* collect multiple errors */ 10819 eik.restore(); 10820 10821 /* be in sync with MediumBase::setStateError() */ 10822 Assert(!error.isEmpty()); 10823 mrc = setError(E_FAIL, 10824 tr("Medium '%ls' is not accessible. %ls"), 10825 loc.raw(), 10826 error.raw()); 10827 10828 eik.fetch(); 10829 } 10830 } 10831 10832 if (first) 10833 { 10834 if (devType != DeviceType_DVD) 10835 { 10836 /* HardDisk and Floppy medium must be locked for writing */ 10837 rc = medium->LockWrite(NULL); 10838 if (FAILED(rc)) throw rc; 10839 } 10840 else 10841 { 10842 /* DVD medium must be locked for reading */ 10843 rc = medium->LockRead(NULL); 10844 if (FAILED(rc)) throw rc; 10845 } 10846 10847 mData->mSession.mLockedMedia.push_back( 10848 Data::Session::LockedMedia::value_type( 10849 ComPtr<IMedium>(medium), true)); 10850 10851 first = false; 10852 } 10853 else 10854 { 10855 rc = medium->LockRead(NULL); 10856 if (FAILED(rc)) throw rc; 10857 10858 mData->mSession.mLockedMedia.push_back( 10859 Data::Session::LockedMedia::value_type( 10860 ComPtr<IMedium>(medium), false)); 10861 } 10862 10863 10864 /* no locks or callers here since there should be no way to 10865 * change the hard disk parent at this point (as it is still 10866 * attached to the machine) */ 10867 medium = medium->getParent(); 10844 delete pMediumLockList; 10845 mData->mSession.mLockedMedia.Clear(); 10846 break; 10868 10847 } 10869 10848 } 10870 10849 10871 /* @todo r=dj is this correct? first restoring the eik and then throwing? */ 10872 eik.restore(); 10873 HRESULT rc2 = (HRESULT)mrc; 10874 if (FAILED(rc2)) throw rc2; 10875 } 10876 catch (HRESULT aRC) 10877 { 10878 /* Unlock all locked media on failure */ 10879 unlockMedia(); 10880 return aRC; 10881 } 10882 10883 return S_OK; 10850 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList); 10851 if (FAILED(rc)) 10852 { 10853 mData->mSession.mLockedMedia.Clear(); 10854 mrc = setError(rc, 10855 tr("Collecting locking information for all attached media failed")); 10856 break; 10857 } 10858 } 10859 10860 if (SUCCEEDED(mrc)) 10861 { 10862 /* Now lock all media. If this fails, nothing is locked. */ 10863 HRESULT rc = mData->mSession.mLockedMedia.Lock(); 10864 if (FAILED(rc)) 10865 { 10866 mrc = setError(rc, 10867 tr("Locking of attached media failed")); 10868 } 10869 } 10870 10871 return mrc; 10884 10872 } 10885 10873 … … 10898 10886 ErrorInfoKeeper eik; 10899 10887 10900 HRESULT rc = S_OK; 10901 10902 for (Data::Session::LockedMedia::const_iterator 10903 it = mData->mSession.mLockedMedia.begin(); 10904 it != mData->mSession.mLockedMedia.end(); ++it) 10905 { 10906 MediumState_T state; 10907 if (it->second) 10908 rc = it->first->UnlockWrite(&state); 10909 else 10910 rc = it->first->UnlockRead(&state); 10911 10912 /* The second can happen if an object was re-locked in 10913 * Machine::fixupMedia(). The last can happen when e.g a DVD/Floppy 10914 * image was unmounted at runtime. */ 10915 Assert(SUCCEEDED(rc) || state == MediumState_LockedRead || state == MediumState_Created); 10916 } 10917 10918 mData->mSession.mLockedMedia.clear(); 10888 HRESULT rc = mData->mSession.mLockedMedia.Clear(); 10889 AssertComRC(rc); 10919 10890 } 10920 10891 … … 11021 10992 { 11022 10993 /* 11023 * delete the saved state after Console:: DiscardSavedState() is called10994 * delete the saved state after Console::ForgetSavedState() is called 11024 10995 * or if the VM process (owning a direct VM session) crashed while the 11025 10996 * VM was Saved … … 11155 11126 /* directControl may be already set to NULL here in #OnSessionEnd() 11156 11127 * called too early by the direct session process while there is still 11157 * some operation (like d iscarding the snapshot) in progress. The client11128 * some operation (like deleting the snapshot) in progress. The client 11158 11129 * process in this case is waiting inside Session::close() for the 11159 11130 * "end session" process object to complete, while #uninit() called by -
trunk/src/VBox/Main/MediumImpl.cpp
r27831 r28401 190 190 public: 191 191 Task(Medium *aMedium, Progress *aProgress) 192 : mMedium(aMedium), 192 : mVDOperationIfaces(NULL), 193 m_pfNeedsSaveSettings(NULL), 194 mMedium(aMedium), 193 195 mMediumCaller(aMedium), 194 m_pfNeedsSaveSettings(NULL),195 mVDOperationIfaces(NULL),196 196 mThread(NIL_RTTHREAD), 197 197 mProgress(aProgress) … … 229 229 bool isAsync() { return mThread != NIL_RTTHREAD; } 230 230 231 const ComObjPtr<Medium> mMedium; 232 AutoCaller mMediumCaller; 231 PVDINTERFACE mVDOperationIfaces; 233 232 234 233 // Whether the caller needs to call VirtualBox::saveSettings() after … … 237 236 bool *m_pfNeedsSaveSettings; 238 237 239 PVDINTERFACE mVDOperationIfaces; 238 const ComObjPtr<Medium> mMedium; 239 AutoCaller mMediumCaller; 240 241 friend HRESULT Medium::runNow(Medium::Task*, bool*); 240 242 241 243 protected: … … 279 281 Progress *aProgress, 280 282 Medium *aTarget, 281 MediumVariant_T aVariant) 283 MediumVariant_T aVariant, 284 MediumLockList *aMediumLockList, 285 bool fKeepMediumLockList = false) 282 286 : Medium::Task(aMedium, aProgress), 287 mpMediumLockList(aMediumLockList), 283 288 mTarget(aTarget), 284 289 mVariant(aVariant), 285 mTargetCaller(aTarget) 290 mTargetCaller(aTarget), 291 mfKeepMediumLockList(fKeepMediumLockList) 286 292 { 287 293 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL); … … 291 297 } 292 298 299 ~CreateDiffTask() 300 { 301 if (!mfKeepMediumLockList && mpMediumLockList) 302 delete mpMediumLockList; 303 } 304 305 MediumLockList *mpMediumLockList; 306 293 307 const ComObjPtr<Medium> mTarget; 294 308 MediumVariant_T mVariant; … … 298 312 299 313 AutoCaller mTargetCaller; 314 bool mfKeepMediumLockList; 300 315 }; 301 316 … … 306 321 Progress *aProgress, 307 322 Medium *aTarget, 323 MediumVariant_T aVariant, 308 324 Medium *aParent, 309 ImageChain *aSourceChain, 310 ImageChain *aParentChain, 311 MediumVariant_T aVariant) 325 MediumLockList *aSourceMediumLockList, 326 MediumLockList *aTargetMediumLockList, 327 bool fKeepSourceMediumLockList = false, 328 bool fKeepTargetMediumLockList = false) 312 329 : Medium::Task(aMedium, aProgress), 313 330 mTarget(aTarget), 314 331 mParent(aParent), 315 m SourceChain(aSourceChain),316 m ParentChain(aParentChain),332 mpSourceMediumLockList(aSourceMediumLockList), 333 mpTargetMediumLockList(aTargetMediumLockList), 317 334 mVariant(aVariant), 318 335 mTargetCaller(aTarget), 319 mParentCaller(aParent) 336 mParentCaller(aParent), 337 mfKeepSourceMediumLockList(fKeepSourceMediumLockList), 338 mfKeepTargetMediumLockList(fKeepTargetMediumLockList) 320 339 { 321 340 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL); … … 327 346 if (FAILED(mRC)) 328 347 return; 329 AssertReturnVoidStmt(aSourceChain != NULL, mRC = E_FAIL); 330 AssertReturnVoidStmt(aParentChain != NULL, mRC = E_FAIL); 348 AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL); 349 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL); 350 } 351 352 ~CloneTask() 353 { 354 if (!mfKeepSourceMediumLockList && mpSourceMediumLockList) 355 delete mpSourceMediumLockList; 356 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList) 357 delete mpTargetMediumLockList; 331 358 } 332 359 333 360 const ComObjPtr<Medium> mTarget; 334 361 const ComObjPtr<Medium> mParent; 335 std::auto_ptr<ImageChain> mSourceChain;336 std::auto_ptr<ImageChain> mParentChain;362 MediumLockList *mpSourceMediumLockList; 363 MediumLockList *mpTargetMediumLockList; 337 364 MediumVariant_T mVariant; 338 365 … … 342 369 AutoCaller mTargetCaller; 343 370 AutoCaller mParentCaller; 371 bool mfKeepSourceMediumLockList; 372 bool mfKeepTargetMediumLockList; 344 373 }; 345 374 … … 349 378 CompactTask(Medium *aMedium, 350 379 Progress *aProgress, 351 ImageChain *aImageChain) 380 MediumLockList *aMediumLockList, 381 bool fKeepMediumLockList = false) 352 382 : Medium::Task(aMedium, aProgress), 353 mImageChain(aImageChain) 354 { 355 AssertReturnVoidStmt(aImageChain != NULL, mRC = E_FAIL); 356 } 357 358 std::auto_ptr<ImageChain> mImageChain; 383 mpMediumLockList(aMediumLockList), 384 mfKeepMediumLockList(fKeepMediumLockList) 385 { 386 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL); 387 } 388 389 ~CompactTask() 390 { 391 if (!mfKeepMediumLockList && mpMediumLockList) 392 delete mpMediumLockList; 393 } 394 395 MediumLockList *mpMediumLockList; 359 396 360 397 private: 361 398 virtual HRESULT handler(); 399 400 bool mfKeepMediumLockList; 362 401 }; 363 402 … … 366 405 public: 367 406 ResetTask(Medium *aMedium, 368 Progress *aProgress) 369 : Medium::Task(aMedium, aProgress) 407 Progress *aProgress, 408 MediumLockList *aMediumLockList, 409 bool fKeepMediumLockList = false) 410 : Medium::Task(aMedium, aProgress), 411 mpMediumLockList(aMediumLockList), 412 mfKeepMediumLockList(fKeepMediumLockList) 370 413 {} 414 415 ~ResetTask() 416 { 417 if (!mfKeepMediumLockList && mpMediumLockList) 418 delete mpMediumLockList; 419 } 420 421 MediumLockList *mpMediumLockList; 371 422 372 423 private: 373 424 virtual HRESULT handler(); 425 426 bool mfKeepMediumLockList; 374 427 }; 375 428 … … 378 431 public: 379 432 DeleteTask(Medium *aMedium, 380 Progress *aProgress) 381 : Medium::Task(aMedium, aProgress) 433 Progress *aProgress, 434 MediumLockList *aMediumLockList, 435 bool fKeepMediumLockList = false) 436 : Medium::Task(aMedium, aProgress), 437 mpMediumLockList(aMediumLockList), 438 mfKeepMediumLockList(fKeepMediumLockList) 382 439 {} 440 441 ~DeleteTask() 442 { 443 if (!mfKeepMediumLockList && mpMediumLockList) 444 delete mpMediumLockList; 445 } 446 447 MediumLockList *mpMediumLockList; 383 448 384 449 private: 385 450 virtual HRESULT handler(); 451 452 bool mfKeepMediumLockList; 386 453 }; 387 454 … … 390 457 public: 391 458 MergeTask(Medium *aMedium, 459 Medium *aTarget, 460 bool fMergeForward, 461 Medium *aParentForTarget, 462 const MediaList &aChildrenToReparent, 392 463 Progress *aProgress, 393 MergeChain *aMergeChain) 464 MediumLockList *aMediumLockList, 465 bool fKeepMediumLockList = false) 394 466 : Medium::Task(aMedium, aProgress), 395 mMergeChain(aMergeChain) 396 { 397 AssertReturnVoidStmt(aMergeChain != NULL, mRC = E_FAIL); 398 } 399 400 std::auto_ptr<MergeChain> mMergeChain; 467 mTarget(aTarget), 468 mfMergeForward(fMergeForward), 469 mParentForTarget(aParentForTarget), 470 mChildrenToReparent(aChildrenToReparent), 471 mpMediumLockList(aMediumLockList), 472 mTargetCaller(aTarget), 473 mParentForTargetCaller(aParentForTarget), 474 mfChildrenCaller(false), 475 mfKeepMediumLockList(fKeepMediumLockList) 476 { 477 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL); 478 for (MediaList::const_iterator it = mChildrenToReparent.begin(); 479 it != mChildrenToReparent.end(); 480 ++it) 481 { 482 HRESULT rc2 = (*it)->addCaller(); 483 if (FAILED(rc2)) 484 { 485 mRC = E_FAIL; 486 for (MediaList::const_iterator it2 = mChildrenToReparent.begin(); 487 it2 != it; 488 --it2) 489 { 490 (*it2)->releaseCaller(); 491 } 492 return; 493 } 494 } 495 mfChildrenCaller = true; 496 } 497 498 ~MergeTask() 499 { 500 if (!mfKeepMediumLockList && mpMediumLockList) 501 delete mpMediumLockList; 502 if (mfChildrenCaller) 503 { 504 for (MediaList::const_iterator it = mChildrenToReparent.begin(); 505 it != mChildrenToReparent.end(); 506 ++it) 507 { 508 (*it)->releaseCaller(); 509 } 510 } 511 } 512 513 const ComObjPtr<Medium> mTarget; 514 bool mfMergeForward; 515 /* When mChildrenToReparent is empty then mParentForTarget is non-null. 516 * In other words: they are used in different cases. */ 517 const ComObjPtr<Medium> mParentForTarget; 518 MediaList mChildrenToReparent; 519 MediumLockList *mpMediumLockList; 401 520 402 521 private: 403 522 virtual HRESULT handler(); 523 524 AutoCaller mTargetCaller; 525 AutoCaller mParentForTargetCaller; 526 bool mfChildrenCaller; 527 bool mfKeepMediumLockList; 404 528 }; 405 529 … … 469 593 HRESULT Medium::CreateBaseTask::handler() 470 594 { 471 return mMedium->task ThreadCreateBase(*this);595 return mMedium->taskCreateBaseHandler(*this); 472 596 } 473 597 … … 477 601 HRESULT Medium::CreateDiffTask::handler() 478 602 { 479 return mMedium->task ThreadCreateDiff(*this);603 return mMedium->taskCreateDiffHandler(*this); 480 604 } 481 605 … … 485 609 HRESULT Medium::CloneTask::handler() 486 610 { 487 return mMedium->task ThreadClone(*this);611 return mMedium->taskCloneHandler(*this); 488 612 } 489 613 … … 493 617 HRESULT Medium::CompactTask::handler() 494 618 { 495 return mMedium->task ThreadCompact(*this);619 return mMedium->taskCompactHandler(*this); 496 620 } 497 621 … … 501 625 HRESULT Medium::ResetTask::handler() 502 626 { 503 return mMedium->task ThreadReset(*this);627 return mMedium->taskResetHandler(*this); 504 628 } 505 629 … … 509 633 HRESULT Medium::DeleteTask::handler() 510 634 { 511 return mMedium->task ThreadDelete(*this);635 return mMedium->taskDeleteHandler(*this); 512 636 } 513 637 … … 517 641 HRESULT Medium::MergeTask::handler() 518 642 { 519 return mMedium->taskThreadMerge(*this); 520 } 521 522 523 //////////////////////////////////////////////////////////////////////////////// 524 // 525 // Merge chain class 526 // 527 //////////////////////////////////////////////////////////////////////////////// 528 529 /** 530 * Helper class for merge operations. 531 * 532 * @note It is assumed that when modifying methods of this class are called, 533 * the medium tree lock is held in read mode. 534 */ 535 class Medium::MergeChain : public MediaList, 536 public com::SupportErrorInfoBase 537 { 538 public: 539 540 MergeChain(bool aForward, bool aIgnoreAttachments) 541 : mForward(aForward) 542 , mIgnoreAttachments(aIgnoreAttachments) {} 543 544 ~MergeChain() 545 { 546 for (iterator it = mChildren.begin(); it != mChildren.end(); ++ it) 547 { 548 HRESULT rc = (*it)->UnlockWrite(NULL); 549 AssertComRC(rc); 550 551 (*it)->releaseCaller(); 552 } 553 554 for (iterator it = begin(); it != end(); ++ it) 555 { 556 AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS); 557 Assert((*it)->m->state == MediumState_LockedWrite || 558 (*it)->m->state == MediumState_LockedRead || 559 (*it)->m->state == MediumState_Deleting); 560 if ((*it)->m->state == MediumState_LockedWrite) 561 (*it)->UnlockWrite(NULL); 562 else if ((*it)->m->state == MediumState_LockedRead) 563 (*it)->UnlockRead(NULL); 564 else 565 (*it)->m->state = MediumState_Created; 566 567 (*it)->releaseCaller(); 568 } 569 570 if (!mParent.isNull()) 571 mParent->releaseCaller(); 572 } 573 574 HRESULT addSource(Medium *aMedium) 575 { 576 HRESULT rc = aMedium->addCaller(); 577 if (FAILED(rc)) return rc; 578 579 AutoWriteLock alock(aMedium COMMA_LOCKVAL_SRC_POS); 580 581 if (mForward) 582 { 583 rc = checkChildrenAndAttachmentsAndImmutable(aMedium); 584 if (FAILED(rc)) 585 { 586 aMedium->releaseCaller(); 587 return rc; 588 } 589 } 590 591 /* We have to fetch the state with the COM method, cause it's possible 592 that the medium isn't fully initialized yet. */ 593 MediumState_T m; 594 rc = aMedium->RefreshState(&m); 595 if (FAILED(rc)) return rc; 596 /* go to Deleting */ 597 switch (m) 598 { 599 case MediumState_Created: 600 aMedium->m->state = MediumState_Deleting; 601 break; 602 default: 603 aMedium->releaseCaller(); 604 return aMedium->setStateError(); 605 } 606 607 push_front(aMedium); 608 609 if (mForward) 610 { 611 /* we will need parent to reparent target */ 612 if (!aMedium->m->pParent.isNull()) 613 { 614 rc = aMedium->m->pParent->addCaller(); 615 if (FAILED(rc)) return rc; 616 617 mParent = aMedium->m->pParent; 618 } 619 620 /* Include all images from base to source. */ 621 ComObjPtr<Medium> pParent = aMedium->m->pParent; 622 while (!pParent.isNull()) 623 { 624 rc = pParent->addCaller(); 625 if (FAILED(rc)) return rc; 626 627 rc = pParent->LockRead(NULL); 628 if (FAILED(rc)) return rc; 629 630 push_front(pParent); 631 pParent = pParent->m->pParent; 632 } 633 } 634 else 635 { 636 /* we will need to reparent children */ 637 for (MediaList::const_iterator it = aMedium->getChildren().begin(); 638 it != aMedium->getChildren().end(); 639 ++it) 640 { 641 ComObjPtr<Medium> pMedium = *it; 642 rc = pMedium->addCaller(); 643 if (FAILED(rc)) return rc; 644 645 rc = pMedium->LockWrite(NULL); 646 if (FAILED(rc)) 647 { 648 pMedium->releaseCaller(); 649 return rc; 650 } 651 652 mChildren.push_back(pMedium); 653 } 654 } 655 656 mSource = aMedium; 657 658 return S_OK; 659 } 660 661 HRESULT addTarget(Medium *aMedium) 662 { 663 HRESULT rc = aMedium->addCaller(); 664 if (FAILED(rc)) return rc; 665 666 AutoWriteLock alock(aMedium COMMA_LOCKVAL_SRC_POS); 667 668 if (!mForward) 669 { 670 rc = checkChildrenAndImmutable(aMedium); 671 if (FAILED(rc)) 672 { 673 aMedium->releaseCaller(); 674 return rc; 675 } 676 } 677 678 /* go to LockedWrite */ 679 rc = aMedium->LockWrite(NULL); 680 if (FAILED(rc)) 681 { 682 aMedium->releaseCaller(); 683 return rc; 684 } 685 686 push_front(aMedium); 687 688 mTarget = aMedium; 689 690 return S_OK; 691 } 692 693 HRESULT addIntermediate(Medium *aMedium) 694 { 695 HRESULT rc = aMedium->addCaller(); 696 if (FAILED(rc)) return rc; 697 698 AutoWriteLock alock(aMedium COMMA_LOCKVAL_SRC_POS); 699 700 rc = checkChildrenAndAttachments(aMedium); 701 if (FAILED(rc)) 702 { 703 aMedium->releaseCaller(); 704 return rc; 705 } 706 707 /* go to Deleting */ 708 switch (aMedium->m->state) 709 { 710 case MediumState_Created: 711 aMedium->m->state = MediumState_Deleting; 712 break; 713 default: 714 aMedium->releaseCaller(); 715 return aMedium->setStateError(); 716 } 717 718 push_front(aMedium); 719 720 return S_OK; 721 } 722 723 int targetIdx() 724 { 725 Assert(!mTarget.isNull()); 726 int idx = 0; 727 728 for (MediaList::const_iterator it = begin(); it != end(); ++it) 729 { 730 ComObjPtr<Medium> pMedium = *it; 731 732 /* Do we have the target? */ 733 if (pMedium == mTarget) 734 break; 735 736 idx++; 737 } 738 739 return idx; 740 } 741 742 int sourceIdx() 743 { 744 Assert(!mSource.isNull()); 745 int idx = 0; 746 747 for (MediaList::const_iterator it = begin(); it != end(); ++it) 748 { 749 ComObjPtr<Medium> pMedium = *it; 750 751 /* Do we have the source? */ 752 if (pMedium == mSource) 753 break; 754 755 idx++; 756 } 757 758 return idx; 759 } 760 761 bool isForward() const { return mForward; } 762 Medium *parent() const { return mParent; } 763 const MediaList& children() const { return mChildren; } 764 765 Medium *source() const 766 { AssertReturn(size() > 0, NULL); return mSource; } 767 768 Medium *target() const 769 { AssertReturn(size() > 0, NULL); return mTarget; } 770 771 protected: 772 773 // SupportErrorInfoBase interface 774 const GUID &mainInterfaceID() const { return COM_IIDOF(IMedium); } 775 const char *componentName() const { return Medium::ComponentName(); } 776 777 private: 778 779 HRESULT check(Medium *aMedium, bool aChildren, bool aAttachments, 780 bool aImmutable) 781 { 782 if (aChildren) 783 { 784 /* not going to multi-merge as it's too expensive */ 785 if (aMedium->getChildren().size() > 1) 786 { 787 return setError(E_FAIL, 788 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"), 789 aMedium->m->strLocationFull.raw(), 790 aMedium->getChildren().size()); 791 } 792 } 793 794 if (aAttachments && !mIgnoreAttachments) 795 { 796 if (aMedium->m->backRefs.size() != 0) 797 return setError(E_FAIL, 798 tr("Medium '%s' is attached to %d virtual machines"), 799 aMedium->m->strLocationFull.raw(), 800 aMedium->m->backRefs.size()); 801 } 802 803 if (aImmutable) 804 { 805 if (aMedium->m->type == MediumType_Immutable) 806 return setError(E_FAIL, 807 tr("Medium '%s' is immutable"), 808 aMedium->m->strLocationFull.raw()); 809 } 810 811 return S_OK; 812 } 813 814 HRESULT checkChildren(Medium *aMedium) 815 { return check(aMedium, true, false, false); } 816 817 HRESULT checkChildrenAndImmutable(Medium *aMedium) 818 { return check(aMedium, true, false, true); } 819 820 HRESULT checkChildrenAndAttachments(Medium *aMedium) 821 { return check(aMedium, true, true, false); } 822 823 HRESULT checkChildrenAndAttachmentsAndImmutable(Medium *aMedium) 824 { return check(aMedium, true, true, true); } 825 826 /** true if forward merge, false if backward */ 827 bool mForward : 1; 828 /** true to not perform attachment checks */ 829 bool mIgnoreAttachments : 1; 830 831 /** Parent of the source when forward merge (if any) */ 832 ComObjPtr <Medium> mParent; 833 /** Children of the source when backward merge (if any) */ 834 MediaList mChildren; 835 /** Source image */ 836 ComObjPtr <Medium> mSource; 837 /** Target image */ 838 ComObjPtr <Medium> mTarget; 839 }; 840 841 //////////////////////////////////////////////////////////////////////////////// 842 // 843 // ImageChain class 844 // 845 //////////////////////////////////////////////////////////////////////////////// 846 847 /** 848 * Helper class for image operations involving the entire parent chain. 849 * 850 * @note It is assumed that when modifying methods of this class are called, 851 * the medium tree lock is held in read mode. 852 */ 853 class Medium::ImageChain : public MediaList, 854 public com::SupportErrorInfoBase 855 { 856 public: 857 858 ImageChain() {} 859 860 ~ImageChain() 861 { 862 /* empty? */ 863 if (begin() != end()) 864 { 865 MediaList::const_iterator last = end(); 866 last--; 867 for (MediaList::const_iterator it = begin(); it != end(); ++ it) 868 { 869 AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS); 870 if (it == last) 871 { 872 Assert( (*it)->m->state == MediumState_LockedRead 873 || (*it)->m->state == MediumState_LockedWrite); 874 if ((*it)->m->state == MediumState_LockedRead) 875 (*it)->UnlockRead(NULL); 876 else if ((*it)->m->state == MediumState_LockedWrite) 877 (*it)->UnlockWrite(NULL); 878 } 879 else 880 { 881 Assert((*it)->m->state == MediumState_LockedRead); 882 if ((*it)->m->state == MediumState_LockedRead) 883 (*it)->UnlockRead(NULL); 884 } 885 886 (*it)->releaseCaller(); 887 } 888 } 889 } 890 891 HRESULT addImage(Medium *aMedium) 892 { 893 HRESULT rc = aMedium->addCaller(); 894 if (FAILED(rc)) return rc; 895 896 push_front(aMedium); 897 898 return S_OK; 899 } 900 901 HRESULT lockImagesRead() 902 { 903 /* Lock all disks in the chain in {parent, child} order, 904 * and make sure they are accessible. */ 905 /// @todo code duplication with SessionMachine::lockMedia, see below 906 ErrorInfoKeeper eik(true /* aIsNull */); 907 MultiResult mrc(S_OK); 908 for (MediaList::const_iterator it = begin(); it != end(); ++ it) 909 { 910 HRESULT rc = S_OK; 911 MediumState_T mediumState = (*it)->getState(); 912 913 /* accessibility check must be first, otherwise locking 914 * interferes with getting the medium state. */ 915 if (mediumState == MediumState_Inaccessible) 916 { 917 rc = (*it)->RefreshState(&mediumState); 918 if (FAILED(rc)) return rc; 919 920 if (mediumState == MediumState_Inaccessible) 921 { 922 Bstr error; 923 rc = (*it)->COMGETTER(LastAccessError)(error.asOutParam()); 924 if (FAILED(rc)) return rc; 925 926 Bstr loc; 927 rc = (*it)->COMGETTER(Location)(loc.asOutParam()); 928 if (FAILED(rc)) return rc; 929 930 /* collect multiple errors */ 931 eik.restore(); 932 933 /* be in sync with Medium::setStateError() */ 934 Assert(!error.isEmpty()); 935 mrc = setError(E_FAIL, 936 tr("Medium '%ls' is not accessible. %ls"), 937 loc.raw(), error.raw()); 938 939 eik.fetch(); 940 } 941 } 942 943 rc = (*it)->LockRead(&mediumState); 944 if (FAILED(rc)) return rc; 945 } 946 947 eik.restore(); 948 HRESULT rc2 = (HRESULT)mrc; 949 if (FAILED(rc2)) return rc2; 950 951 return S_OK; 952 } 953 954 HRESULT lockImagesReadAndLastWrite() 955 { 956 /* Lock all disks in the chain in {parent, child} order, 957 * and make sure they are accessible. */ 958 /// @todo code duplication with SessionMachine::lockMedia, see below 959 ErrorInfoKeeper eik(true /* aIsNull */); 960 MultiResult mrc(S_OK); 961 MediaList::const_iterator last = end(); 962 last--; 963 for (MediaList::const_iterator it = begin(); it != end(); ++ it) 964 { 965 HRESULT rc = S_OK; 966 MediumState_T mediumState = (*it)->getState(); 967 968 /* accessibility check must be first, otherwise locking 969 * interferes with getting the medium state. */ 970 if (mediumState == MediumState_Inaccessible) 971 { 972 rc = (*it)->RefreshState(&mediumState); 973 if (FAILED(rc)) return rc; 974 975 if (mediumState == MediumState_Inaccessible) 976 { 977 Bstr error; 978 rc = (*it)->COMGETTER(LastAccessError)(error.asOutParam()); 979 if (FAILED(rc)) return rc; 980 981 Bstr loc; 982 rc = (*it)->COMGETTER(Location)(loc.asOutParam()); 983 if (FAILED(rc)) return rc; 984 985 /* collect multiple errors */ 986 eik.restore(); 987 988 /* be in sync with Medium::setStateError() */ 989 Assert(!error.isEmpty()); 990 mrc = setError(E_FAIL, 991 tr("Medium '%ls' is not accessible. %ls"), 992 loc.raw(), error.raw()); 993 994 eik.fetch(); 995 } 996 } 997 998 if (it == last) 999 rc = (*it)->LockWrite(&mediumState); 1000 else 1001 rc = (*it)->LockRead(&mediumState); 1002 } 1003 1004 eik.restore(); 1005 HRESULT rc2 = (HRESULT)mrc; 1006 if (FAILED(rc2)) return rc2; 1007 1008 return S_OK; 1009 } 1010 1011 protected: 1012 1013 // SupportErrorInfoBase interface 1014 const GUID &mainInterfaceID() const { return COM_IIDOF(IMedium); } 1015 const char *componentName() const { return Medium::ComponentName(); } 1016 1017 private: 1018 1019 }; 643 return mMedium->taskMergeHandler(*this); 644 } 1020 645 1021 646 … … 1345 970 * XML format version change if we wish) */ 1346 971 for (settings::PropertiesMap::const_iterator it = data.properties.begin(); 1347 it != data.properties.end(); ++ 972 it != data.properties.end(); ++it) 1348 973 { 1349 974 const Utf8Str &name = it->first; … … 1496 1121 /* we are being uninitialized after've been deleted by merge. 1497 1122 * Reparenting has already been done so don't touch it here (we are 1498 * now orphans and remo eDependentChild() will assert) */1123 * now orphans and removeDependentChild() will assert) */ 1499 1124 Assert(m->pParent.isNull()); 1500 1125 } … … 1957 1582 size_t i = 0; 1958 1583 for (BackRefList::const_iterator it = m->backRefs.begin(); 1959 it != m->backRefs.end(); ++ it, ++i)1584 it != m->backRefs.end(); ++it, ++i) 1960 1585 { 1961 1586 it->machineId.toUtf16().detachTo(&machineIds[i]); … … 2013 1638 Guid id(aMachineId); 2014 1639 for (BackRefList::const_iterator it = m->backRefs.begin(); 2015 it != m->backRefs.end(); ++ 1640 it != m->backRefs.end(); ++it) 2016 1641 { 2017 1642 if (it->machineId == id) … … 2135 1760 LogFlowThisFunc(("Failing - state=%d\n", m->state)); 2136 1761 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 2137 tr 1762 tr("Medium '%s' is not locked for reading"), 2138 1763 m->strLocationFull.raw()); 2139 1764 break; … … 2220 1845 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, getLocationFull().c_str())); 2221 1846 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 2222 tr 1847 tr("Medium '%s' is not locked for writing"), 2223 1848 m->strLocationFull.raw()); 2224 1849 break; … … 2433 2058 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2434 2059 2435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2436 2437 aVariant = (MediumVariant_T)((unsigned)aVariant & (unsigned)~MediumVariant_Diff); 2438 if ( !(aVariant & MediumVariant_Fixed) 2439 && !(m->formatObj->capabilities() & MediumFormatCapabilities_CreateDynamic)) 2440 return setError(VBOX_E_NOT_SUPPORTED, 2441 tr("Hard disk format '%s' does not support dynamic storage creation"), 2442 m->strFormat.raw()); 2443 if ( (aVariant & MediumVariant_Fixed) 2444 && !(m->formatObj->capabilities() & MediumFormatCapabilities_CreateDynamic)) 2445 return setError(VBOX_E_NOT_SUPPORTED, 2446 tr("Hard disk format '%s' does not support fixed storage creation"), 2447 m->strFormat.raw()); 2448 2449 switch (m->state) 2450 { 2451 case MediumState_NotCreated: 2452 break; 2453 default: 2454 return setStateError(); 2455 } 2456 2457 ComObjPtr <Progress> progress; 2458 progress.createObject(); 2459 HRESULT rc = progress->init(m->pVirtualBox, 2460 static_cast<IMedium*>(this), 2461 (aVariant & MediumVariant_Fixed) 2462 ? BstrFmt(tr("Creating fixed hard disk storage unit '%s'"), m->strLocationFull.raw()) 2463 : BstrFmt(tr("Creating dynamic hard disk storage unit '%s'"), m->strLocationFull.raw()), 2464 TRUE /* aCancelable */); 2465 if (FAILED(rc)) return rc; 2466 2467 /* setup task object to carry out the operation asynchronously */ 2468 Medium::Task *pTask(new Medium::CreateBaseTask(this, progress, 2469 aLogicalSize, aVariant)); 2470 rc = pTask->rc(); 2471 if (FAILED(rc)) 2472 { 2060 HRESULT rc = S_OK; 2061 ComObjPtr <Progress> pProgress; 2062 Medium::Task *pTask = NULL; 2063 2064 try 2065 { 2066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2067 2068 aVariant = (MediumVariant_T)((unsigned)aVariant & (unsigned)~MediumVariant_Diff); 2069 if ( !(aVariant & MediumVariant_Fixed) 2070 && !(m->formatObj->capabilities() & MediumFormatCapabilities_CreateDynamic)) 2071 throw setError(VBOX_E_NOT_SUPPORTED, 2072 tr("Hard disk format '%s' does not support dynamic storage creation"), 2073 m->strFormat.raw()); 2074 if ( (aVariant & MediumVariant_Fixed) 2075 && !(m->formatObj->capabilities() & MediumFormatCapabilities_CreateDynamic)) 2076 throw setError(VBOX_E_NOT_SUPPORTED, 2077 tr("Hard disk format '%s' does not support fixed storage creation"), 2078 m->strFormat.raw()); 2079 2080 if (m->state != MediumState_NotCreated) 2081 throw setStateError(); 2082 2083 pProgress.createObject(); 2084 rc = pProgress->init(m->pVirtualBox, 2085 static_cast<IMedium*>(this), 2086 (aVariant & MediumVariant_Fixed) 2087 ? BstrFmt(tr("Creating fixed hard disk storage unit '%s'"), m->strLocationFull.raw()) 2088 : BstrFmt(tr("Creating dynamic hard disk storage unit '%s'"), m->strLocationFull.raw()), 2089 TRUE /* aCancelable */); 2090 if (FAILED(rc)) throw rc; 2091 2092 /* setup task object to carry out the operation asynchronously */ 2093 pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize, 2094 aVariant); 2095 rc = pTask->rc(); 2473 2096 AssertComRC(rc); 2097 if (FAILED(rc)) throw rc; 2098 2099 m->state = MediumState_Creating; 2100 } 2101 catch (HRESULT aRC) { rc = aRC; } 2102 2103 if (SUCCEEDED(rc)) 2104 { 2105 rc = startThread(pTask); 2106 2107 if (SUCCEEDED(rc)) 2108 pProgress.queryInterfaceTo(aProgress); 2109 } 2110 else if (pTask != NULL) 2474 2111 delete pTask; 2475 return rc; 2476 } 2477 2478 rc = startThread(pTask); 2479 if (FAILED(rc)) return rc; 2480 2481 /* go to Creating state on success */ 2482 m->state = MediumState_Creating; 2483 2484 progress.queryInterfaceTo(aProgress); 2485 2486 return S_OK; 2112 2113 return rc; 2487 2114 } 2488 2115 … … 2494 2121 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2495 2122 2496 ComObjPtr <Progress> progress; 2497 2498 HRESULT rc = deleteStorageNoWait(progress); 2123 ComObjPtr <Progress> pProgress; 2124 2125 HRESULT rc = deleteStorage(&pProgress, false /* aWait */, 2126 NULL /* pfNeedsSaveSettings */); 2499 2127 if (SUCCEEDED(rc)) 2500 p rogress.queryInterfaceTo(aProgress);2128 pProgress.queryInterfaceTo(aProgress); 2501 2129 2502 2130 return rc; … … 2522 2150 m->strLocationFull.raw()); 2523 2151 2524 /* We want to be locked for reading as long as our diff child is being 2525 * created */ 2526 HRESULT rc = LockRead(NULL); 2527 if (FAILED(rc)) return rc; 2528 2529 ComObjPtr <Progress> progress; 2530 2531 rc = createDiffStorageNoWait(diff, aVariant, progress); 2152 /* Apply the normal locking logic to the entire chain. */ 2153 MediumLockList *pMediumLockList(new MediumLockList()); 2154 HRESULT rc = createMediumLockList(true, this, *pMediumLockList); 2532 2155 if (FAILED(rc)) 2533 2156 { 2534 HRESULT rc2 = UnlockRead(NULL); 2535 AssertComRC(rc2); 2536 /* Note: on success, the task will unlock this */ 2537 } 2157 delete pMediumLockList; 2158 return rc; 2159 } 2160 2161 ComObjPtr <Progress> pProgress; 2162 2163 rc = createDiffStorage(diff, aVariant, pMediumLockList, &pProgress, 2164 false /* aWait */, NULL /* pfNeedsSaveSettings*/); 2165 if (FAILED(rc)) 2166 delete pMediumLockList; 2538 2167 else 2539 p rogress.queryInterfaceTo(aProgress);2168 pProgress.queryInterfaceTo(aProgress); 2540 2169 2541 2170 return rc; 2542 2171 } 2543 2172 2544 STDMETHODIMP Medium::MergeTo(IN_BSTR /* aTargetId */, IProgress ** /* aProgress */) 2545 { 2173 STDMETHODIMP Medium::MergeTo(IMedium *aTarget, IProgress **aProgress) 2174 { 2175 CheckComArgNotNull(aTarget); 2176 CheckComArgOutPointerValid(aProgress); 2177 ComAssertRet(aTarget != this, E_INVALIDARG); 2178 2546 2179 AutoCaller autoCaller(this); 2547 2180 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2548 2181 2549 ReturnComNotImplemented(); 2182 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget); 2183 2184 bool fMergeForward = false; 2185 ComObjPtr<Medium> pParentForTarget; 2186 MediaList childrenToReparent; 2187 MediumLockList *pMediumLockList = NULL; 2188 2189 HRESULT rc = S_OK; 2190 2191 rc = prepareMergeTo(pTarget, NULL, NULL, fMergeForward, pParentForTarget, 2192 childrenToReparent, pMediumLockList); 2193 if (FAILED(rc)) return rc; 2194 2195 ComObjPtr <Progress> pProgress; 2196 2197 rc = mergeTo(pTarget, fMergeForward, pParentForTarget, childrenToReparent, 2198 pMediumLockList, &pProgress, false /* aWait */, 2199 NULL /* pfNeedsSaveSettings */); 2200 if (FAILED(rc)) 2201 cancelMergeTo(childrenToReparent, pMediumLockList); 2202 else 2203 pProgress.queryInterfaceTo(aProgress); 2204 2205 return rc; 2550 2206 } 2551 2207 … … 2557 2213 CheckComArgNotNull(aTarget); 2558 2214 CheckComArgOutPointerValid(aProgress); 2215 ComAssertRet(aTarget != this, E_INVALIDARG); 2559 2216 2560 2217 AutoCaller autoCaller(this); 2561 2218 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2562 2219 2563 ComObjPtr<Medium> target = static_cast<Medium*>(aTarget);2564 ComObjPtr<Medium> p arent;2220 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget); 2221 ComObjPtr<Medium> pParent; 2565 2222 if (aParent) 2566 parent = static_cast<Medium*>(aParent); 2567 2568 ComObjPtr<Progress> progress; 2223 pParent = static_cast<Medium*>(aParent); 2224 2569 2225 HRESULT rc = S_OK; 2226 ComObjPtr<Progress> pProgress; 2227 Medium::Task *pTask = NULL; 2570 2228 2571 2229 try … … 2574 2232 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 2575 2233 // and we need to write-lock the images involved 2576 AutoMultiWriteLock3 alock(this, target, parent COMMA_LOCKVAL_SRC_POS); 2577 2578 if ( target->m->state != MediumState_NotCreated 2579 && target->m->state != MediumState_Created) 2580 throw target->setStateError(); 2581 2582 /** @todo separate out creating/locking an image chain from 2583 * SessionMachine::lockMedia and use it from here too. 2584 * logically this belongs into Medium functionality. */ 2585 2586 /* Build the source chain and lock images in the proper order. */ 2587 std::auto_ptr<ImageChain> sourceChain(new ImageChain()); 2588 2589 for (Medium *hd = this; 2590 hd; 2591 hd = hd->m->pParent) 2592 { 2593 rc = sourceChain->addImage(hd); 2594 if (FAILED(rc)) throw rc; 2595 } 2596 rc = sourceChain->lockImagesRead(); 2234 AutoMultiWriteLock3 alock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS); 2235 2236 if ( pTarget->m->state != MediumState_NotCreated 2237 && pTarget->m->state != MediumState_Created) 2238 throw pTarget->setStateError(); 2239 2240 /* Build the source lock list. */ 2241 MediumLockList *pSourceMediumLockList(new MediumLockList()); 2242 rc = createMediumLockList(false, NULL, 2243 *pSourceMediumLockList); 2244 if (FAILED(rc)) 2245 { 2246 delete pSourceMediumLockList; 2247 throw rc; 2248 } 2249 2250 /* Build the target lock list (including the to-be parent chain). */ 2251 MediumLockList *pTargetMediumLockList(new MediumLockList()); 2252 rc = pTarget->createMediumLockList(true, pParent, 2253 *pTargetMediumLockList); 2254 if (FAILED(rc)) 2255 { 2256 delete pSourceMediumLockList; 2257 delete pTargetMediumLockList; 2258 throw rc; 2259 } 2260 2261 rc = pSourceMediumLockList->Lock(); 2262 if (FAILED(rc)) 2263 { 2264 delete pSourceMediumLockList; 2265 delete pTargetMediumLockList; 2266 throw setError(rc, 2267 tr("Failed to lock source media '%ls'"), 2268 getLocationFull().raw()); 2269 } 2270 rc = pTargetMediumLockList->Lock(); 2271 if (FAILED(rc)) 2272 { 2273 delete pSourceMediumLockList; 2274 delete pTargetMediumLockList; 2275 throw setError(rc, 2276 tr("Failed to lock target media '%ls'"), 2277 pTarget->getLocationFull().raw()); 2278 } 2279 2280 pProgress.createObject(); 2281 rc = pProgress->init(m->pVirtualBox, 2282 static_cast <IMedium *>(this), 2283 BstrFmt(tr("Creating clone hard disk '%s'"), pTarget->m->strLocationFull.raw()), 2284 TRUE /* aCancelable */); 2285 if (FAILED(rc)) 2286 { 2287 delete pSourceMediumLockList; 2288 delete pTargetMediumLockList; 2289 throw rc; 2290 } 2291 2292 /* setup task object to carry out the operation asynchronously */ 2293 pTask = new Medium::CloneTask(this, pProgress, pTarget, aVariant, 2294 pParent, pSourceMediumLockList, 2295 pTargetMediumLockList); 2296 rc = pTask->rc(); 2297 AssertComRC(rc); 2597 2298 if (FAILED(rc)) throw rc; 2598 2299 2599 /* Build the parent chain and lock images in the proper order. */ 2600 std::auto_ptr<ImageChain> parentChain(new ImageChain()); 2601 2602 for (Medium *hd = parent; 2603 hd; 2604 hd = hd->m->pParent) 2605 { 2606 rc = parentChain->addImage(hd); 2607 if (FAILED(rc)) throw rc; 2608 } 2609 if (target->m->state == MediumState_Created) 2610 { 2611 /* If we're cloning to an existing image the parent chain also 2612 * contains the target image, and it gets locked for writing. */ 2613 rc = parentChain->addImage(target); 2614 if (FAILED(rc)) throw rc; 2615 rc = parentChain->lockImagesReadAndLastWrite(); 2616 if (FAILED(rc)) throw rc; 2617 } 2618 else 2619 { 2620 rc = parentChain->lockImagesRead(); 2621 if (FAILED(rc)) throw rc; 2622 } 2623 2624 progress.createObject(); 2625 rc = progress->init(m->pVirtualBox, 2626 static_cast <IMedium *>(this), 2627 BstrFmt(tr("Creating clone hard disk '%s'"), target->m->strLocationFull.raw()), 2628 TRUE /* aCancelable */); 2629 if (FAILED(rc)) throw rc; 2630 2631 /* setup task object to carry out the operation asynchronously */ 2632 Medium::Task *pTask(new Medium::CloneTask(this, progress, target, 2633 parent, 2634 sourceChain.release(), 2635 parentChain.release(), 2636 aVariant)); 2637 rc = pTask->rc(); 2638 if (FAILED(rc)) 2639 { 2640 AssertComRC(rc); 2641 delete pTask; 2642 throw rc; 2643 } 2644 2300 if (pTarget->m->state == MediumState_NotCreated) 2301 pTarget->m->state = MediumState_Creating; 2302 } 2303 catch (HRESULT aRC) { rc = aRC; } 2304 2305 if (SUCCEEDED(rc)) 2306 { 2645 2307 rc = startThread(pTask); 2646 if (FAILED(rc)) throw rc; 2647 2648 if (target->m->state == MediumState_NotCreated) 2649 { 2650 /* go to Creating state before leaving the lock */ 2651 target->m->state = MediumState_Creating; 2652 } 2653 } 2654 catch (HRESULT aRC) 2655 { 2656 rc = aRC; 2657 } 2658 2659 if (SUCCEEDED(rc)) 2660 progress.queryInterfaceTo(aProgress); 2308 2309 if (SUCCEEDED(rc)) 2310 pProgress.queryInterfaceTo(aProgress); 2311 } 2312 else if (pTask != NULL) 2313 delete pTask; 2661 2314 2662 2315 return rc; … … 2670 2323 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2671 2324 2672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);2673 2674 ComObjPtr <Progress> progress;2675 2676 2325 HRESULT rc = S_OK; 2326 ComObjPtr <Progress> pProgress; 2327 Medium::Task *pTask = NULL; 2677 2328 2678 2329 try 2679 2330 { 2680 /** @todo separate out creating/locking an image chain from 2681 * SessionMachine::lockMedia and use it from here too. 2682 * logically this belongs into Medium functionality. */ 2683 2684 /* Build the image chain and lock images in the proper order. */ 2685 std::auto_ptr<ImageChain> imgChain(new ImageChain()); 2686 2687 /* we walk the image tree */ 2688 AutoReadLock srcTreeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 2689 for (Medium *hd = this; 2690 hd; 2691 hd = hd->m->pParent) 2692 { 2693 rc = imgChain->addImage(hd); 2694 if (FAILED(rc)) throw rc; 2695 } 2696 rc = imgChain->lockImagesReadAndLastWrite(); 2331 /* We need to lock both the current object, and the tree lock (would 2332 * cause a lock order violation otherwise) for createMediumLockList. */ 2333 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(), 2334 this->lockHandle() 2335 COMMA_LOCKVAL_SRC_POS); 2336 2337 /* Build the medium lock list. */ 2338 MediumLockList *pMediumLockList(new MediumLockList()); 2339 rc = createMediumLockList(true, NULL, 2340 *pMediumLockList); 2341 if (FAILED(rc)) 2342 { 2343 delete pMediumLockList; 2344 throw rc; 2345 } 2346 2347 rc = pMediumLockList->Lock(); 2348 if (FAILED(rc)) 2349 { 2350 delete pMediumLockList; 2351 throw setError(rc, 2352 tr("Failed to lock media when compacting '%ls'"), 2353 getLocationFull().raw()); 2354 } 2355 2356 pProgress.createObject(); 2357 rc = pProgress->init(m->pVirtualBox, 2358 static_cast <IMedium *>(this), 2359 BstrFmt(tr("Compacting hard disk '%s'"), m->strLocationFull.raw()), 2360 TRUE /* aCancelable */); 2361 if (FAILED(rc)) 2362 { 2363 delete pMediumLockList; 2364 throw rc; 2365 } 2366 2367 /* setup task object to carry out the operation asynchronously */ 2368 pTask = new Medium::CompactTask(this, pProgress, pMediumLockList); 2369 rc = pTask->rc(); 2370 AssertComRC(rc); 2697 2371 if (FAILED(rc)) throw rc; 2698 2699 progress.createObject(); 2700 rc = progress->init(m->pVirtualBox, 2701 static_cast <IMedium *>(this), 2702 BstrFmt(tr("Compacting hard disk '%s'"), m->strLocationFull.raw()), 2703 TRUE /* aCancelable */); 2704 if (FAILED(rc)) throw rc; 2705 2706 /* setup task object to carry out the operation asynchronously */ 2707 Medium::Task *pTask(new Medium::CompactTask(this, progress, 2708 imgChain.release())); 2709 rc = pTask->rc(); 2710 if (FAILED(rc)) 2711 { 2712 AssertComRC(rc); 2713 delete pTask; 2714 throw rc; 2715 } 2716 2372 } 2373 catch (HRESULT aRC) { rc = aRC; } 2374 2375 if (SUCCEEDED(rc)) 2376 { 2717 2377 rc = startThread(pTask); 2718 if (FAILED(rc)) throw rc; 2719 } 2720 catch (HRESULT aRC) 2721 { 2722 rc = aRC; 2723 } 2724 2725 if (SUCCEEDED(rc)) 2726 progress.queryInterfaceTo(aProgress); 2378 2379 if (SUCCEEDED(rc)) 2380 pProgress.queryInterfaceTo(aProgress); 2381 } 2382 else if (pTask != NULL) 2383 delete pTask; 2727 2384 2728 2385 return rc; … … 2748 2405 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2749 2406 2750 /* canClose() needs the tree lock */ 2751 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(), 2752 this->lockHandle() 2753 COMMA_LOCKVAL_SRC_POS); 2754 2755 LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str())); 2756 2757 if (m->pParent.isNull()) 2758 return setError(VBOX_E_NOT_SUPPORTED, 2759 tr ("Hard disk '%s' is not differencing"), 2760 m->strLocationFull.raw()); 2761 2762 HRESULT rc = canClose(); 2763 if (FAILED(rc)) return rc; 2764 2765 rc = LockWrite(NULL); 2766 if (FAILED(rc)) return rc; 2767 2768 ComObjPtr <Progress> progress; 2407 HRESULT rc = S_OK; 2408 ComObjPtr <Progress> pProgress; 2409 Medium::Task *pTask = NULL; 2769 2410 2770 2411 try 2771 2412 { 2772 progress.createObject(); 2773 rc = progress->init(m->pVirtualBox, 2774 static_cast<IMedium*>(this), 2775 BstrFmt(tr("Resetting differencing hard disk '%s'"), m->strLocationFull.raw()), 2776 FALSE /* aCancelable */); 2413 /* canClose() needs the tree lock */ 2414 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(), 2415 this->lockHandle() 2416 COMMA_LOCKVAL_SRC_POS); 2417 2418 LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str())); 2419 2420 if (m->pParent.isNull()) 2421 throw setError(VBOX_E_NOT_SUPPORTED, 2422 tr("Hard disk '%s' is not differencing"), 2423 m->strLocationFull.raw()); 2424 2425 rc = canClose(); 2777 2426 if (FAILED(rc)) throw rc; 2778 2427 2428 /* Build the medium lock list. */ 2429 MediumLockList *pMediumLockList(new MediumLockList()); 2430 rc = createMediumLockList(true, NULL, 2431 *pMediumLockList); 2432 if (FAILED(rc)) 2433 { 2434 delete pMediumLockList; 2435 throw rc; 2436 } 2437 2438 rc = pMediumLockList->Lock(); 2439 if (FAILED(rc)) 2440 { 2441 delete pMediumLockList; 2442 throw setError(rc, 2443 tr("Failed to lock media when resetting '%ls'"), 2444 getLocationFull().raw()); 2445 } 2446 2447 pProgress.createObject(); 2448 rc = pProgress->init(m->pVirtualBox, 2449 static_cast<IMedium*>(this), 2450 BstrFmt(tr("Resetting differencing hard disk '%s'"), m->strLocationFull.raw()), 2451 FALSE /* aCancelable */); 2452 if (FAILED(rc)) throw rc; 2453 2779 2454 /* setup task object to carry out the operation asynchronously */ 2780 Medium::Task *pTask(new Medium::ResetTask(this, progress));2455 pTask = new Medium::ResetTask(this, pProgress, pMediumLockList); 2781 2456 rc = pTask->rc(); 2782 if (FAILED(rc)) 2783 { 2784 AssertComRC(rc); 2457 AssertComRC(rc); 2458 if (FAILED(rc)) throw rc; 2459 } 2460 catch (HRESULT aRC) { rc = aRC; } 2461 2462 if (SUCCEEDED(rc)) 2463 { 2464 rc = startThread(pTask); 2465 2466 if (SUCCEEDED(rc)) 2467 pProgress.queryInterfaceTo(aProgress); 2468 } 2469 else 2470 { 2471 /* Note: on success, the task will unlock this */ 2472 { 2473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2474 HRESULT rc2 = UnlockWrite(NULL); 2475 AssertComRC(rc2); 2476 } 2477 if (pTask != NULL) 2785 2478 delete pTask; 2786 throw rc; 2787 } 2788 2789 rc = startThread(pTask); 2790 if (FAILED(rc)) throw rc; 2791 } 2792 catch (HRESULT aRC) 2793 { 2794 rc = aRC; 2795 } 2796 2797 if (FAILED(rc)) 2798 { 2799 HRESULT rc2 = UnlockWrite(NULL); 2800 AssertComRC(rc2); 2801 /* Note: on success, the task will unlock this */ 2802 } 2803 else 2804 progress.queryInterfaceTo(aProgress); 2479 } 2805 2480 2806 2481 LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc)); … … 3126 2801 AssertComRCReturnVoid(autoCaller.rc()); 3127 2802 3128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);3129 3130 2803 /* we access children() */ 3131 2804 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 2805 2806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3132 2807 3133 2808 updatePath(aOldPath, aNewPath); … … 3136 2811 for (MediaList::const_iterator it = getChildren().begin(); 3137 2812 it != getChildren().end(); 3138 ++ 2813 ++it) 3139 2814 { 3140 2815 (*it)->updatePaths(aOldPath, aNewPath); … … 3214 2889 3215 2890 for (BackRefList::const_iterator it = m->backRefs.begin(); 3216 it != m->backRefs.end(); ++ 2891 it != m->backRefs.end(); ++it) 3217 2892 if (it->llSnapshotIds.size() != 0) 3218 2893 return true; … … 3352 3027 3353 3028 /** 3354 * Checks that this hard disk may be discarded and performs necessary state 3355 * changes. Must not be called for writethrough disks because there is nothing 3356 * to discard then. 3357 * 3358 * This method is to be called prior to calling the #discard() to perform 3359 * necessary consistency checks and place involved hard disks to appropriate 3360 * states. If #discard() is not called or fails, the state modifications 3361 * performed by this method must be undone by #cancelDiscard(). 3362 * 3363 * See #discard() for more info about discarding hard disks. 3364 * 3365 * @param aChain Where to store the created merge chain (may return NULL 3366 * if no real merge is necessary). 3367 * 3368 * @note Caller must hold media tree lock for writing. This locks this object 3369 * and every medium object on the merge chain for writing. 3370 */ 3371 HRESULT Medium::prepareDiscard(MergeChain * &aChain) 3372 { 3373 AutoCaller autoCaller(this); 3374 AssertComRCReturnRC(autoCaller.rc()); 3375 3376 aChain = NULL; 3377 3378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3379 3380 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread()); 3381 3382 // Medium must not be writethrough at this point 3383 AssertReturn( m->type == MediumType_Normal 3384 || m->type == MediumType_Immutable, E_FAIL); 3385 3386 if (getChildren().size() == 0) 3387 { 3388 /* special treatment of the last hard disk in the chain: */ 3389 3390 if (m->pParent.isNull()) 3391 { 3392 /* lock only, to prevent any usage; discard() will unlock */ 3393 return LockWrite(NULL); 3394 } 3395 3396 /* the differencing hard disk w/o children will be deleted, protect it 3397 * from attaching to other VMs (this is why Deleting) */ 3398 3399 switch (m->state) 3400 { 3401 case MediumState_Created: 3402 m->state = MediumState_Deleting; 3403 break; 3404 default: 3405 return setStateError(); 3406 } 3407 3408 /* aChain is intentionally NULL here */ 3409 3410 return S_OK; 3411 } 3412 3413 /* not going multi-merge as it's too expensive */ 3414 if (getChildren().size() > 1) 3415 return setError(E_FAIL, 3416 tr ("Hard disk '%s' has more than one child hard disk (%d)"), 3417 m->strLocationFull.raw(), getChildren().size()); 3418 3419 /* this is a read-only hard disk with children; it must be associated with 3420 * exactly one snapshot (when the snapshot is being taken, none of the 3421 * current VM's hard disks may be attached to other VMs). Note that by the 3422 * time when discard() is called, there must be no any attachments at all 3423 * (the code calling prepareDiscard() should detach). */ 3424 AssertReturn(m->backRefs.size() == 1, E_FAIL); 3425 AssertReturn(!m->backRefs.front().fInCurState, E_FAIL); 3426 AssertReturn(m->backRefs.front().llSnapshotIds.size() == 1, E_FAIL); 3427 3428 ComObjPtr<Medium> child = getChildren().front(); 3429 3430 /* we keep this locked, so lock the affected child to make sure the lock 3431 * order is correct when calling prepareMergeTo() */ 3432 AutoWriteLock childLock(child COMMA_LOCKVAL_SRC_POS); 3433 3434 /* delegate the rest to the profi */ 3435 if (m->pParent.isNull()) 3436 { 3437 /* base hard disk, backward merge */ 3438 Assert(child->m->backRefs.size() == 1); 3439 if (child->m->backRefs.front().machineId != m->backRefs.front().machineId) 3440 { 3441 /* backward merge is too tricky, we'll just detach on discard, so 3442 * lock only, to prevent any usage; discard() will only unlock 3443 * (since we return NULL in aChain) */ 3444 return LockWrite(NULL); 3445 } 3446 3447 return child->prepareMergeTo(this, aChain, true /* aIgnoreAttachments */); 3448 } 3449 else 3450 { 3451 /* forward merge */ 3452 return prepareMergeTo(child, aChain, true /* aIgnoreAttachments */); 3453 } 3454 } 3455 3456 /** 3457 * Discards this hard disk. 3458 * 3459 * Discarding the hard disk is merging its contents to its differencing child 3460 * hard disk (forward merge) or contents of its child hard disk to itself 3461 * (backward merge) if this hard disk is a base hard disk. If this hard disk is 3462 * a differencing hard disk w/o children, then it will be simply deleted. 3463 * Calling this method on a base hard disk w/o children will do nothing and 3464 * silently succeed. If this hard disk has more than one child, the method will 3465 * currently return an error (since merging in this case would be too expensive 3466 * and result in data duplication). 3467 * 3468 * When the backward merge takes place (i.e. this hard disk is a target) then, 3469 * on success, this hard disk will automatically replace the differencing child 3470 * hard disk used as a source (which will then be deleted) in the attachment 3471 * this child hard disk is associated with. This will happen only if both hard 3472 * disks belong to the same machine because otherwise such a replace would be 3473 * too tricky and could be not expected by the other machine. Same relates to a 3474 * case when the child hard disk is not associated with any machine at all. When 3475 * the backward merge is not applied, the method behaves as if the base hard 3476 * disk were not attached at all -- i.e. simply detaches it from the machine but 3477 * leaves the hard disk chain intact. 3478 * 3479 * This method is basically a wrapper around #mergeTo() that selects the correct 3480 * merge direction and performs additional actions as described above and. 3481 * 3482 * Note that this method will not return until the merge operation is complete 3483 * (which may be quite time consuming depending on the size of the merged hard 3484 * disks). 3485 * 3486 * Note that #prepareDiscard() must be called before calling this method. If 3487 * this method returns a failure, the caller must call #cancelDiscard(). On 3488 * success, #cancelDiscard() must not be called (this method will perform all 3489 * necessary steps such as resetting states of all involved hard disks and 3490 * deleting @a aChain). 3491 * 3492 * @param aChain Merge chain created by #prepareDiscard() (may be NULL if 3493 * no real merge takes place). 3494 * 3495 * @note Locks the hard disks from the chain for writing. Locks the machine 3496 * object when the backward merge takes place. Locks medium tree for 3497 * reading or writing. 3498 */ 3499 HRESULT Medium::discard(ComObjPtr<Progress> &aProgress, 3500 ULONG ulWeight, 3501 MergeChain *aChain, 3502 bool *pfNeedsSaveSettings) 3503 { 3504 AssertReturn(!aProgress.isNull(), E_FAIL); 3505 3506 ComObjPtr <Medium> hdFrom; 3507 3029 * Constructs a medium lock list for this medium. The lock is not taken. 3030 * 3031 * @note Locks the medium tree for reading. 3032 * 3033 * @param fMediumWritable Whether to associate a write lock with this medium. 3034 * @param pToBeParent Medium which will become the parent of this medium. 3035 * @param mediumLockList Where to store the resulting list. 3036 */ 3037 HRESULT Medium::createMediumLockList(bool fMediumWritable, 3038 Medium *pToBeParent, 3039 MediumLockList &mediumLockList) 3040 { 3508 3041 HRESULT rc = S_OK; 3509 3042 3510 { 3511 AutoCaller autoCaller(this); 3512 AssertComRCReturnRC(autoCaller.rc()); 3513 3514 aProgress->SetNextOperation(BstrFmt(tr("Merging differencing image '%s'"), getName().raw()), 3515 ulWeight); // weight 3516 3517 if (aChain == NULL) 3518 { 3519 /* we access mParent & children() */ 3520 AutoMultiWriteLock2 mLock(&m->pVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS); 3521 3522 Assert(getChildren().size() == 0); 3523 3524 /* special treatment of the last hard disk in the chain: */ 3525 3526 if (m->pParent.isNull()) 3043 /* we access parent medium objects */ 3044 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 3045 3046 /* paranoid sanity checking if the medium has a to-be parent medium */ 3047 if (pToBeParent) 3048 { 3049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3050 ComAssertRet(getParent().isNull(), E_FAIL); 3051 ComAssertRet(getChildren().size() == 0, E_FAIL); 3052 } 3053 3054 ErrorInfoKeeper eik; 3055 MultiResult mrc(S_OK); 3056 3057 ComObjPtr<Medium> pMedium = this; 3058 while (!pMedium.isNull()) 3059 { 3060 // need write lock for RefreshState if medium is inaccessible 3061 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 3062 3063 /* Accessibility check must be first, otherwise locking interferes 3064 * with getting the medium state. Lock lists are not created for 3065 * fun, and thus getting the image status is no luxury. */ 3066 MediumState_T mediumState = pMedium->getState(); 3067 if (mediumState == MediumState_Inaccessible) 3068 { 3069 rc = pMedium->RefreshState(&mediumState); 3070 if (FAILED(rc)) return rc; 3071 3072 if (mediumState == MediumState_Inaccessible) 3527 3073 { 3528 rc = UnlockWrite(NULL); 3529 AssertComRC(rc); 3530 return rc; 3074 Bstr error; 3075 rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam()); 3076 if (FAILED(rc)) return rc; 3077 3078 Bstr loc; 3079 rc = pMedium->COMGETTER(Location)(loc.asOutParam()); 3080 if (FAILED(rc)) return rc; 3081 3082 /* collect multiple errors */ 3083 eik.restore(); 3084 3085 /* be in sync with MediumBase::setStateError() */ 3086 Assert(!error.isEmpty()); 3087 mrc = setError(E_FAIL, 3088 tr("Medium '%ls' is not accessible. %ls"), 3089 loc.raw(), 3090 error.raw()); 3091 3092 eik.fetch(); 3531 3093 } 3532 3533 /* delete the differencing hard disk w/o children */ 3534 3535 Assert(m->state == MediumState_Deleting); 3536 3537 /* go back to Created since deleteStorage() expects this state */ 3538 m->state = MediumState_Created; 3539 3540 hdFrom = this; 3541 3542 rc = deleteStorageAndWait(&aProgress, pfNeedsSaveSettings); 3543 } 3094 } 3095 3096 if (pMedium == this) 3097 mediumLockList.Prepend(pMedium, fMediumWritable); 3544 3098 else 3545 { 3546 hdFrom = aChain->source(); 3547 3548 rc = hdFrom->mergeToAndWait(aChain, &aProgress, pfNeedsSaveSettings); 3549 } 3550 } 3551 3552 if (SUCCEEDED(rc)) 3553 { 3554 /* mergeToAndWait() cannot uninitialize the initiator because of 3555 * possible AutoCallers on the current thread, deleteStorageAndWait() 3556 * doesn't do it either; do it ourselves */ 3557 hdFrom->uninit(); 3558 } 3559 3560 return rc; 3561 } 3562 3563 /** 3564 * Undoes what #prepareDiscard() did. Must be called if #discard() is not called 3565 * or fails. Frees memory occupied by @a aChain. 3566 * 3567 * @param aChain Merge chain created by #prepareDiscard() (may be NULL if 3568 * no real merge takes place). 3569 * 3570 * @note Locks the hard disks from the chain for writing. Locks medium tree for 3571 * reading. 3572 */ 3573 void Medium::cancelDiscard(MergeChain *aChain) 3574 { 3575 AutoCaller autoCaller(this); 3576 AssertComRCReturnVoid(autoCaller.rc()); 3577 3578 if (aChain == NULL) 3579 { 3580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3581 3582 /* we access mParent & children() */ 3583 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 3584 3585 Assert(getChildren().size() == 0); 3586 3587 /* special treatment of the last hard disk in the chain: */ 3588 3589 if (m->pParent.isNull()) 3590 { 3591 HRESULT rc = UnlockWrite(NULL); 3592 AssertComRC(rc); 3593 return; 3594 } 3595 3596 /* the differencing hard disk w/o children will be deleted, protect it 3597 * from attaching to other VMs (this is why Deleting) */ 3598 3599 Assert(m->state == MediumState_Deleting); 3600 m->state = MediumState_Created; 3601 3602 return; 3603 } 3604 3605 /* delegate the rest to the profi */ 3606 cancelMergeTo(aChain); 3099 mediumLockList.Prepend(pMedium, false); 3100 3101 pMedium = pMedium->getParent(); 3102 if (pMedium.isNull() && pToBeParent) 3103 pMedium = pToBeParent; 3104 } 3105 3106 return mrc; 3607 3107 } 3608 3108 … … 3915 3415 /* leave the lock before a lengthy operation */ 3916 3416 vrc = RTSemEventMultiReset(m->queryInfoSem); 3917 ComAssertRCThrow(vrc, E_FAIL);3417 AssertRCReturn(vrc, E_FAIL); 3918 3418 m->queryInfoRunning = true; 3919 3419 alock.leave(); … … 3935 3435 { 3936 3436 /** @todo This kind of opening of images is assuming that diff 3937 * images can be opened as base images. Should be fixed ASAP. */ 3437 * images can be opened as base images. Should be documented if 3438 * it must work for all medium format backends. */ 3938 3439 vrc = VDOpen(hdd, 3939 3440 format.c_str(), … … 4033 3534 4034 3535 Guid id = parentId; 4035 ComObjPtr<Medium> p arent;3536 ComObjPtr<Medium> pParent; 4036 3537 rc = m->pVirtualBox->findHardDisk(&id, NULL, 4037 3538 false /* aSetError */, 4038 &p arent);3539 &pParent); 4039 3540 if (FAILED(rc)) 4040 3541 { … … 4050 3551 4051 3552 Assert(m->pParent.isNull()); 4052 m->pParent = p arent;3553 m->pParent = pParent; 4053 3554 m->pParent->m->llChildren.push_back(this); 4054 3555 } … … 4253 3754 AssertReturn(aProgress != NULL || aWait == true, E_FAIL); 4254 3755 4255 /* we're accessing the media tree, and canClose() needs the tree lock too */ 4256 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(), 4257 this->lockHandle() 4258 COMMA_LOCKVAL_SRC_POS); 4259 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() )); 4260 4261 if ( !(m->formatObj->capabilities() & ( MediumFormatCapabilities_CreateDynamic 4262 | MediumFormatCapabilities_CreateFixed))) 4263 return setError(VBOX_E_NOT_SUPPORTED, 4264 tr("Hard disk format '%s' does not support storage deletion"), 4265 m->strFormat.raw()); 4266 4267 /* Note that we are fine with Inaccessible state too: a) for symmetry with 4268 * create calls and b) because it doesn't really harm to try, if it is 4269 * really inaccessible, the delete operation will fail anyway. Accepting 4270 * Inaccessible state is especially important because all registered hard 4271 * disks are initially Inaccessible upon VBoxSVC startup until 4272 * COMGETTER(State) is called. */ 4273 3756 HRESULT rc = S_OK; 3757 ComObjPtr<Progress> pProgress; 3758 Medium::Task *pTask = NULL; 3759 3760 try 3761 { 3762 /* we're accessing the media tree, and canClose() needs it too */ 3763 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(), 3764 this->lockHandle() 3765 COMMA_LOCKVAL_SRC_POS); 3766 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() )); 3767 3768 if ( !(m->formatObj->capabilities() & ( MediumFormatCapabilities_CreateDynamic 3769 | MediumFormatCapabilities_CreateFixed))) 3770 throw setError(VBOX_E_NOT_SUPPORTED, 3771 tr("Hard disk format '%s' does not support storage deletion"), 3772 m->strFormat.raw()); 3773 3774 /* Note that we are fine with Inaccessible state too: a) for symmetry 3775 * with create calls and b) because it doesn't really harm to try, if 3776 * it is really inaccessible, the delete operation will fail anyway. 3777 * Accepting Inaccessible state is especially important because all 3778 * registered hard disks are initially Inaccessible upon VBoxSVC 3779 * startup until COMGETTER(RefreshState) is called. Accept Deleting 3780 * state because some callers need to put the image in this state early 3781 * to prevent races. */ 3782 switch (m->state) 3783 { 3784 case MediumState_Created: 3785 case MediumState_Deleting: 3786 case MediumState_Inaccessible: 3787 break; 3788 default: 3789 throw setStateError(); 3790 } 3791 3792 if (m->backRefs.size() != 0) 3793 { 3794 Utf8Str strMachines; 3795 for (BackRefList::const_iterator it = m->backRefs.begin(); 3796 it != m->backRefs.end(); 3797 ++it) 3798 { 3799 const BackRef &b = *it; 3800 if (strMachines.length()) 3801 strMachines.append(", "); 3802 strMachines.append(b.machineId.toString().c_str()); 3803 } 3804 #ifdef DEBUG 3805 dumpBackRefs(); 3806 #endif 3807 throw setError(VBOX_E_OBJECT_IN_USE, 3808 tr("Cannot delete storage: hard disk '%s' is still attached to the following %d virtual machine(s): %s"), 3809 m->strLocationFull.c_str(), 3810 m->backRefs.size(), 3811 strMachines.c_str()); 3812 } 3813 3814 rc = canClose(); 3815 if (FAILED(rc)) throw rc; 3816 3817 /* go to Deleting state, so that the medium is not actually locked */ 3818 rc = markForDeletion(); 3819 if (FAILED(rc)) throw rc; 3820 3821 /* Build the medium lock list. */ 3822 MediumLockList *pMediumLockList(new MediumLockList()); 3823 rc = createMediumLockList(true, NULL, 3824 *pMediumLockList); 3825 if (FAILED(rc)) 3826 { 3827 delete pMediumLockList; 3828 throw rc; 3829 } 3830 3831 rc = pMediumLockList->Lock(); 3832 if (FAILED(rc)) 3833 { 3834 delete pMediumLockList; 3835 throw setError(rc, 3836 tr("Failed to lock media when deleting '%ls'"), 3837 getLocationFull().raw()); 3838 } 3839 3840 /* try to remove from the list of known hard disks before performing 3841 * actual deletion (we favor the consistency of the media registry in 3842 * the first place which would have been broken if 3843 * unregisterWithVirtualBox() failed after we successfully deleted the 3844 * storage) */ 3845 rc = unregisterWithVirtualBox(pfNeedsSaveSettings); 3846 if (FAILED(rc)) throw rc; 3847 3848 if (aProgress != NULL) 3849 { 3850 /* use the existing progress object... */ 3851 pProgress = *aProgress; 3852 3853 /* ...but create a new one if it is null */ 3854 if (pProgress.isNull()) 3855 { 3856 pProgress.createObject(); 3857 rc = pProgress->init(m->pVirtualBox, 3858 static_cast<IMedium*>(this), 3859 BstrFmt(tr("Deleting hard disk storage unit '%s'"), m->strLocationFull.raw()), 3860 FALSE /* aCancelable */); 3861 if (FAILED(rc)) throw rc; 3862 } 3863 } 3864 3865 /* setup task object to carry out the operation sync/async */ 3866 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList); 3867 rc = pTask->rc(); 3868 AssertComRC(rc); 3869 if (FAILED(rc)) throw rc; 3870 } 3871 catch (HRESULT aRC) { rc = aRC; } 3872 3873 if (SUCCEEDED(rc)) 3874 { 3875 if (aWait) 3876 rc = runNow(pTask, NULL /* pfNeedsSaveSettings*/); 3877 else 3878 rc = startThread(pTask); 3879 3880 if (SUCCEEDED(rc) && aProgress != NULL) 3881 *aProgress = pProgress; 3882 3883 } 3884 else 3885 { 3886 if (pTask) 3887 delete pTask; 3888 3889 /* Undo deleting state if necessary. */ 3890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3891 unmarkForDeletion(); 3892 } 3893 3894 return rc; 3895 } 3896 3897 /** 3898 * Mark a medium for deletion. 3899 * 3900 * @note Caller must hold the write lock on this medium! 3901 */ 3902 HRESULT Medium::markForDeletion() 3903 { 3904 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL); 4274 3905 switch (m->state) 4275 3906 { 4276 3907 case MediumState_Created: 4277 3908 case MediumState_Inaccessible: 4278 break; 3909 m->preLockState = m->state; 3910 m->state = MediumState_Deleting; 3911 return S_OK; 4279 3912 default: 4280 3913 return setStateError(); 4281 3914 } 4282 4283 if (m->backRefs.size() != 0) 4284 { 4285 Utf8Str strMachines; 4286 for (BackRefList::const_iterator it = m->backRefs.begin(); 4287 it != m->backRefs.end(); 4288 ++it) 4289 { 4290 const BackRef &b = *it; 4291 if (strMachines.length()) 4292 strMachines.append(", "); 4293 strMachines.append(b.machineId.toString().c_str()); 4294 } 4295 #ifdef DEBUG 4296 dumpBackRefs(); 4297 #endif 4298 return setError(VBOX_E_OBJECT_IN_USE, 4299 tr("Cannot delete storage: hard disk '%s' is still attached to the following %d virtual machine(s): %s"), 4300 m->strLocationFull.c_str(), 4301 m->backRefs.size(), 4302 strMachines.c_str()); 4303 } 4304 4305 HRESULT rc = canClose(); 4306 if (FAILED(rc)) return rc; 4307 4308 /* go to Deleting state before leaving the lock */ 4309 m->state = MediumState_Deleting; 4310 4311 /* try to remove from the list of known hard disks before performing actual 4312 * deletion (we favor the consistency of the media registry in the first 4313 * place which would have been broken if unregisterWithVirtualBox() failed 4314 * after we successfully deleted the storage) */ 4315 rc = unregisterWithVirtualBox(pfNeedsSaveSettings); 4316 4317 /* restore the state because we may fail below; we will set it later again*/ 4318 m->state = MediumState_Created; 4319 4320 if (FAILED(rc)) return rc; 4321 4322 ComObjPtr<Progress> progress; 4323 4324 if (aProgress != NULL) 4325 { 4326 /* use the existing progress object... */ 4327 progress = *aProgress; 4328 4329 /* ...but create a new one if it is null */ 4330 if (progress.isNull()) 4331 { 4332 progress.createObject(); 4333 rc = progress->init(m->pVirtualBox, 4334 static_cast<IMedium*>(this), 4335 BstrFmt(tr("Deleting hard disk storage unit '%s'"), m->strLocationFull.raw()), 4336 FALSE /* aCancelable */); 4337 if (FAILED(rc)) return rc; 4338 } 4339 } 4340 4341 /* setup task object to carry out the operation asynchronously */ 4342 Medium::Task *pTask(new Medium::DeleteTask(this, progress)); 4343 rc = pTask->rc(); 4344 if (FAILED(rc)) 4345 { 4346 AssertComRC(rc); 4347 delete pTask; 4348 return rc; 4349 } 4350 4351 if (aWait) 4352 { 4353 /* go to Deleting state before starting the task */ 4354 m->state = MediumState_Deleting; 4355 4356 rc = runNow(pTask, NULL /* pfNeedsSaveSettings*/ ); // there is no save settings to do in taskThreadDelete() 4357 if (FAILED(rc)) return rc; 4358 } 4359 else 4360 { 4361 rc = startThread(pTask); 4362 if (FAILED(rc)) return rc; 4363 4364 /* go to Deleting state before leaving the lock */ 4365 m->state = MediumState_Deleting; 4366 } 4367 4368 if (aProgress != NULL) 4369 *aProgress = progress; 4370 4371 return rc; 3915 } 3916 3917 /** 3918 * Removes the "mark for deletion". 3919 * 3920 * @note Caller must hold the write lock on this medium! 3921 */ 3922 HRESULT Medium::unmarkForDeletion() 3923 { 3924 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL); 3925 switch (m->state) 3926 { 3927 case MediumState_Deleting: 3928 m->state = m->preLockState; 3929 return S_OK; 3930 default: 3931 return setStateError(); 3932 } 4372 3933 } 4373 3934 … … 4376 3937 * format and the location. Note that @c aTarget must be NotCreated. 4377 3938 * 4378 * As opposed to the CreateDiffStorage() method, this method doesn't try to lock 4379 * this hard disk for reading assuming that the caller has already done so. This 4380 * is used when taking an online snapshot (where all original hard disks are 4381 * locked for writing and must remain such). Note however that if @a aWait is 4382 * @c false and this method returns a success then the thread started by 4383 * this method will unlock the hard disk (unless it is in 4384 * MediumState_LockedWrite state) so make sure the hard disk is either in 4385 * MediumState_LockedWrite or call #LockRead() before calling this method! If @a 4386 * aWait is @c true then this method neither locks nor unlocks the hard disk, so 4387 * make sure you do it yourself as needed. 4388 * 4389 * If @a aProgress is not NULL but the object it points to is @c null then a new 4390 * progress object will be created and assigned to @a *aProgress on success, 4391 * otherwise the existing progress object is used. If @a aProgress is NULL, then no 4392 * progress object is created/used at all. 3939 * The @a aMediumLockList parameter contains the associated medium lock list, 3940 * which must be in locked state. If @a aWait is @c true then the caller is 3941 * responsible for unlocking. 3942 * 3943 * If @a aProgress is not NULL but the object it points to is @c null then a 3944 * new progress object will be created and assigned to @a *aProgress on 3945 * success, otherwise the existing progress object is used. If @a aProgress is 3946 * NULL, then no progress object is created/used at all. 4393 3947 * 4394 3948 * When @a aWait is @c false, this method will create a thread to perform the … … 4398 3952 * NULL when @a aWait is @c false (this method will assert in this case). 4399 3953 * 4400 * @param aTarget Target hard disk. 4401 * @param aVariant Precise image variant to create. 3954 * @param aTarget Target hard disk. 3955 * @param aVariant Precise image variant to create. 3956 * @param aMediumLockList List of media which should be locked. 3957 * @param aProgress Where to find/store a Progress object to track 3958 * operation completion. 3959 * @param aWait @c true if this method should block instead of 3960 * creating an asynchronous thread. 3961 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been 3962 * initialized to false and that will be set to true 3963 * by this function if the caller should invoke 3964 * VirtualBox::saveSettings() because the global 3965 * settings have changed. This only works in "wait" 3966 * mode; otherwise saveSettings is called 3967 * automatically by the thread that was created, 3968 * and this parameter is ignored. 3969 * 3970 * @note Locks this object and @a aTarget for writing. 3971 */ 3972 HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget, 3973 MediumVariant_T aVariant, 3974 MediumLockList *aMediumLockList, 3975 ComObjPtr<Progress> *aProgress, 3976 bool aWait, 3977 bool *pfNeedsSaveSettings) 3978 { 3979 AssertReturn(!aTarget.isNull(), E_FAIL); 3980 AssertReturn(aMediumLockList, E_FAIL); 3981 AssertReturn(aProgress != NULL || aWait == true, E_FAIL); 3982 3983 AutoCaller autoCaller(this); 3984 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3985 3986 AutoCaller targetCaller(aTarget); 3987 if (FAILED(targetCaller.rc())) return targetCaller.rc(); 3988 3989 HRESULT rc = S_OK; 3990 ComObjPtr<Progress> pProgress; 3991 Medium::Task *pTask = NULL; 3992 3993 try 3994 { 3995 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS); 3996 3997 ComAssertThrow(m->type != MediumType_Writethrough, E_FAIL); 3998 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL); 3999 4000 if (aTarget->m->state != MediumState_NotCreated) 4001 throw aTarget->setStateError(); 4002 4003 /* Check that the hard disk is not attached to the current state of 4004 * any VM referring to it. */ 4005 for (BackRefList::const_iterator it = m->backRefs.begin(); 4006 it != m->backRefs.end(); 4007 ++it) 4008 { 4009 if (it->fInCurState) 4010 { 4011 /* Note: when a VM snapshot is being taken, all normal hard 4012 * disks attached to the VM in the current state will be, as an 4013 * exception, also associated with the snapshot which is about 4014 * to create (see SnapshotMachine::init()) before deassociating 4015 * them from the current state (which takes place only on 4016 * success in Machine::fixupHardDisks()), so that the size of 4017 * snapshotIds will be 1 in this case. The extra condition is 4018 * used to filter out this legal situation. */ 4019 if (it->llSnapshotIds.size() == 0) 4020 throw setError(VBOX_E_INVALID_OBJECT_STATE, 4021 tr("Hard disk '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing hard disks based on it may be created until it is detached"), 4022 m->strLocationFull.raw(), it->machineId.raw()); 4023 4024 Assert(it->llSnapshotIds.size() == 1); 4025 } 4026 } 4027 4028 if (aProgress != NULL) 4029 { 4030 /* use the existing progress object... */ 4031 pProgress = *aProgress; 4032 4033 /* ...but create a new one if it is null */ 4034 if (pProgress.isNull()) 4035 { 4036 pProgress.createObject(); 4037 rc = pProgress->init(m->pVirtualBox, 4038 static_cast<IMedium*>(this), 4039 BstrFmt(tr("Creating differencing hard disk storage unit '%s'"), aTarget->m->strLocationFull.raw()), 4040 TRUE /* aCancelable */); 4041 if (FAILED(rc)) throw rc; 4042 } 4043 } 4044 4045 /* setup task object to carry out the operation sync/async */ 4046 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant, 4047 aMediumLockList, 4048 aWait /* fKeepMediumLockList */); 4049 rc = pTask->rc(); 4050 AssertComRC(rc); 4051 if (FAILED(rc)) throw rc; 4052 4053 /* register a task (it will deregister itself when done) */ 4054 ++m->numCreateDiffTasks; 4055 Assert(m->numCreateDiffTasks != 0); /* overflow? */ 4056 4057 aTarget->m->state = MediumState_Creating; 4058 } 4059 catch (HRESULT aRC) { rc = aRC; } 4060 4061 if (SUCCEEDED(rc)) 4062 { 4063 if (aWait) 4064 rc = runNow(pTask, pfNeedsSaveSettings); 4065 else 4066 rc = startThread(pTask); 4067 4068 if (SUCCEEDED(rc) && aProgress != NULL) 4069 *aProgress = pProgress; 4070 } 4071 else if (pTask != NULL) 4072 delete pTask; 4073 4074 return rc; 4075 } 4076 4077 /** 4078 * Prepares this (source) hard disk, target hard disk and all intermediate hard 4079 * disks for the merge operation. 4080 * 4081 * This method is to be called prior to calling the #mergeTo() to perform 4082 * necessary consistency checks and place involved hard disks to appropriate 4083 * states. If #mergeTo() is not called or fails, the state modifications 4084 * performed by this method must be undone by #cancelMergeTo(). 4085 * 4086 * See #mergeTo() for more information about merging. 4087 * 4088 * @param pTarget Target hard disk. 4089 * @param aMachineId Allowed machine attachment. NULL means do not check. 4090 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means 4091 * do not check. 4092 * @param fMergeForward Resulting merge direction (out). 4093 * @param pParentForTarget New parent for target medium after merge (out). 4094 * @param aChildrenToReparent List of children of the source which will have 4095 * to be reparented to the target after merge (out). 4096 * @param aMediumLockList Medium locking information (out). 4097 * 4098 * @note Locks medium tree for reading. Locks this object, aTarget and all 4099 * intermediate hard disks for writing. 4100 */ 4101 HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget, 4102 const Guid *aMachineId, 4103 const Guid *aSnapshotId, 4104 bool &fMergeForward, 4105 ComObjPtr<Medium> &pParentForTarget, 4106 MediaList &aChildrenToReparent, 4107 MediumLockList * &aMediumLockList) 4108 { 4109 AssertReturn(pTarget != NULL, E_FAIL); 4110 AssertReturn(pTarget != this, E_FAIL); 4111 4112 AutoCaller autoCaller(this); 4113 AssertComRCReturnRC(autoCaller.rc()); 4114 4115 AutoCaller targetCaller(pTarget); 4116 AssertComRCReturnRC(targetCaller.rc()); 4117 4118 HRESULT rc = S_OK; 4119 fMergeForward = false; 4120 pParentForTarget.setNull(); 4121 aChildrenToReparent.clear(); 4122 Assert(aMediumLockList == NULL); 4123 aMediumLockList = NULL; 4124 4125 try 4126 { 4127 // locking: we need the tree lock first because we access parent pointers 4128 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4129 4130 /* more sanity checking and figuring out the merge direction */ 4131 ComObjPtr<Medium> pMedium = getParent(); 4132 while (!pMedium.isNull() && pMedium != pTarget) 4133 pMedium = pMedium->getParent(); 4134 if (pMedium == pTarget) 4135 fMergeForward = false; 4136 else 4137 { 4138 pMedium = pTarget->getParent(); 4139 while (!pMedium.isNull() && pMedium != this) 4140 pMedium = pMedium->getParent(); 4141 if (pMedium == this) 4142 fMergeForward = true; 4143 else 4144 { 4145 Utf8Str tgtLoc; 4146 { 4147 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS); 4148 tgtLoc = pTarget->getLocationFull(); 4149 } 4150 4151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4152 throw setError(E_FAIL, 4153 tr("Hard disks '%s' and '%s' are unrelated"), 4154 m->strLocationFull.raw(), tgtLoc.raw()); 4155 } 4156 } 4157 4158 /* Build the lock list. */ 4159 aMediumLockList = new MediumLockList(); 4160 if (fMergeForward) 4161 rc = pTarget->createMediumLockList(true, NULL, *aMediumLockList); 4162 else 4163 rc = createMediumLockList(false, NULL, *aMediumLockList); 4164 if (FAILED(rc)) 4165 { 4166 delete aMediumLockList; 4167 aMediumLockList = NULL; 4168 throw rc; 4169 } 4170 4171 /* sanity checking */ 4172 { 4173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4174 if (m->state != MediumState_Created) 4175 throw setStateError(); 4176 } 4177 { 4178 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS); 4179 if (pTarget->m->state != MediumState_Created) 4180 throw pTarget->setStateError(); 4181 } 4182 4183 /* check medium attachment and other sanity conditions */ 4184 if (fMergeForward) 4185 { 4186 AutoReadLock(this COMMA_LOCKVAL_SRC_POS); 4187 if (getChildren().size() > 1) 4188 { 4189 throw setError(E_FAIL, 4190 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"), 4191 m->strLocationFull.raw(), getChildren().size()); 4192 } 4193 /* One backreference is only allowed if the machine ID is not empty 4194 * and it matches the machine the image is attached to (including 4195 * the snapshot ID if not empty). */ 4196 if ( m->backRefs.size() != 0 4197 && ( !aMachineId 4198 || m->backRefs.size() != 1 4199 || aMachineId->isEmpty() 4200 || *getFirstMachineBackrefId() != *aMachineId 4201 || ( (!aSnapshotId || !aSnapshotId->isEmpty()) 4202 && *getFirstMachineBackrefSnapshotId() != *aSnapshotId))) 4203 throw setError(E_FAIL, 4204 tr("Medium '%s' is attached to %d virtual machines"), 4205 m->strLocationFull.raw(), m->backRefs.size()); 4206 if (m->type == MediumType_Immutable) 4207 throw setError(E_FAIL, 4208 tr("Medium '%s' is immutable"), 4209 m->strLocationFull.raw()); 4210 } 4211 else 4212 { 4213 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS); 4214 if (pTarget->getChildren().size() > 1) 4215 { 4216 throw setError(E_FAIL, 4217 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"), 4218 pTarget->m->strLocationFull.raw(), 4219 pTarget->getChildren().size()); 4220 } 4221 if (pTarget->m->type == MediumType_Immutable) 4222 throw setError(E_FAIL, 4223 tr("Medium '%s' is immutable"), 4224 pTarget->m->strLocationFull.raw()); 4225 } 4226 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this); 4227 ComObjPtr<Medium> pLastIntermediate = pLast->getParent(); 4228 for (pLast = pLastIntermediate; 4229 !pLast.isNull() && pLast != pTarget && pLast != this; 4230 pLast = pLast->getParent()) 4231 { 4232 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS); 4233 if (pLast->getChildren().size() > 1) 4234 { 4235 throw setError(E_FAIL, 4236 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"), 4237 pLast->m->strLocationFull.raw(), 4238 pLast->getChildren().size()); 4239 } 4240 if (pLast->m->backRefs.size() != 0) 4241 throw setError(E_FAIL, 4242 tr("Medium '%s' is attached to %d virtual machines"), 4243 pLast->m->strLocationFull.raw(), 4244 pLast->m->backRefs.size()); 4245 4246 } 4247 4248 /* Update medium states appropriately */ 4249 switch (m->state) 4250 { 4251 case MediumState_Created: 4252 m->state = MediumState_Deleting; 4253 break; 4254 default: 4255 throw setStateError(); 4256 } 4257 if (fMergeForward) 4258 { 4259 /* we will need parent to reparent target */ 4260 pParentForTarget = m->pParent; 4261 } 4262 else 4263 { 4264 /* we will need to reparent children of the source */ 4265 for (MediaList::const_iterator it = getChildren().begin(); 4266 it != getChildren().end(); 4267 ++it) 4268 { 4269 pMedium = *it; 4270 rc = pMedium->LockWrite(NULL); 4271 if (FAILED(rc)) throw rc; 4272 4273 aChildrenToReparent.push_back(pMedium); 4274 } 4275 } 4276 for (pLast = pLastIntermediate; 4277 !pLast.isNull() && pLast != pTarget && pLast != this; 4278 pLast = pLast->getParent()) 4279 { 4280 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS); 4281 switch (pLast->m->state) 4282 { 4283 case MediumState_Created: 4284 pLast->m->state = MediumState_Deleting; 4285 break; 4286 default: 4287 throw pLast->setStateError(); 4288 } 4289 } 4290 4291 /* Tweak the lock list in the backward merge case, as the target 4292 * isn't marked to be locked for writing yet. */ 4293 if (!fMergeForward) 4294 { 4295 MediumLockList::Base::iterator lockListBegin = 4296 aMediumLockList->GetBegin(); 4297 MediumLockList::Base::iterator lockListEnd = 4298 aMediumLockList->GetEnd(); 4299 lockListEnd--; 4300 for (MediumLockList::Base::iterator it = lockListBegin; 4301 it != lockListEnd; 4302 ++it) 4303 { 4304 MediumLock &mediumLock = *it; 4305 if (mediumLock.GetMedium() == pTarget) 4306 { 4307 HRESULT rc2 = mediumLock.UpdateLock(true); 4308 AssertComRC(rc2); 4309 break; 4310 } 4311 } 4312 } 4313 4314 rc = aMediumLockList->Lock(); 4315 if (FAILED(rc)) 4316 { 4317 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS); 4318 throw setError(rc, 4319 tr("Failed to lock media when merging to '%ls'"), 4320 pTarget->getLocationFull().raw()); 4321 } 4322 } 4323 catch (HRESULT aRC) { rc = aRC; } 4324 4325 if (FAILED(rc)) 4326 { 4327 delete aMediumLockList; 4328 aMediumLockList = NULL; 4329 } 4330 4331 return rc; 4332 } 4333 4334 /** 4335 * Merges this hard disk to the specified hard disk which must be either its 4336 * direct ancestor or descendant. 4337 * 4338 * Given this hard disk is SOURCE and the specified hard disk is TARGET, we will 4339 * get two varians of the merge operation: 4340 * 4341 * forward merge 4342 * -------------------------> 4343 * [Extra] <- SOURCE <- Intermediate <- TARGET 4344 * Any Del Del LockWr 4345 * 4346 * 4347 * backward merge 4348 * <------------------------- 4349 * TARGET <- Intermediate <- SOURCE <- [Extra] 4350 * LockWr Del Del LockWr 4351 * 4352 * Each diagram shows the involved hard disks on the hard disk chain where 4353 * SOURCE and TARGET belong. Under each hard disk there is a state value which 4354 * the hard disk must have at a time of the mergeTo() call. 4355 * 4356 * The hard disks in the square braces may be absent (e.g. when the forward 4357 * operation takes place and SOURCE is the base hard disk, or when the backward 4358 * merge operation takes place and TARGET is the last child in the chain) but if 4359 * they present they are involved too as shown. 4360 * 4361 * Nor the source hard disk neither intermediate hard disks may be attached to 4362 * any VM directly or in the snapshot, otherwise this method will assert. 4363 * 4364 * The #prepareMergeTo() method must be called prior to this method to place all 4365 * involved to necessary states and perform other consistency checks. 4366 * 4367 * If @a aWait is @c true then this method will perform the operation on the 4368 * calling thread and will not return to the caller until the operation is 4369 * completed. When this method succeeds, all intermediate hard disk objects in 4370 * the chain will be uninitialized, the state of the target hard disk (and all 4371 * involved extra hard disks) will be restored. @a aMediumLockList will not be 4372 * deleted, whether the operation is successful or not. The caller has to do 4373 * this if appropriate. Note that this (source) hard disk is not uninitialized 4374 * because of possible AutoCaller instances held by the caller of this method 4375 * on the current thread. It's therefore the responsibility of the caller to 4376 * call Medium::uninit() after releasing all callers. 4377 * 4378 * If @a aWait is @c false then this method will create a thread to perform the 4379 * operation asynchronously and will return immediately. If the operation 4380 * succeeds, the thread will uninitialize the source hard disk object and all 4381 * intermediate hard disk objects in the chain, reset the state of the target 4382 * hard disk (and all involved extra hard disks) and delete @a aMediumLockList. 4383 * If the operation fails, the thread will only reset the states of all 4384 * involved hard disks and delete @a aMediumLockList. 4385 * 4386 * When this method fails (regardless of the @a aWait mode), it is a caller's 4387 * responsiblity to undo state changes and delete @a aMediumLockList using 4388 * #cancelMergeTo(). 4389 * 4390 * If @a aProgress is not NULL but the object it points to is @c null then a new 4391 * progress object will be created and assigned to @a *aProgress on success, 4392 * otherwise the existing progress object is used. If Progress is NULL, then no 4393 * progress object is created/used at all. Note that @a aProgress cannot be 4394 * NULL when @a aWait is @c false (this method will assert in this case). 4395 * 4396 * @param pTarget Target hard disk. 4397 * @param fMergeForward Merge direction. 4398 * @param pParentForTarget New parent for target medium after merge. 4399 * @param aChildrenToReparent List of children of the source which will have 4400 * to be reparented to the target after merge. 4401 * @param aMediumLockList Medium locking information. 4402 4402 * @param aProgress Where to find/store a Progress object to track operation 4403 4403 * completion. … … 4409 4409 * and this parameter is ignored. 4410 4410 * 4411 * @note Locks this object and @a aTarget for writing. 4412 */ 4413 HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget, 4414 MediumVariant_T aVariant, 4415 ComObjPtr<Progress> *aProgress, 4416 bool aWait, 4417 bool *pfNeedsSaveSettings) 4418 { 4419 AssertReturn(!aTarget.isNull(), E_FAIL); 4420 AssertReturn(aProgress != NULL || aWait == true, E_FAIL); 4421 4422 AutoCaller autoCaller(this); 4423 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4424 4425 AutoCaller targetCaller(aTarget); 4426 if (FAILED(targetCaller.rc())) return targetCaller.rc(); 4427 4428 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS); 4429 4430 AssertReturn(m->type != MediumType_Writethrough, E_FAIL); 4431 4432 /* Note: MediumState_LockedWrite is ok when taking an online snapshot */ 4433 AssertReturn( m->state == MediumState_LockedRead 4434 || m->state == MediumState_LockedWrite, E_FAIL); 4435 4436 if (aTarget->m->state != MediumState_NotCreated) 4437 return aTarget->setStateError(); 4438 4439 HRESULT rc = S_OK; 4440 4441 /* check that the hard disk is not attached to any VM in the current state*/ 4442 for (BackRefList::const_iterator it = m->backRefs.begin(); 4443 it != m->backRefs.end(); 4444 ++it) 4445 { 4446 if (it->fInCurState) 4447 { 4448 /* Note: when a VM snapshot is being taken, all normal hard disks 4449 * attached to the VM in the current state will be, as an exception, 4450 * also associated with the snapshot which is about to create (see 4451 * SnapshotMachine::init()) before deassociating them from the 4452 * current state (which takes place only on success in 4453 * Machine::fixupHardDisks()), so that the size of snapshotIds 4454 * will be 1 in this case. The given condition is used to filter out 4455 * this legal situatinon and do not report an error. */ 4456 4457 if (it->llSnapshotIds.size() == 0) 4458 return setError(VBOX_E_INVALID_OBJECT_STATE, 4459 tr("Hard disk '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing hard disks based on it may be created until it is detached"), 4460 m->strLocationFull.raw(), it->machineId.raw()); 4461 4462 Assert(it->llSnapshotIds.size() == 1); 4463 } 4464 } 4465 4466 ComObjPtr<Progress> progress; 4467 4468 if (aProgress != NULL) 4469 { 4470 /* use the existing progress object... */ 4471 progress = *aProgress; 4472 4473 /* ...but create a new one if it is null */ 4474 if (progress.isNull()) 4475 { 4476 progress.createObject(); 4477 rc = progress->init(m->pVirtualBox, 4478 static_cast<IMedium*>(this), 4479 BstrFmt(tr("Creating differencing hard disk storage unit '%s'"), aTarget->m->strLocationFull.raw()), 4480 TRUE /* aCancelable */); 4481 if (FAILED(rc)) return rc; 4482 } 4483 } 4484 4485 /* setup task object to carry out the operation asynchronously */ 4486 Medium::Task *pTask(new Medium::CreateDiffTask(this, progress, aTarget, 4487 aVariant)); 4488 rc = pTask->rc(); 4489 if (FAILED(rc)) 4490 { 4491 AssertComRC(rc); 4492 delete pTask; 4493 return rc; 4494 } 4495 4496 /* register a task (it will deregister itself when done) */ 4497 ++m->numCreateDiffTasks; 4498 Assert(m->numCreateDiffTasks != 0); /* overflow? */ 4499 4500 if (aWait) 4501 { 4502 // go to Creating state before starting the task 4503 aTarget->m->state = MediumState_Creating; 4504 4505 // release the locks because the task function will acquire other locks; 4506 // this is safe because both this and the target are not protected by 4507 // their states; we have asserted above that *this* is locked read or write! 4508 alock.release(); 4509 4510 rc = runNow(pTask, pfNeedsSaveSettings); 4511 if (FAILED(rc)) return rc; 4512 } 4513 else 4514 { 4515 rc = startThread(pTask); 4516 if (FAILED(rc)) return rc; 4517 4518 /* go to Creating state before leaving the lock */ 4519 aTarget->m->state = MediumState_Creating; 4520 } 4521 4522 if (aProgress != NULL) 4523 *aProgress = progress; 4524 4525 return rc; 4526 } 4527 4528 /** 4529 * Prepares this (source) hard disk, target hard disk and all intermediate hard 4530 * disks for the merge operation. 4531 * 4532 * This method is to be called prior to calling the #mergeTo() to perform 4533 * necessary consistency checks and place involved hard disks to appropriate 4534 * states. If #mergeTo() is not called or fails, the state modifications 4535 * performed by this method must be undone by #cancelMergeTo(). 4536 * 4537 * Note that when @a aIgnoreAttachments is @c true then it's the caller's 4538 * responsibility to detach the source and all intermediate hard disks before 4539 * calling #mergeTo() (which will fail otherwise). 4540 * 4541 * See #mergeTo() for more information about merging. 4542 * 4543 * @param aTarget Target hard disk. 4544 * @param aChain Where to store the created merge chain. 4545 * @param aIgnoreAttachments Don't check if the source or any intermediate 4546 * hard disk is attached to any VM. 4547 * 4548 * @note Locks medium tree for reading. Locks this object, aTarget and all 4549 * intermediate hard disks for writing. 4550 */ 4551 HRESULT Medium::prepareMergeTo(Medium *aTarget, 4552 MergeChain * &aChain, 4553 bool aIgnoreAttachments /*= false*/) 4554 { 4555 AssertReturn(aTarget != NULL, E_FAIL); 4556 4557 AutoCaller autoCaller(this); 4558 AssertComRCReturnRC(autoCaller.rc()); 4559 4560 AutoCaller targetCaller(aTarget); 4561 AssertComRCReturnRC(targetCaller.rc()); 4562 4563 aChain = NULL; 4564 4565 /* we walk the tree */ 4566 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4567 4568 HRESULT rc = S_OK; 4569 4570 /* detect the merge direction */ 4571 bool forward; 4572 { 4573 Medium *parent = m->pParent; 4574 while (parent != NULL && parent != aTarget) 4575 parent = parent->m->pParent; 4576 if (parent == aTarget) 4577 forward = false; 4578 else 4579 { 4580 parent = aTarget->m->pParent; 4581 while (parent != NULL && parent != this) 4582 parent = parent->m->pParent; 4583 if (parent == this) 4584 forward = true; 4585 else 4586 { 4587 Utf8Str tgtLoc; 4588 { 4589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4590 tgtLoc = aTarget->getLocationFull(); 4591 } 4592 4593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4594 return setError(E_FAIL, 4595 tr("Hard disks '%s' and '%s' are unrelated"), 4596 m->strLocationFull.raw(), tgtLoc.raw()); 4597 } 4598 } 4599 } 4600 4601 /* build the chain (will do necessary checks and state changes) */ 4602 std::auto_ptr<MergeChain> chain(new MergeChain(forward, 4603 aIgnoreAttachments)); 4604 { 4605 Medium *last = forward ? aTarget : this; 4606 Medium *first = forward ? this : aTarget; 4607 4608 for (;;) 4609 { 4610 if (last == aTarget) 4611 rc = chain->addTarget(last); 4612 else if (last == this) 4613 rc = chain->addSource(last); 4614 else 4615 rc = chain->addIntermediate(last); 4616 if (FAILED(rc)) return rc; 4617 4618 if (last == first) 4619 break; 4620 4621 last = last->m->pParent; 4622 } 4623 } 4624 4625 aChain = chain.release(); 4626 4627 return S_OK; 4628 } 4629 4630 /** 4631 * Merges this hard disk to the specified hard disk which must be either its 4632 * direct ancestor or descendant. 4633 * 4634 * Given this hard disk is SOURCE and the specified hard disk is TARGET, we will 4635 * get two varians of the merge operation: 4636 * 4637 * forward merge 4638 * -------------------------> 4639 * [Extra] <- SOURCE <- Intermediate <- TARGET 4640 * Any Del Del LockWr 4641 * 4642 * 4643 * backward merge 4644 * <------------------------- 4645 * TARGET <- Intermediate <- SOURCE <- [Extra] 4646 * LockWr Del Del LockWr 4647 * 4648 * Each scheme shows the involved hard disks on the hard disk chain where 4649 * SOURCE and TARGET belong. Under each hard disk there is a state value which 4650 * the hard disk must have at a time of the mergeTo() call. 4651 * 4652 * The hard disks in the square braces may be absent (e.g. when the forward 4653 * operation takes place and SOURCE is the base hard disk, or when the backward 4654 * merge operation takes place and TARGET is the last child in the chain) but if 4655 * they present they are involved too as shown. 4656 * 4657 * Nor the source hard disk neither intermediate hard disks may be attached to 4658 * any VM directly or in the snapshot, otherwise this method will assert. 4659 * 4660 * The #prepareMergeTo() method must be called prior to this method to place all 4661 * involved to necessary states and perform other consistency checks. 4662 * 4663 * If @a aWait is @c true then this method will perform the operation on the 4664 * calling thread and will not return to the caller until the operation is 4665 * completed. When this method succeeds, all intermediate hard disk objects in 4666 * the chain will be uninitialized, the state of the target hard disk (and all 4667 * involved extra hard disks) will be restored and @a aChain will be deleted. 4668 * Note that this (source) hard disk is not uninitialized because of possible 4669 * AutoCaller instances held by the caller of this method on the current thread. 4670 * It's therefore the responsibility of the caller to call Medium::uninit() 4671 * after releasing all callers in this case! 4672 * 4673 * If @a aWait is @c false then this method will crea,te a thread to perform the 4674 * create operation asynchronously and will return immediately. If the operation 4675 * succeeds, the thread will uninitialize the source hard disk object and all 4676 * intermediate hard disk objects in the chain, reset the state of the target 4677 * hard disk (and all involved extra hard disks) and delete @a aChain. If the 4678 * operation fails, the thread will only reset the states of all involved hard 4679 * disks and delete @a aChain. 4680 * 4681 * When this method fails (regardless of the @a aWait mode), it is a caller's 4682 * responsiblity to undo state changes and delete @a aChain using 4683 * #cancelMergeTo(). 4684 * 4685 * If @a aProgress is not NULL but the object it points to is @c null then a new 4686 * progress object will be created and assigned to @a *aProgress on success, 4687 * otherwise the existing progress object is used. If Progress is NULL, then no 4688 * progress object is created/used at all. Note that @a aProgress cannot be 4689 * NULL when @a aWait is @c false (this method will assert in this case). 4690 * 4691 * @param aChain Merge chain created by #prepareMergeTo(). 4692 * @param aProgress Where to find/store a Progress object to track operation 4693 * completion. 4694 * @param aWait @c true if this method should block instead of creating 4695 * an asynchronous thread. 4696 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true 4697 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed. 4698 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created, 4699 * and this parameter is ignored. 4700 * 4701 * @note Locks the branch lock for writing. Locks the hard disks from the chain 4411 * @note Locks the tree lock for writing. Locks the hard disks from the chain 4702 4412 * for writing. 4703 4413 */ 4704 HRESULT Medium::mergeTo(MergeChain *aChain, 4414 HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget, 4415 bool &fMergeForward, 4416 ComObjPtr<Medium> pParentForTarget, 4417 const MediaList &aChildrenToReparent, 4418 MediumLockList *aMediumLockList, 4705 4419 ComObjPtr <Progress> *aProgress, 4706 4420 bool aWait, 4707 4421 bool *pfNeedsSaveSettings) 4708 4422 { 4709 AssertReturn(aChain != NULL, E_FAIL); 4423 AssertReturn(pTarget != NULL, E_FAIL); 4424 AssertReturn(pTarget != this, E_FAIL); 4425 AssertReturn(aMediumLockList != NULL, E_FAIL); 4710 4426 AssertReturn(aProgress != NULL || aWait == true, E_FAIL); 4711 4427 … … 4714 4430 4715 4431 HRESULT rc = S_OK; 4716 4717 ComObjPtr <Progress> progress; 4718 4719 if (aProgress != NULL) 4720 { 4721 /* use the existing progress object... */ 4722 progress = *aProgress; 4723 4724 /* ...but create a new one if it is null */ 4725 if (progress.isNull()) 4726 { 4727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4728 4729 progress.createObject(); 4730 rc = progress->init(m->pVirtualBox, 4731 static_cast<IMedium*>(this), 4732 BstrFmt(tr("Merging hard disk '%s' to '%s'"), 4733 getName().raw(), 4734 aChain->target()->getName().raw()), 4735 TRUE /* aCancelable */); 4736 if (FAILED(rc)) return rc; 4737 } 4738 } 4739 4740 /* setup task object to carry out the operation asynchronously */ 4741 Medium::Task *pTask(new Medium::MergeTask(this, progress, aChain)); 4742 rc = pTask->rc(); 4743 if (FAILED(rc)) 4744 { 4432 ComObjPtr <Progress> pProgress; 4433 Medium::Task *pTask = NULL; 4434 4435 try 4436 { 4437 if (aProgress != NULL) 4438 { 4439 /* use the existing progress object... */ 4440 pProgress = *aProgress; 4441 4442 /* ...but create a new one if it is null */ 4443 if (pProgress.isNull()) 4444 { 4445 Utf8Str tgtName; 4446 { 4447 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS); 4448 tgtName = pTarget->getName(); 4449 } 4450 4451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4452 4453 pProgress.createObject(); 4454 rc = pProgress->init(m->pVirtualBox, 4455 static_cast<IMedium*>(this), 4456 BstrFmt(tr("Merging hard disk '%s' to '%s'"), 4457 getName().raw(), 4458 tgtName.raw()), 4459 TRUE /* aCancelable */); 4460 if (FAILED(rc)) throw rc; 4461 } 4462 } 4463 4464 /* setup task object to carry out the operation sync/async */ 4465 pTask = new Medium::MergeTask(this, pTarget, fMergeForward, 4466 pParentForTarget, aChildrenToReparent, 4467 pProgress, aMediumLockList, 4468 aWait /* fKeepMediumLockList */); 4469 rc = pTask->rc(); 4745 4470 AssertComRC(rc); 4471 if (FAILED(rc)) throw rc; 4472 } 4473 catch (HRESULT aRC) { rc = aRC; } 4474 4475 if (SUCCEEDED(rc)) 4476 { 4477 if (aWait) 4478 rc = runNow(pTask, pfNeedsSaveSettings); 4479 else 4480 rc = startThread(pTask); 4481 4482 if (SUCCEEDED(rc) && aProgress != NULL) 4483 *aProgress = pProgress; 4484 } 4485 else if (pTask != NULL) 4746 4486 delete pTask; 4747 return rc;4748 }4749 4750 /* Note: task owns aChain (will delete it when not needed) in all cases4751 * except when @a aWait is @c true and runNow() fails -- in this case4752 * aChain will be left away because cancelMergeTo() will be applied by the4753 * caller on it as it is required in the documentation above */4754 4755 if (aWait)4756 {4757 rc = runNow(pTask, pfNeedsSaveSettings);4758 if (FAILED(rc)) return rc;4759 }4760 else4761 {4762 rc = startThread(pTask);4763 if (FAILED(rc)) return rc;4764 }4765 4766 if (aProgress != NULL)4767 *aProgress = progress;4768 4487 4769 4488 return rc; … … 4771 4490 4772 4491 /** 4773 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not called 4774 * or fails. Frees memory occupied by @a aChain. 4775 * 4776 * @param aChain Merge chain created by #prepareMergeTo(). 4492 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not 4493 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks 4494 * the medium objects in @a aChildrenToReparent. 4495 * 4496 * @param aChildrenToReparent List of children of the source which will have 4497 * to be reparented to the target after merge. 4498 * @param aMediumLockList Medium locking information. 4777 4499 * 4778 4500 * @note Locks the hard disks from the chain for writing. 4779 4501 */ 4780 void Medium::cancelMergeTo(MergeChain *aChain) 4502 void Medium::cancelMergeTo(const MediaList &aChildrenToReparent, 4503 MediumLockList *aMediumLockList) 4781 4504 { 4782 4505 AutoCaller autoCaller(this); 4783 4506 AssertComRCReturnVoid(autoCaller.rc()); 4784 4507 4785 AssertReturnVoid(aChain != NULL); 4786 4787 /* the destructor will do the thing */ 4788 delete aChain; 4508 AssertReturnVoid(aMediumLockList != NULL); 4509 4510 /* the destructor will do the work */ 4511 delete aMediumLockList; 4512 4513 /* unlock the children which had to be reparented */ 4514 for (MediaList::const_iterator it = aChildrenToReparent.begin(); 4515 it != aChildrenToReparent.end(); 4516 ++it) 4517 { 4518 const ComObjPtr<Medium> &pMedium = *it; 4519 4520 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 4521 pMedium->UnlockWrite(NULL); 4522 } 4789 4523 } 4790 4524 … … 4808 4542 if (m->formatObj.isNull()) 4809 4543 return setError(E_INVALIDARG, 4810 tr("Invalid hard disk storage format '%ls'"), aFormat); 4544 tr("Invalid hard disk storage format '%ls'"), 4545 aFormat); 4811 4546 4812 4547 /* reference the format permanently to prevent its unexpected … … 4824 4559 m->formatObj->properties().begin(); 4825 4560 it != m->formatObj->properties().end(); 4826 ++ 4561 ++it) 4827 4562 { 4828 4563 m->properties.insert(std::make_pair(it->name, Bstr::Null)); … … 5047 4782 HRESULT Medium::startThread(Medium::Task *pTask) 5048 4783 { 4784 #ifdef VBOX_WITH_MAIN_LOCK_VALIDATION 4785 /* Extreme paranoia: The calling thread should not hold the medium 4786 * tree lock or any medium lock. Since there is no separate lock class 4787 * for medium objects be even more strict: no other object locks. */ 4788 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA)); 4789 Assert(!AutoLockHoldsLocksInClass(getLockingClass())); 4790 #endif 4791 5049 4792 /// @todo use a more descriptive task name 5050 4793 int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask, … … 5081 4824 bool *pfNeedsSaveSettings) 5082 4825 { 4826 #ifdef VBOX_WITH_MAIN_LOCK_VALIDATION 4827 /* Extreme paranoia: The calling thread should not hold the medium 4828 * tree lock or any medium lock. Since there is no separate lock class 4829 * for medium objects be even more strict: no other object locks. */ 4830 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA)); 4831 Assert(!AutoLockHoldsLocksInClass(getLockingClass())); 4832 #endif 4833 5083 4834 pTask->m_pfNeedsSaveSettings = pfNeedsSaveSettings; 5084 4835 … … 5097 4848 * @return 5098 4849 */ 5099 HRESULT Medium::task ThreadCreateBase(Medium::CreateBaseTask &task)4850 HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task) 5100 4851 { 5101 4852 HRESULT rc = S_OK; … … 5107 4858 try 5108 4859 { 5109 /* The lock is also used as a signal from the task initiator (which5110 * releases it only after RTThreadCreate()) that we can start the job */5111 4860 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS); 5112 4861 … … 5122 4871 } 5123 4872 4873 Utf8Str format(m->strFormat); 4874 Utf8Str location(m->strLocationFull); 4875 uint64_t capabilities = m->formatObj->capabilities(); 4876 ComAssertThrow(capabilities & ( VD_CAP_CREATE_FIXED 4877 | VD_CAP_CREATE_DYNAMIC), E_FAIL); 4878 Assert(m->state == MediumState_Creating); 4879 5124 4880 PVBOXHDD hdd; 5125 4881 int vrc = VDCreate(m->vdDiskIfaces, &hdd); 5126 4882 ComAssertRCThrow(vrc, E_FAIL); 5127 4883 5128 Utf8Str format(m->strFormat);5129 Utf8Str location(m->strLocationFull);5130 /* uint64_t capabilities = */ m->formatObj->capabilities();5131 5132 4884 /* unlock before the potentially lengthy operation */ 5133 Assert(m->state == MediumState_Creating);5134 4885 thisLock.leave(); 5135 4886 … … 5214 4965 * 5215 4966 * This task always gets started from Medium::createDiffStorage() and can run 5216 * synchronously or asynchro usly depending on the "wait" parameter passed to4967 * synchronously or asynchronously depending on the "wait" parameter passed to 5217 4968 * that function. If we run synchronously, the caller expects the bool 5218 4969 * *pfNeedsSaveSettings to be set before returning; otherwise (in asynchronous … … 5222 4973 * @return 5223 4974 */ 5224 HRESULT Medium::task ThreadCreateDiff(Medium::CreateDiffTask &task)4975 HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task) 5225 4976 { 5226 4977 HRESULT rc = S_OK; … … 5235 4986 try 5236 4987 { 5237 /* Lock both in {parent,child} order. The lock is also used as a 5238 * signal from the task initiator (which releases it only after 5239 * RTThreadCreate()) that we can start the job*/ 4988 /* Lock both in {parent,child} order. */ 5240 4989 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS); 5241 4990 5242 4991 /* The object may request a specific UUID (through a special form of 5243 * the setLocation() argument). Otherwise we have to generate it */4992 * the setLocation() argument). Otherwise we have to generate it */ 5244 4993 Guid targetId = pTarget->m->id; 5245 4994 fGenerateUuid = targetId.isEmpty(); … … 5251 5000 } 5252 5001 5002 Guid id = m->id; 5003 5004 Utf8Str targetFormat(pTarget->m->strFormat); 5005 Utf8Str targetLocation(pTarget->m->strLocationFull); 5006 uint64_t capabilities = m->formatObj->capabilities(); 5007 ComAssertThrow(capabilities & VD_CAP_CREATE_DYNAMIC, E_FAIL); 5008 5009 Assert(pTarget->m->state == MediumState_Creating); 5010 Assert(m->state == MediumState_LockedRead); 5011 5253 5012 PVBOXHDD hdd; 5254 5013 int vrc = VDCreate(m->vdDiskIfaces, &hdd); 5255 5014 ComAssertRCThrow(vrc, E_FAIL); 5256 5015 5257 Guid id = m->id;5258 Utf8Str format(m->strFormat);5259 Utf8Str location(m->strLocationFull);5260 5261 Utf8Str targetFormat(pTarget->m->strFormat);5262 Utf8Str targetLocation(pTarget->m->strLocationFull);5263 5264 Assert(pTarget->m->state == MediumState_Creating);5265 5266 /* Note: MediumState_LockedWrite is ok when taking an online5267 * snapshot */5268 Assert( m->state == MediumState_LockedRead5269 || m->state == MediumState_LockedWrite);5270 5271 5016 /* the two media are now protected by their non-default states; 5272 5017 * unlock the media before the potentially lengthy operation */ 5273 5018 mediaLock.leave(); 5274 5019 5275 5020 try 5276 5021 { 5277 vrc = VDOpen(hdd, 5278 format.c_str(), 5279 location.c_str(), 5280 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO, 5281 m->vdDiskIfaces); 5282 if (RT_FAILURE(vrc)) 5283 throw setError(E_FAIL, 5284 tr("Could not open the hard disk storage unit '%s'%s"), 5285 location.raw(), vdError(vrc).raw()); 5022 /* Open all hard disk images in the target chain but the last. */ 5023 MediumLockList::Base::const_iterator targetListBegin = 5024 task.mpMediumLockList->GetBegin(); 5025 MediumLockList::Base::const_iterator targetListEnd = 5026 task.mpMediumLockList->GetEnd(); 5027 targetListEnd--; 5028 for (MediumLockList::Base::const_iterator it = targetListBegin; 5029 it != targetListEnd; 5030 ++it) 5031 { 5032 const MediumLock &mediumLock = *it; 5033 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium(); 5034 5035 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 5036 5037 /* sanity check */ 5038 Assert(pMedium->m->state == MediumState_LockedRead); 5039 5040 /* Open all images in appropriate mode. */ 5041 vrc = VDOpen(hdd, 5042 pMedium->m->strFormat.c_str(), 5043 pMedium->m->strLocationFull.c_str(), 5044 VD_OPEN_FLAGS_READONLY, 5045 pMedium->m->vdDiskIfaces); 5046 if (RT_FAILURE(vrc)) 5047 throw setError(E_FAIL, 5048 tr("Could not open the hard disk storage unit '%s'%s"), 5049 pMedium->m->strLocationFull.raw(), 5050 vdError(vrc).raw()); 5051 } 5286 5052 5287 5053 /* ensure the target directory exists */ … … 5326 5092 * potential race! */ 5327 5093 /* diffs for immutable hard disks are auto-reset by default */ 5328 pTarget->m->autoReset = getBase()->m->type == MediumType_Immutable 5329 ? TRUE 5330 : FALSE; 5094 pTarget->m->autoReset = (getBase()->m->type == MediumType_Immutable); 5331 5095 5332 5096 /* register with mVirtualBox as the last step and move to … … 5363 5127 if (task.isAsync()) 5364 5128 { 5365 /* unlock ourselves when done (unless in MediumState_LockedWrite5366 * state because of taking the online snapshot*/5367 if (m->state != MediumState_LockedWrite)5368 {5369 HRESULT rc2 = UnlockRead(NULL);5370 AssertComRC(rc2);5371 }5372 5373 5129 if (fNeedsSaveSettings) 5374 5130 { … … 5405 5161 * @return 5406 5162 */ 5407 HRESULT Medium::task ThreadMerge(Medium::MergeTask &task)5163 HRESULT Medium::taskMergeHandler(Medium::MergeTask &task) 5408 5164 { 5409 5165 HRESULT rc = S_OK; 5410 5166 5411 /* The lock is also used as a signal from the task initiator (which 5412 * releases it only after RTThreadCreate()) that we can start the 5413 * job. We don't actually need the lock for anything else since the 5414 * object is protected by MediumState_Deleting and we don't modify 5415 * its sensitive fields below */ 5416 { 5417 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS); 5418 } 5419 5420 MergeChain *chain = task.mMergeChain.get(); 5167 const ComObjPtr<Medium> &pTarget = task.mTarget; 5421 5168 5422 5169 try … … 5428 5175 try 5429 5176 { 5430 /* Open all hard disks in the chain (they are in the 5431 * {parent,child} order in there. Note that we don't lock 5432 * objects in this chain since they must be in states 5433 * (Deleting and LockedWrite) that prevent from changing 5434 * their format and location fields from outside. */ 5435 5436 for (MergeChain::const_iterator it = chain->begin(); 5437 it != chain->end(); 5177 unsigned uTargetIdx = VD_LAST_IMAGE; 5178 unsigned uSourceIdx = VD_LAST_IMAGE; 5179 /* Open all hard disks in the chain. */ 5180 MediumLockList::Base::iterator lockListBegin = 5181 task.mpMediumLockList->GetBegin(); 5182 MediumLockList::Base::iterator lockListEnd = 5183 task.mpMediumLockList->GetEnd(); 5184 unsigned i = 0; 5185 for (MediumLockList::Base::iterator it = lockListBegin; 5186 it != lockListEnd; 5438 5187 ++it) 5439 5188 { 5189 MediumLock &mediumLock = *it; 5190 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium(); 5191 5192 if (pMedium == this) 5193 uSourceIdx = i; 5194 else if (pMedium == pTarget) 5195 uTargetIdx = i; 5196 5197 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 5198 5440 5199 /* 5441 5200 * complex sanity (sane complexity) … … 5445 5204 * If it is the target it must be in the LockedWrite state. 5446 5205 */ 5447 Assert( ( *it != chain->target()5448 && ( (*it)->m->state == MediumState_Deleting5449 || (*it)->m->state == MediumState_LockedRead))5450 || ( *it == chain->target()5451 && (*it)->m->state == MediumState_LockedWrite));5206 Assert( ( pMedium != pTarget 5207 && ( pMedium->m->state == MediumState_Deleting 5208 || pMedium->m->state == MediumState_LockedRead)) 5209 || ( pMedium == pTarget 5210 && pMedium->m->state == MediumState_LockedWrite)); 5452 5211 5453 5212 /* … … 5456 5215 * to a virtual machine. 5457 5216 */ 5458 Assert( *it == chain->target() 5459 || (*it)->m->state == MediumState_LockedRead 5460 || ( (*it)->m->backRefs.size() == 0 5461 && (*it)->m->state == MediumState_Deleting) 5462 ); 5217 Assert( pMedium == pTarget 5218 || pMedium->m->state == MediumState_LockedRead 5219 || ( pMedium->m->backRefs.size() == 0 5220 && pMedium->m->state == MediumState_Deleting)); 5221 /* The source medium must be in Deleting state. */ 5222 Assert( pMedium != this 5223 || pMedium->m->state == MediumState_Deleting); 5463 5224 5464 5225 unsigned uOpenFlags = 0; 5465 5226 5466 if ( (*it)->m->state == MediumState_LockedRead5467 || (*it)->m->state == MediumState_Deleting)5227 if ( pMedium->m->state == MediumState_LockedRead 5228 || pMedium->m->state == MediumState_Deleting) 5468 5229 uOpenFlags = VD_OPEN_FLAGS_READONLY; 5469 5230 5470 5231 /* Open the image */ 5471 5232 vrc = VDOpen(hdd, 5472 (*it)->m->strFormat.c_str(),5473 (*it)->m->strLocationFull.c_str(),5233 pMedium->m->strFormat.c_str(), 5234 pMedium->m->strLocationFull.c_str(), 5474 5235 uOpenFlags, 5475 (*it)->m->vdDiskIfaces);5236 pMedium->m->vdDiskIfaces); 5476 5237 if (RT_FAILURE(vrc)) 5477 5238 throw vrc; 5239 5240 i++; 5478 5241 } 5479 5242 5480 vrc = VDMerge(hdd, chain->sourceIdx(), chain->targetIdx(), 5243 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE 5244 && uTargetIdx != VD_LAST_IMAGE, E_FAIL); 5245 5246 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx, 5481 5247 task.mVDOperationIfaces); 5482 5248 if (RT_FAILURE(vrc)) … … 5484 5250 5485 5251 /* update parent UUIDs */ 5486 if (! chain->isForward())5252 if (!task.mfMergeForward) 5487 5253 { 5488 5254 /* we need to update UUIDs of all source's children 5489 5255 * which cannot be part of the container at once so 5490 5256 * add each one in there individually */ 5491 if ( chain->children().size() > 0)5257 if (task.mChildrenToReparent.size() > 0) 5492 5258 { 5493 for (MediaList::const_iterator it = chain->children().begin();5494 it != chain->children().end();5259 for (MediaList::const_iterator it = task.mChildrenToReparent.begin(); 5260 it != task.mChildrenToReparent.end(); 5495 5261 ++it) 5496 5262 { … … 5505 5271 5506 5272 vrc = VDSetParentUuid(hdd, 1, 5507 chain->target()->m->id);5273 pTarget->m->id); 5508 5274 if (RT_FAILURE(vrc)) 5509 5275 throw vrc; … … 5521 5287 throw setError(E_FAIL, 5522 5288 tr("Could not merge the hard disk '%s' to '%s'%s"), 5523 chain->source()->m->strLocationFull.raw(), 5524 chain->target()->m->strLocationFull.raw(), 5289 m->strLocationFull.raw(), m->strLocationFull.raw(), 5525 5290 vdError(aVRC).raw()); 5526 5291 } … … 5535 5300 { 5536 5301 /* all hard disks but the target were successfully deleted by 5537 * VDMerge; reparent the last one and uninitialize deleted */5302 * VDMerge; reparent the last one and uninitialize deleted media. */ 5538 5303 5539 5304 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 5540 5305 5541 Medium *pSource = chain->source(); 5542 Medium *pTarget = chain->target(); 5543 5544 if (chain->isForward()) 5306 if (task.mfMergeForward) 5545 5307 { 5546 5308 /* first, unregister the target since it may become a base … … 5552 5314 * both ends (chain->parent() is source's parent) */ 5553 5315 pTarget->deparent(); 5554 pTarget->m->pParent = chain->parent();5316 pTarget->m->pParent = task.mParentForTarget; 5555 5317 if (pTarget->m->pParent) 5556 5318 { 5557 5319 pTarget->m->pParent->m->llChildren.push_back(pTarget); 5558 pSource->deparent();5320 deparent(); 5559 5321 } 5560 5322 … … 5571 5333 targetChild->deparent(); 5572 5334 5573 const MediaList &children = chain->children();5574 5575 5335 /* reparent source's chidren and disconnect the deleted 5576 5336 * branch at the younger end m*/ 5577 if ( children.size() > 0)5337 if (task.mChildrenToReparent.size() > 0) 5578 5338 { 5579 5339 /* obey {parent,child} lock order */ 5580 AutoWriteLock sourceLock( pSourceCOMMA_LOCKVAL_SRC_POS);5581 5582 for (MediaList:: const_iterator it = children.begin();5583 it != children.end();5584 ++it)5340 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS); 5341 5342 for (MediaList::iterator it = task.mChildrenToReparent.begin(); 5343 it != task.mChildrenToReparent.end(); 5344 it++) 5585 5345 { 5586 AutoWriteLock childLock(*it COMMA_LOCKVAL_SRC_POS);5587 5588 Medium *p = *it; 5589 p ->deparent(); // removes pfrom source5590 pTarget->m->llChildren.push_back(p );5591 p ->m->pParent = pTarget;5346 Medium *pMedium = *it; 5347 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS); 5348 5349 pMedium->deparent(); // removes pMedium from source 5350 pTarget->m->llChildren.push_back(pMedium); 5351 pMedium->m->pParent = pTarget; 5592 5352 } 5593 5353 } 5594 5354 } 5595 5355 5596 /* unregister and uninitialize all hard disks in the chain but the target */ 5597 for (MergeChain::iterator it = chain->begin(); 5598 it != chain->end(); 5599 ) 5600 { 5356 /* unregister and uninitialize all hard disks removed by the merge */ 5357 MediumLockList::Base::iterator lockListBegin = 5358 task.mpMediumLockList->GetBegin(); 5359 MediumLockList::Base::iterator lockListEnd = 5360 task.mpMediumLockList->GetEnd(); 5361 for (MediumLockList::Base::iterator it = lockListBegin; 5362 it != lockListEnd; 5363 ) 5364 { 5365 MediumLock &mediumLock = *it; 5366 /* Create a real copy of the medium pointer, as the medium 5367 * lock deletion below would invalidate the referenced object. */ 5368 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium(); 5369 5601 5370 /* The target and all images not merged (readonly) are skipped */ 5602 if ( *it == chain->target()5603 || (*it)->m->state == MediumState_LockedRead)5371 if ( pMedium == pTarget 5372 || pMedium->m->state == MediumState_LockedRead) 5604 5373 { 5605 5374 ++it; … … 5607 5376 } 5608 5377 5609 rc2 = (*it)->m->pVirtualBox->unregisterHardDisk(*it, NULL /*pfNeedsSaveSettings*/); 5378 rc2 = pMedium->m->pVirtualBox->unregisterHardDisk(pMedium, 5379 NULL /*pfNeedsSaveSettings*/); 5610 5380 AssertComRC(rc2); 5611 5381 … … 5622 5392 * and therefore we cannot uninit() it (it's therefore 5623 5393 * the caller's responsibility) */ 5624 if (*it == this) 5394 if (pMedium == this) 5395 { 5396 Assert(getChildren().size() == 0); 5397 Assert(m->backRefs.size() == 0); 5625 5398 task.mMediumCaller.release(); 5626 5627 /* release the caller added by MergeChain before uninit() */ 5628 (*it)->releaseCaller();5629 5630 if (task.isAsync() || *it != this)5631 (*it)->uninit();5632 5633 /* delete (to prevent uninitialization in MergeChain 5634 * dtor) and advance to the next item */5635 it = chain->erase(it);5399 } 5400 5401 /* Delete the medium lock list entry, which also releases the 5402 * caller added by MergeChain before uninit() and updates the 5403 * iterator to point to the right place. */ 5404 rc2 = task.mpMediumLockList->RemoveByIterator(it); 5405 AssertComRC(rc2); 5406 5407 if (task.isAsync() || pMedium != this) 5408 pMedium->uninit(); 5636 5409 } 5637 5410 } … … 5660 5433 * in the mergeTo() docs. The latter also implies that we 5661 5434 * don't own the merge chain, so release it in this case. */ 5662 5663 if (!task.isAsync()) 5664 task.mMergeChain.release(); 5435 if (task.isAsync()) 5436 { 5437 Assert(task.mChildrenToReparent.size() == 0); 5438 cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList); 5439 } 5665 5440 } 5666 5441 … … 5677 5452 * @return 5678 5453 */ 5679 HRESULT Medium::task ThreadClone(Medium::CloneTask &task)5454 HRESULT Medium::taskCloneHandler(Medium::CloneTask &task) 5680 5455 { 5681 5456 HRESULT rc = S_OK; … … 5697 5472 5698 5473 fCreatingTarget = pTarget->m->state == MediumState_Creating; 5699 5700 ImageChain *sourceChain = task.mSourceChain.get();5701 ImageChain *parentChain = task.mParentChain.get();5702 5474 5703 5475 /* The object may request a specific UUID (through a special form of … … 5719 5491 { 5720 5492 /* Open all hard disk images in the source chain. */ 5721 for (MediaList::const_iterator it = sourceChain->begin(); 5722 it != sourceChain->end(); 5493 MediumLockList::Base::const_iterator sourceListBegin = 5494 task.mpSourceMediumLockList->GetBegin(); 5495 MediumLockList::Base::const_iterator sourceListEnd = 5496 task.mpSourceMediumLockList->GetEnd(); 5497 for (MediumLockList::Base::const_iterator it = sourceListBegin; 5498 it != sourceListEnd; 5723 5499 ++it) 5724 5500 { 5501 const MediumLock &mediumLock = *it; 5502 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium(); 5503 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 5504 5725 5505 /* sanity check */ 5726 Assert( (*it)->m->state == MediumState_LockedRead);5506 Assert(pMedium->m->state == MediumState_LockedRead); 5727 5507 5728 5508 /** Open all images in read-only mode. */ 5729 5509 vrc = VDOpen(hdd, 5730 (*it)->m->strFormat.c_str(),5731 (*it)->m->strLocationFull.c_str(),5510 pMedium->m->strFormat.c_str(), 5511 pMedium->m->strLocationFull.c_str(), 5732 5512 VD_OPEN_FLAGS_READONLY, 5733 (*it)->m->vdDiskIfaces);5513 pMedium->m->vdDiskIfaces); 5734 5514 if (RT_FAILURE(vrc)) 5735 5515 throw setError(E_FAIL, 5736 5516 tr("Could not open the hard disk storage unit '%s'%s"), 5737 (*it)->m->strLocationFull.raw(),5517 pMedium->m->strLocationFull.raw(), 5738 5518 vdError(vrc).raw()); 5739 5519 } … … 5760 5540 try 5761 5541 { 5762 /* Open all hard disk images in the parent chain. */ 5763 for (MediaList::const_iterator it = parentChain->begin(); 5764 it != parentChain->end(); 5542 /* Open all hard disk images in the target chain. */ 5543 MediumLockList::Base::const_iterator targetListBegin = 5544 task.mpTargetMediumLockList->GetBegin(); 5545 MediumLockList::Base::const_iterator targetListEnd = 5546 task.mpTargetMediumLockList->GetEnd(); 5547 for (MediumLockList::Base::const_iterator it = targetListBegin; 5548 it != targetListEnd; 5765 5549 ++it) 5766 5550 { 5767 /** @todo r=klaus (*it) is not locked, lots of 5768 * race opportunities below */ 5551 const MediumLock &mediumLock = *it; 5552 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium(); 5553 5554 /* If the target medium is not created yet there's no 5555 * reason to open it. */ 5556 if (pMedium == pTarget && fCreatingTarget) 5557 continue; 5558 5559 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 5560 5769 5561 /* sanity check */ 5770 Assert( (*it)->m->state == MediumState_LockedRead5771 || (*it)->m->state == MediumState_LockedWrite);5562 Assert( pMedium->m->state == MediumState_LockedRead 5563 || pMedium->m->state == MediumState_LockedWrite); 5772 5564 5773 5565 /* Open all images in appropriate mode. */ 5774 5566 vrc = VDOpen(targetHdd, 5775 (*it)->m->strFormat.c_str(),5776 (*it)->m->strLocationFull.c_str(),5777 ( (*it)->m->state == MediumState_LockedWrite) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,5778 (*it)->m->vdDiskIfaces);5567 pMedium->m->strFormat.c_str(), 5568 pMedium->m->strLocationFull.c_str(), 5569 (pMedium->m->state == MediumState_LockedWrite) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY, 5570 pMedium->m->vdDiskIfaces); 5779 5571 if (RT_FAILURE(vrc)) 5780 5572 throw setError(E_FAIL, 5781 5573 tr("Could not open the hard disk storage unit '%s'%s"), 5782 (*it)->m->strLocationFull.raw(),5574 pMedium->m->strLocationFull.raw(), 5783 5575 vdError(vrc).raw()); 5784 5576 } … … 5880 5672 * that we get a deadlock in Appliance::Import when Medium::Close 5881 5673 * is called & the source chain is released at the same time. */ 5882 task.m SourceChain.reset();5674 task.mpSourceMediumLockList->Clear(); 5883 5675 5884 5676 return rc; … … 5895 5687 * @return 5896 5688 */ 5897 HRESULT Medium::task ThreadDelete(Medium::DeleteTask &task)5689 HRESULT Medium::taskDeleteHandler(Medium::DeleteTask &task) 5898 5690 { 5899 5691 NOREF(task); … … 5961 5753 * @return 5962 5754 */ 5963 HRESULT Medium::task ThreadReset(Medium::ResetTask &task)5755 HRESULT Medium::taskResetHandler(Medium::ResetTask &task) 5964 5756 { 5965 5757 HRESULT rc = S_OK; … … 6071 5863 * @return 6072 5864 */ 6073 HRESULT Medium::task ThreadCompact(Medium::CompactTask &task)5865 HRESULT Medium::taskCompactHandler(Medium::CompactTask &task) 6074 5866 { 6075 5867 HRESULT rc = S_OK; … … 6080 5872 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS); 6081 5873 6082 ImageChain *imgChain = task.mImageChain.get();6083 6084 5874 try 6085 5875 { … … 6091 5881 { 6092 5882 /* Open all hard disk images in the chain. */ 6093 MediaList::const_iterator last = imgChain->end(); 6094 last--; 6095 for (MediaList::const_iterator it = imgChain->begin(); 6096 it != imgChain->end(); 5883 MediumLockList::Base::const_iterator mediumListBegin = 5884 task.mpMediumLockList->GetBegin(); 5885 MediumLockList::Base::const_iterator mediumListEnd = 5886 task.mpMediumLockList->GetEnd(); 5887 MediumLockList::Base::const_iterator mediumListLast = 5888 mediumListEnd; 5889 mediumListLast--; 5890 for (MediumLockList::Base::const_iterator it = mediumListBegin; 5891 it != mediumListEnd; 6097 5892 ++it) 6098 5893 { 5894 const MediumLock &mediumLock = *it; 5895 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium(); 5896 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 5897 6099 5898 /* sanity check */ 6100 if (it == last)6101 Assert( (*it)->m->state == MediumState_LockedWrite);5899 if (it == mediumListLast) 5900 Assert(pMedium->m->state == MediumState_LockedWrite); 6102 5901 else 6103 Assert( (*it)->m->state == MediumState_LockedRead);5902 Assert(pMedium->m->state == MediumState_LockedRead); 6104 5903 6105 5904 /** Open all images but last in read-only mode. */ 6106 5905 vrc = VDOpen(hdd, 6107 (*it)->m->strFormat.c_str(),6108 (*it)->m->strLocationFull.c_str(),6109 (it == last) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,6110 (*it)->m->vdDiskIfaces);5906 pMedium->m->strFormat.c_str(), 5907 pMedium->m->strLocationFull.c_str(), 5908 (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY, 5909 pMedium->m->vdDiskIfaces); 6111 5910 if (RT_FAILURE(vrc)) 6112 5911 throw setError(E_FAIL, 6113 5912 tr("Could not open the hard disk storage unit '%s'%s"), 6114 (*it)->m->strLocationFull.raw(),5913 pMedium->m->strLocationFull.raw(), 6115 5914 vdError(vrc).raw()); 6116 5915 } -
trunk/src/VBox/Main/SnapshotImpl.cpp
r28149 r28401 1 /* $Id$ */ 2 1 3 /** @file 2 4 * 3 * COM class implementation for Snapshot and SnapshotMachine .5 * COM class implementation for Snapshot and SnapshotMachine in VBoxSVC. 4 6 */ 5 7 6 8 /* 7 * Copyright (C) 2006-20 07Sun Microsystems, Inc.9 * Copyright (C) 2006-2010 Sun Microsystems, Inc. 8 10 * 9 11 * This file is part of VirtualBox Open Source Edition (OSE), as … … 20 22 */ 21 23 24 #include "Logging.h" 22 25 #include "SnapshotImpl.h" 23 26 … … 34 37 35 38 #include "AutoCaller.h" 36 #include "Logging.h"37 39 38 40 #include <iprt/path.h> … … 107 109 HRESULT Snapshot::FinalConstruct() 108 110 { 109 LogFlow Member(("Snapshot::FinalConstruct()\n"));111 LogFlowThisFunc(("\n")); 110 112 return S_OK; 111 113 } … … 113 115 void Snapshot::FinalRelease() 114 116 { 115 LogFlow Member(("Snapshot::FinalRelease()\n"));117 LogFlowThisFunc(("\n")); 116 118 uninit(); 117 119 } … … 135 137 Snapshot *aParent) 136 138 { 137 LogFlow Member(("Snapshot::init(uuid: %s, aParent->uuid=%s)\n", aId.toString().c_str(), (aParent) ? aParent->m->uuid.toString().c_str() : ""));139 LogFlowThisFunc(("uuid=%s aParent->uuid=%s\n", aId.toString().c_str(), (aParent) ? aParent->m->uuid.toString().c_str() : "")); 138 140 139 141 ComAssertRet(!aId.isEmpty() && !aName.isEmpty() && aMachine, E_INVALIDARG); … … 175 177 void Snapshot::uninit() 176 178 { 177 LogFlow Member(("Snapshot::uninit()\n"));179 LogFlowThisFunc(("\n")); 178 180 179 181 /* Enclose the state transition Ready->InUninit->NotReady */ … … 210 212 211 213 /** 212 * D iscardsthe current snapshot by removing it from the tree of snapshots214 * Delete the current snapshot by removing it from the tree of snapshots 213 215 * and reparenting its children. 214 216 * … … 221 223 * lock in write mode AND the machine state must be DeletingSnapshot. 222 224 */ 223 void Snapshot::begin Discard()225 void Snapshot::beginSnapshotDelete() 224 226 { 225 227 AutoCaller autoCaller(this); … … 231 233 Assert(m->pMachine->isWriteLockOnCurrentThread()); 232 234 233 // the snapshot must have only one child when discarded or no children at all235 // the snapshot must have only one child when being deleted or no children at all 234 236 AssertReturnVoid(m->llChildren.size() <= 1); 235 237 … … 237 239 238 240 /// @todo (dmik): 239 // when we introduce clones later, d iscarding the snapshot240 // will affect the current and first snapshots of clones, if they are241 // direct children of this snapshot. So we will need to lock machines242 // associated with child snapshots as well and update mCurrentSnapshot243 // and/ormFirstSnapshot fields.241 // when we introduce clones later, deleting the snapshot will affect 242 // the current and first snapshots of clones, if they are direct children 243 // of this snapshot. So we will need to lock machines associated with 244 // child snapshots as well and update mCurrentSnapshot and/or 245 // mFirstSnapshot fields. 244 246 245 247 if (this == m->pMachine->mData->mCurrentSnapshot) … … 1148 1150 }; 1149 1151 1150 /** D iscardsnapshot task */1152 /** Delete snapshot task */ 1151 1153 struct SessionMachine::DeleteSnapshotTask 1152 1154 : public SessionMachine::SnapshotTask … … 1685 1687 try 1686 1688 { 1687 /* discardthe saved state file if the machine was Saved prior to this1689 /* Delete the saved state file if the machine was Saved prior to this 1688 1690 * operation */ 1689 1691 if (aTask.machineStateBackup == MachineState_Saved) … … 1811 1813 int saveFlags = 0; 1812 1814 1813 /* we have already d iscarded the current state, so set the execution1814 * state accordingly no matter of the d iscardsnapshot result */1815 /* we have already deleted the current state, so set the execution 1816 * state accordingly no matter of the delete snapshot result */ 1815 1817 if (!mSSData->mStateFilePath.isEmpty()) 1816 1818 setMachineState(MachineState_Saved); … … 1872 1874 LogFlowThisFunc(("Deleting old current state in differencing image '%s'\n", pMedium->getName().raw())); 1873 1875 1874 HRESULT rc2 = pMedium->deleteStorageAndWait(NULL /*aProgress*/, &fNeedsSaveSettings); 1876 HRESULT rc2 = pMedium->deleteStorage(NULL /* aProgress */, 1877 true /* aWait */, 1878 &fNeedsSaveSettings); 1875 1879 // ignore errors here because we cannot roll back after saveSettings() above 1876 1880 if (SUCCEEDED(rc2)) … … 1971 1975 childrenCount); 1972 1976 1973 /* If the snapshot being d iscarded is the current one, ensure current1977 /* If the snapshot being deleted is the current one, ensure current 1974 1978 * settings are committed and saved. 1975 1979 */ … … 2017 2021 if (type != MediumType_Writethrough) // writethrough images are unaffected by snapshots, so do nothing for them 2018 2022 { 2019 // normal or immutable : then this will need to be discarded2023 // normal or immutable media need attention 2020 2024 ++ulOpCount; 2021 2025 ulTotalWeight += (ULONG)(pHD->getSize() / _1M); … … 2069 2073 * Helper struct for SessionMachine::deleteSnapshotHandler(). 2070 2074 */ 2071 struct MediumD iscardRec2072 { 2073 MediumD iscardRec()2074 : chain(NULL)2075 struct MediumDeleteRec 2076 { 2077 MediumDeleteRec() 2078 : mpMediumLockList(NULL) 2075 2079 {} 2076 2080 2077 MediumDiscardRec(const ComObjPtr<Medium> &aHd, 2078 Medium::MergeChain *aChain = NULL) 2079 : hd(aHd), 2080 chain(aChain) 2081 MediumDeleteRec(const ComObjPtr<Medium> &aHd, 2082 const ComObjPtr<Medium> &aSource, 2083 const ComObjPtr<Medium> &aTarget, 2084 bool fMergeForward, 2085 const ComObjPtr<Medium> &aParentForTarget, 2086 const MediaList &aChildrenToReparent, 2087 MediumLockList *aMediumLockList) 2088 : mpHD(aHd), 2089 mpSource(aSource), 2090 mpTarget(aTarget), 2091 mfMergeForward(fMergeForward), 2092 mpParentForTarget(aParentForTarget), 2093 mChildrenToReparent(aChildrenToReparent), 2094 mpMediumLockList(aMediumLockList) 2081 2095 {} 2082 2096 2083 MediumDiscardRec(const ComObjPtr<Medium> &aHd, 2084 Medium::MergeChain *aChain, 2085 const ComObjPtr<Medium> &aReplaceHd, 2086 const ComObjPtr<MediumAttachment> &aReplaceHda, 2087 const Guid &aSnapshotId) 2088 : hd(aHd), 2089 chain(aChain), 2090 replaceHd(aReplaceHd), 2091 replaceHda(aReplaceHda), 2092 snapshotId(aSnapshotId) 2097 MediumDeleteRec(const ComObjPtr<Medium> &aHd, 2098 const ComObjPtr<Medium> &aSource, 2099 const ComObjPtr<Medium> &aTarget, 2100 bool fMergeForward, 2101 const ComObjPtr<Medium> &aParentForTarget, 2102 const MediaList &aChildrenToReparent, 2103 MediumLockList *aMediumLockList, 2104 const ComObjPtr<MediumAttachment> &aReplaceHda, 2105 const Guid &aSnapshotId) 2106 : mpHD(aHd), 2107 mpSource(aSource), 2108 mpTarget(aTarget), 2109 mfMergeForward(fMergeForward), 2110 mpParentForTarget(aParentForTarget), 2111 mChildrenToReparent(aChildrenToReparent), 2112 mpMediumLockList(aMediumLockList), 2113 mpReplaceHda(aReplaceHda), 2114 mSnapshotId(aSnapshotId) 2093 2115 {} 2094 2116 2095 ComObjPtr<Medium> hd; 2096 Medium::MergeChain *chain; 2117 ComObjPtr<Medium> mpHD; 2118 ComObjPtr<Medium> mpSource; 2119 ComObjPtr<Medium> mpTarget; 2120 bool mfMergeForward; 2121 ComObjPtr<Medium> mpParentForTarget; 2122 MediaList mChildrenToReparent; 2123 MediumLockList *mpMediumLockList; 2097 2124 /* these are for the replace hard disk case: */ 2098 ComObjPtr<Medium> replaceHd; 2099 ComObjPtr<MediumAttachment> replaceHda; 2100 Guid snapshotId; 2125 ComObjPtr<MediumAttachment> mpReplaceHda; 2126 Guid mSnapshotId; 2101 2127 }; 2102 2128 2103 typedef std::list <MediumDiscardRec> MediumDiscardRecList;2129 typedef std::list<MediumDeleteRec> MediumDeleteRecList; 2104 2130 2105 2131 /** … … 2138 2164 } 2139 2165 2140 MediumD iscardRecList toDiscard;2166 MediumDeleteRecList toDelete; 2141 2167 2142 2168 HRESULT rc = S_OK; … … 2145 2171 bool fNeedsSaveSettings = false; // VirtualBox.xml 2146 2172 2147 Guid snapshotId 1;2173 Guid snapshotId; 2148 2174 2149 2175 try … … 2159 2185 ComObjPtr<SnapshotMachine> pSnapMachine = aTask.pSnapshot->getSnapshotMachine(); 2160 2186 // no need to lock the snapshot machine since it is const by definiton 2187 Guid machineId = pSnapMachine->getId(); 2161 2188 2162 2189 // save the snapshot ID (for callbacks) 2163 snapshotId 1= aTask.pSnapshot->getId();2190 snapshotId = aTask.pSnapshot->getId(); 2164 2191 2165 2192 // first pass: … … 2177 2204 ComObjPtr<MediumAttachment> &pAttach = *it; 2178 2205 AutoReadLock attachLock(pAttach COMMA_LOCKVAL_SRC_POS); 2179 if (pAttach->getType() == DeviceType_HardDisk) 2206 if (pAttach->getType() != DeviceType_HardDisk) 2207 continue; 2208 2209 ComObjPtr<Medium> pHD = pAttach->getMedium(); 2210 Assert(!pHD.isNull()); 2211 2180 2212 { 2181 Assert(pAttach->getMedium()); 2182 ComObjPtr<Medium> pHD = pAttach->getMedium(); 2183 2213 // writethrough images are unaffected by snapshots, skip them 2214 AutoReadLock medlock(pHD COMMA_LOCKVAL_SRC_POS); 2215 MediumType_T type = pHD->getType(); 2216 if (type == MediumType_Writethrough) 2217 continue; 2218 } 2219 2220 #ifdef DEBUG 2221 pHD->dumpBackRefs(); 2222 #endif 2223 2224 // needs to be merged with child or deleted, check prerequisites 2225 ComObjPtr<Medium> pTarget; 2226 ComObjPtr<Medium> pSource; 2227 bool fMergeForward = false; 2228 ComObjPtr<Medium> pParentForTarget; 2229 MediaList childrenToReparent; 2230 MediumLockList *pMediumLockList = NULL; 2231 rc = prepareDeleteSnapshotMedium(pHD, machineId, snapshotId, 2232 pSource, pTarget, fMergeForward, 2233 pParentForTarget, 2234 childrenToReparent, 2235 pMediumLockList); 2236 if (FAILED(rc)) throw rc; 2237 2238 // for simplicity, we merge pHd onto its child (forward merge), not 2239 // the other way round, because that saves us from updating the 2240 // attachments for the machine that follows the snapshot (next 2241 // snapshot or real machine), unless it's a base image: 2242 2243 if (pMediumLockList != NULL && !fMergeForward) 2244 { 2245 // parent is null -> this disk is a base hard disk: we will 2246 // then do a backward merge, i.e. merge its only child onto the 2247 // base disk. Here we need then to update the attachment that 2248 // refers to the child and have it point to the parent instead 2249 Assert(pHD->getParent().isNull()); 2250 Assert(pHD->getChildren().size() == 1); 2251 2252 ComObjPtr<Medium> pReplaceHD = pHD->getChildren().front(); 2253 2254 ComAssertThrow(pReplaceHD == pSource, E_FAIL); 2255 2256 const Guid *pReplaceMachineId = pSource->getFirstMachineBackrefId(); 2257 NOREF(pReplaceMachineId); 2258 Assert(pReplaceMachineId); 2259 Assert(*pReplaceMachineId == mData->mUuid); 2260 2261 Guid snapshotId2; 2262 const Guid *pSnapshotId = pSource->getFirstMachineBackrefSnapshotId(); 2263 if (pSnapshotId) 2264 snapshotId2 = *pSnapshotId; 2265 2266 HRESULT rc2 = S_OK; 2267 2268 attachLock.release(); 2269 2270 // First we must detach the child otherwise mergeTo() will 2271 // will assert because it is going to delete the child. 2272 // Adjust the backreferences: 2273 // 1) detach the first child hard disk 2274 rc2 = pSource->detachFrom(mData->mUuid, snapshotId2); 2275 AssertComRC(rc2); 2276 // 2) attach to machine and snapshot 2277 rc2 = pHD->attachTo(mData->mUuid, snapshotId2); 2278 AssertComRC(rc2); 2279 2280 /* replace the hard disk in the attachment object */ 2281 if (snapshotId2.isEmpty()) 2184 2282 { 2185 // writethrough images are unaffected by snapshots, so do nothing for them 2186 AutoReadLock medlock(pHD COMMA_LOCKVAL_SRC_POS); 2187 MediumType_T type = pHD->getType(); 2188 if (type == MediumType_Writethrough) 2189 continue; 2283 /* in current state */ 2284 AssertBreak(pAttach = findAttachment(mMediaData->mAttachments, pSource)); 2190 2285 } 2191 2192 // image is normal or immutable: then this will need to be discarded 2193 2194 // do not lock medium now, prepareDiscard() has a write lock which will hang otherwise 2195 2196 #ifdef DEBUG 2197 pHD->dumpBackRefs(); 2198 #endif 2199 2200 Medium::MergeChain *chain = NULL; 2201 2202 // needs to be discarded (merged with the child if any), check prerequisites 2203 rc = pHD->prepareDiscard(chain); 2204 if (FAILED(rc)) throw rc; 2205 2206 // for simplicity, we merge pHd onto its child (forward merge), not the 2207 // other way round, because that saves us from updating the attachments 2208 // for the machine that follows the snapshot (next snapshot or real machine), 2209 // unless it's a base image: 2210 2211 if ( pHD->getParent().isNull() 2212 && chain != NULL 2213 ) 2286 else 2214 2287 { 2215 // parent is null -> this disk is a base hard disk: we will 2216 // then do a backward merge, i.e. merge its only child onto 2217 // the base disk; prepareDiscard() does necessary checks. 2218 // So here we need then to update the attachment that refers 2219 // to the child and have it point to the parent instead 2220 2221 /* The below assert would be nice but I don't want to move 2222 * Medium::MergeChain to the header just for that 2223 * Assert(!chain->isForward()); */ 2224 2225 // prepareDiscard() should have raised an error already 2226 // if there was more than one child 2227 Assert(pHD->getChildren().size() == 1); 2228 2229 ComObjPtr<Medium> pReplaceHD = pHD->getChildren().front(); 2230 2231 const Guid *pReplaceMachineId = pReplaceHD->getFirstMachineBackrefId(); 2232 NOREF(pReplaceMachineId); 2233 Assert(pReplaceMachineId); 2234 Assert(*pReplaceMachineId == mData->mUuid); 2235 2236 Guid snapshotId; 2237 const Guid *pSnapshotId = pReplaceHD->getFirstMachineBackrefSnapshotId(); 2238 if (pSnapshotId) 2239 snapshotId = *pSnapshotId; 2240 2241 HRESULT rc2 = S_OK; 2242 2243 attachLock.release(); 2244 2245 // First we must detach the child (otherwise mergeTo() called 2246 // by discard() will assert because it will be going to delete 2247 // the child), so adjust the backreferences: 2248 // 1) detach the first child hard disk 2249 rc2 = pReplaceHD->detachFrom(mData->mUuid, snapshotId); 2288 /* in snapshot */ 2289 ComObjPtr<Snapshot> snapshot; 2290 rc2 = findSnapshot(snapshotId2, snapshot); 2250 2291 AssertComRC(rc2); 2251 // 2) attach to machine and snapshot 2252 rc2 = pHD->attachTo(mData->mUuid, snapshotId); 2253 AssertComRC(rc2); 2254 2255 /* replace the hard disk in the attachment object */ 2256 if (snapshotId.isEmpty()) 2257 { 2258 /* in current state */ 2259 AssertBreak(pAttach = findAttachment(mMediaData->mAttachments, pReplaceHD)); 2260 } 2261 else 2262 { 2263 /* in snapshot */ 2264 ComObjPtr<Snapshot> snapshot; 2265 rc2 = findSnapshot(snapshotId, snapshot); 2266 AssertComRC(rc2); 2267 2268 /* don't lock the snapshot; cannot be modified outside */ 2269 MediaData::AttachmentList &snapAtts = snapshot->getSnapshotMachine()->mMediaData->mAttachments; 2270 AssertBreak(pAttach = findAttachment(snapAtts, pReplaceHD)); 2271 } 2272 2273 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 2274 pAttach->updateMedium(pHD, false /* aImplicit */); 2275 2276 toDiscard.push_back(MediumDiscardRec(pHD, 2277 chain, 2278 pReplaceHD, 2279 pAttach, 2280 snapshotId)); 2281 continue; 2292 2293 /* don't lock the snapshot; cannot be modified outside */ 2294 MediaData::AttachmentList &snapAtts = snapshot->getSnapshotMachine()->mMediaData->mAttachments; 2295 AssertBreak(pAttach = findAttachment(snapAtts, pSource)); 2282 2296 } 2283 2297 2284 toDiscard.push_back(MediumDiscardRec(pHD, chain)); 2298 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 2299 pAttach->updateMedium(pHD, false /* aImplicit */); 2300 2301 toDelete.push_back(MediumDeleteRec(pHD, pSource, pTarget, 2302 fMergeForward, 2303 pParentForTarget, 2304 childrenToReparent, 2305 pMediumLockList, 2306 pAttach, snapshotId2)); 2285 2307 } 2308 else 2309 toDelete.push_back(MediumDeleteRec(pHD, pSource, pTarget, 2310 fMergeForward, 2311 pParentForTarget, 2312 childrenToReparent, 2313 pMediumLockList)); 2286 2314 } 2287 2315 … … 2289 2317 multiLock.release(); 2290 2318 2319 /** @todo r=klaus the comment below makes only limited sense to me. 2320 * Deleting the snapshot early has some benefits, but also the drawback 2321 * that a normal user surely has difficulties to perform the not 2322 * completed merge operations later. Before IMedium::mergeTo is added 2323 * to VBoxManage it actually is completely impossible. */ 2324 2291 2325 /* Now we checked that we can successfully merge all normal hard disks 2292 2326 * (unless a runtime error like end-of-disc happens). Prior to 2293 * performing the actual merge, we want to d iscardthe snapshot itself2327 * performing the actual merge, we want to delete the snapshot itself 2294 2328 * and remove it from the XML file to make sure that a possible merge 2295 * ru intime error will not make this snapshot inconsistent because of2329 * runtime error will not make this snapshot inconsistent because of 2296 2330 * the partially merged or corrupted hard disks */ 2297 2331 2298 2332 /* second pass: */ 2299 LogFlowThisFunc(("2: D iscarding snapshot...\n"));2333 LogFlowThisFunc(("2: Deleting snapshot...\n")); 2300 2334 2301 2335 { … … 2307 2341 Utf8Str stateFilePath = aTask.pSnapshot->stateFilePath(); 2308 2342 2309 // Note that d iscarding the snapshot will deassociate it from the2343 // Note that deleting the snapshot will deassociate it from the 2310 2344 // hard disks which will allow the merge+delete operation for them 2311 aTask.pSnapshot->begin Discard();2345 aTask.pSnapshot->beginSnapshotDelete(); 2312 2346 aTask.pSnapshot->uninit(); 2313 2347 // this requests the machine lock in turn when deleting all the children … … 2320 2354 if (!stateFilePath.isEmpty()) 2321 2355 { 2322 aTask.pProgress->SetNextOperation(Bstr(tr("D iscarding the execution state")),2356 aTask.pProgress->SetNextOperation(Bstr(tr("Deleting the execution state")), 2323 2357 1); // weight 2324 2358 … … 2330 2364 /// saveSnapshotSettings fails. Actually, we may call 2331 2365 /// #saveSnapshotSettings() with a special flag that will tell it to 2332 /// skip the given snapshot as if it would have been d iscarded and2333 /// only actually d iscardit if the save operation succeeds.2366 /// skip the given snapshot as if it would have been deleted and 2367 /// only actually delete it if the save operation succeeds. 2334 2368 } 2335 2369 2336 /* here we come when we've irreve sibly discarded the snapshot which2370 /* here we come when we've irreversibly eleted the snapshot which 2337 2371 * means that the VM settigns (our relevant changes to mData) need to be 2338 2372 * saved too */ … … 2347 2381 /// snapshot itself has been already deleted (and interpret these 2348 2382 /// warnings properly on the GUI side) 2349 for (MediumD iscardRecList::iterator it = toDiscard.begin();2350 it != toD iscard.end();)2383 for (MediumDeleteRecList::iterator it = toDelete.begin(); 2384 it != toDelete.end();) 2351 2385 { 2352 rc = it->hd->discard(aTask.pProgress, 2353 (ULONG)(it->hd->getSize() / _1M), // weight 2354 it->chain, 2355 &fNeedsSaveSettings); 2386 const ComObjPtr<Medium> &pMedium(it->mpHD); 2387 ULONG ulWeight; 2388 2389 { 2390 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 2391 ulWeight = (ULONG)(pMedium->getSize() / _1M); 2392 } 2393 2394 aTask.pProgress->SetNextOperation(BstrFmt(tr("Merging differencing image '%s'"), 2395 pMedium->getName().raw()), 2396 ulWeight); 2397 2398 if (it->mpMediumLockList == NULL) 2399 { 2400 /* no real merge needed, just updating state and delete 2401 * diff files if necessary */ 2402 AutoMultiWriteLock2 mLock(&mParent->getMediaTreeLockHandle(), pMedium->lockHandle() COMMA_LOCKVAL_SRC_POS); 2403 2404 Assert( !it->mfMergeForward 2405 || pMedium->getChildren().size() == 0); 2406 2407 /* Delete the differencing hard disk (has no children). Two 2408 * exceptions: if it's the last medium in the chain or if it's 2409 * a backward merge we don't want to handle due to complextity. 2410 * In both cases leave the image in place. If it's the first 2411 * exception the user can delete it later if he wants. */ 2412 if (!pMedium->getParent().isNull()) 2413 { 2414 Assert(pMedium->getState() == MediumState_Deleting); 2415 rc = pMedium->deleteStorage(&aTask.pProgress, 2416 true /* aWait */, 2417 &fNeedsSaveSettings); 2418 if (SUCCEEDED(rc)) 2419 pMedium->uninit(); 2420 } 2421 } 2422 else 2423 { 2424 /* Normal merge operation, in the direction decided earlier. */ 2425 rc = it->mpSource->mergeTo(it->mpTarget, it->mfMergeForward, 2426 it->mpParentForTarget, 2427 it->mChildrenToReparent, 2428 it->mpMediumLockList, 2429 &aTask.pProgress, true /* aWait */, 2430 &fNeedsSaveSettings); 2431 if (SUCCEEDED(rc)) 2432 { 2433 /* On success delete the no longer needed medium lock 2434 * list. This unlocks the media as well. */ 2435 delete it->mpMediumLockList; 2436 it->mpMediumLockList = NULL; 2437 it->mpSource->uninit(); 2438 } 2439 } 2440 2356 2441 if (FAILED(rc)) throw rc; 2357 2442 2358 /* prevent from calling cancelD iscard() */2359 it = toD iscard.erase(it);2443 /* prevent from calling cancelDeleteSnapshotMedium() */ 2444 it = toDelete.erase(it); 2360 2445 } 2361 2446 } … … 2364 2449 if (FAILED(rc)) 2365 2450 { 2366 HRESULT rc2 = S_OK;2367 2368 2451 AutoMultiWriteLock2 multiLock(this->lockHandle(), // machine 2369 2452 &mParent->getMediaTreeLockHandle() // media tree … … 2371 2454 2372 2455 // un-prepare the remaining hard disks 2373 for (MediumD iscardRecList::const_iterator it = toDiscard.begin();2374 it != toD iscard.end();2456 for (MediumDeleteRecList::const_iterator it = toDelete.begin(); 2457 it != toDelete.end(); 2375 2458 ++it) 2376 2459 { 2377 it->hd->cancelDiscard(it->chain); 2378 2379 if (!it->replaceHd.isNull()) 2380 { 2381 rc2 = it->replaceHd->attachTo(mData->mUuid, it->snapshotId); 2382 AssertComRC(rc2); 2383 2384 rc2 = it->hd->detachFrom(mData->mUuid, it->snapshotId); 2385 AssertComRC(rc2); 2386 2387 AutoWriteLock attLock(it->replaceHda COMMA_LOCKVAL_SRC_POS); 2388 it->replaceHda->updateMedium(it->replaceHd, false /* aImplicit */); 2389 } 2460 cancelDeleteSnapshotMedium(it->mpHD, it->mpSource, it->mpTarget, 2461 it->mChildrenToReparent, 2462 it->mpMediumLockList, 2463 it->mpReplaceHda, it->mSnapshotId); 2390 2464 } 2391 2465 } … … 2423 2497 2424 2498 if (SUCCEEDED(rc)) 2425 mParent->onSnapshotDeleted(mData->mUuid, snapshotId 1);2499 mParent->onSnapshotDeleted(mData->mUuid, snapshotId); 2426 2500 2427 2501 LogFlowThisFunc(("Done deleting snapshot (rc=%08X)\n", rc)); … … 2429 2503 } 2430 2504 2505 /** 2506 * Checks that this hard disk (part of a snapshot) may be deleted/merged and 2507 * performs necessary state changes. Must not be called for writethrough disks 2508 * because there is nothing to delete/merge then. 2509 * 2510 * This method is to be called prior to calling #deleteSnapshotMedium(). 2511 * If #deleteSnapshotMedium() is not called or fails, the state modifications 2512 * performed by this method must be undone by #cancelDeleteSnapshotMedium(). 2513 * 2514 * @param aHD Hard disk which is connected to the snapshot. 2515 * @param aMachineId UUID of machine this hard disk is attached to. 2516 * @param aSnapshotId UUID of snapshot this hard disk is attached to. May 2517 * be a zero UUID if no snapshot is applicable. 2518 * @param aSource Source hard disk for merge (out). 2519 * @param aTarget Target hard disk for merge (out). 2520 * @param aMergeForward Merge direction decision (out). 2521 * @param aParentForTarget New parent if target needs to be reparented (out). 2522 * @param aChildrenToReparent Children which have to be reparented to the 2523 * target (out). 2524 * @param aMediumLockList Where to store the created medium lock list (may 2525 * return NULL if no real merge is necessary). 2526 * 2527 * @note Caller must hold media tree lock for writing. This locks this object 2528 * and every medium object on the merge chain for writing. 2529 */ 2530 HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD, 2531 const Guid &aMachineId, 2532 const Guid &aSnapshotId, 2533 ComObjPtr<Medium> &aSource, 2534 ComObjPtr<Medium> &aTarget, 2535 bool &aMergeForward, 2536 ComObjPtr<Medium> &aParentForTarget, 2537 MediaList &aChildrenToReparent, 2538 MediumLockList * &aMediumLockList) 2539 { 2540 Assert(mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread()); 2541 2542 AutoWriteLock alock(aHD COMMA_LOCKVAL_SRC_POS); 2543 2544 // Medium must not be writethrough at this point 2545 AssertReturn(aHD->getType() != MediumType_Writethrough, E_FAIL); 2546 2547 aMediumLockList = NULL; 2548 2549 if (aHD->getChildren().size() == 0) 2550 { 2551 /* special treatment of the last hard disk in the chain: */ 2552 if (aHD->getParent().isNull()) 2553 { 2554 /* lock only, to prevent any usage until the snapshot deletion 2555 * is completed */ 2556 return aHD->LockWrite(NULL); 2557 } 2558 2559 /* the differencing hard disk w/o children will be deleted, protect it 2560 * from attaching to other VMs (this is why Deleting) */ 2561 return aHD->markForDeletion(); 2562 } 2563 2564 /* not going multi-merge as it's too expensive */ 2565 if (aHD->getChildren().size() > 1) 2566 return setError(E_FAIL, 2567 tr ("Hard disk '%s' has more than one child hard disk (%d)"), 2568 aHD->getLocationFull().raw(), 2569 aHD->getChildren().size()); 2570 2571 ComObjPtr<Medium> pChild = aHD->getChildren().front(); 2572 2573 /* we keep this locked, so lock the affected child to make sure the lock 2574 * order is correct when calling prepareMergeTo() */ 2575 AutoWriteLock childLock(pChild COMMA_LOCKVAL_SRC_POS); 2576 2577 /* the rest is a normal merge setup */ 2578 if (aHD->getParent().isNull()) 2579 { 2580 /* base hard disk, backward merge */ 2581 const Guid *pMachineId1 = pChild->getFirstMachineBackrefId(); 2582 const Guid *pMachineId2 = aHD->getFirstMachineBackrefId(); 2583 if (pMachineId1 && pMachineId2 && *pMachineId1 != *pMachineId2) 2584 { 2585 /* backward merge is too tricky, we'll just detach on snapshot 2586 * deletion, so lock only, to prevent any usage */ 2587 return aHD->LockWrite(NULL); 2588 } 2589 2590 aSource = pChild; 2591 aTarget = aHD; 2592 } 2593 else 2594 { 2595 /* forward merge */ 2596 aSource = aHD; 2597 aTarget = pChild; 2598 } 2599 2600 return aSource->prepareMergeTo(aTarget, &aMachineId, &aSnapshotId, 2601 aMergeForward, aParentForTarget, 2602 aChildrenToReparent, aMediumLockList); 2603 } 2604 2605 /** 2606 * Cancels the deletion/merging of this hard disk (part of a snapshot). Undoes 2607 * what #prepareDeleteSnapshotMedium() did. Must be called if 2608 * #deleteSnapshotMedium() is not called or fails. 2609 * 2610 * @param aHD Hard disk which is connected to the snapshot. 2611 * @param aSource Source hard disk for merge. 2612 * @param aTarget Source hard disk for merge. 2613 * @param aChildrenToReparent Children to unlock. 2614 * @param aMediumLockList Medium locks to cancel. 2615 * @param aReplaceHda Hard disk attachment to restore. 2616 * @param aSnapshotId Snapshot id to attach the medium to. 2617 * 2618 * @note Locks the medium tree and the hard disks in the chain for writing. 2619 */ 2620 void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD, 2621 const ComObjPtr<Medium> &aSource, 2622 const ComObjPtr<Medium> &aTarget, 2623 const MediaList &aChildrenToReparent, 2624 MediumLockList *aMediumLockList, 2625 const ComObjPtr<MediumAttachment> &aReplaceHda, 2626 const Guid &aSnapshotId) 2627 { 2628 if (aMediumLockList == NULL) 2629 { 2630 AutoMultiWriteLock2 mLock(&mParent->getMediaTreeLockHandle(), aHD->lockHandle() COMMA_LOCKVAL_SRC_POS); 2631 2632 Assert(aHD->getChildren().size() == 0); 2633 2634 if (aHD->getParent().isNull()) 2635 { 2636 HRESULT rc = aHD->UnlockWrite(NULL);; 2637 AssertComRC(rc); 2638 } 2639 else 2640 { 2641 HRESULT rc = aHD->unmarkForDeletion(); 2642 AssertComRC(rc); 2643 } 2644 } 2645 else 2646 aSource->cancelMergeTo(aChildrenToReparent, aMediumLockList); 2647 2648 if (!aReplaceHda.isNull()) 2649 { 2650 HRESULT rc = aTarget->attachTo(mData->mUuid, aSnapshotId); 2651 AssertComRC(rc); 2652 2653 rc = aHD->detachFrom(mData->mUuid, aSnapshotId); 2654 AssertComRC(rc); 2655 2656 AutoWriteLock attLock(aReplaceHda COMMA_LOCKVAL_SRC_POS); 2657 aReplaceHda->updateMedium(aTarget, false /* aImplicit */); 2658 } 2659 } -
trunk/src/VBox/Main/idl/VirtualBox.xidl
r28377 r28401 9159 9159 <interface 9160 9160 name="IMedium" extends="$unknown" 9161 uuid=" aa8167ba-df72-4738-b740-9b84377ba9f1"9161 uuid="d709160c-303f-4ead-b7ef-53ffa26aa861" 9162 9162 wsmap="managed" 9163 9163 > … … 10351 10351 </note> 10352 10352 </desc> 10353 <param name="target Id" type="uuid" mod="string" dir="in">10354 <desc> UUID of the target ancestor or descendant medium.</desc>10353 <param name="target" type="IMedium" dir="in"> 10354 <desc>Target medium.</desc> 10355 10355 </param> 10356 10356 <param name="progress" type="IProgress" dir="return"> -
trunk/src/VBox/Main/include/Logging.h
r14949 r28401 1 /* $Id$ */ 2 1 3 /** @file 2 4 * … … 5 7 6 8 /* 7 * Copyright (C) 2006-20 07Sun Microsystems, Inc.9 * Copyright (C) 2006-2010 Sun Microsystems, Inc. 8 10 * 9 11 * This file is part of VirtualBox Open Source Edition (OSE), as … … 54 56 #include <iprt/assert.h> 55 57 56 /** @deprecated Please use LogFlowThisFunc instead! */57 #define LogFlowMember(m) \58 do { LogFlow (("{%p} ", this)); LogFlow (m); } while (0)59 60 58 /** @def MyLogIt 61 59 * Copy of LogIt that works even when logging is completely disabled (e.g. in -
trunk/src/VBox/Main/include/MachineImpl.h
r28343 r28401 3 3 /** @file 4 4 * 5 * VirtualBox COM class declaration5 * VirtualBox COM class implementation 6 6 */ 7 7 … … 29 29 #include "VRDPServerImpl.h" 30 30 #include "MediumAttachmentImpl.h" 31 #include "MediumLock.h" 31 32 #include "NetworkAdapterImpl.h" 32 33 #include "AudioAdapterImpl.h" … … 148 149 ComObjPtr<SessionMachine> mMachine; 149 150 150 /** 151 * Successfully locked media list. The 2nd value in the pair is true 152 * if the medium is locked for writing and false if locked for 153 * reading. 154 */ 155 typedef std::list<std::pair<ComPtr<IMedium>, bool > > LockedMedia; 156 LockedMedia mLockedMedia; 151 /** Medium object lock collection. */ 152 MediumLockListMap mLockedMedia; 157 153 }; 158 154 … … 316 312 * The usage policy is the same as for HWData, but a separate structure 317 313 * is necessary because hard disk data requires different procedures when 318 * taking or d iscarding snapshots, etc.314 * taking or deleting snapshots, etc. 319 315 * 320 316 * The data variable is |mMediaData|. … … 997 993 void restoreSnapshotHandler(RestoreSnapshotTask &aTask); 998 994 995 HRESULT prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD, 996 const Guid &machineId, 997 const Guid &snapshotId, 998 ComObjPtr<Medium> &aSource, 999 ComObjPtr<Medium> &aTarget, 1000 bool &fMergeForward, 1001 ComObjPtr<Medium> &pParentForTarget, 1002 MediaList &aChildrenToReparent, 1003 MediumLockList * &aMediumLockList); 1004 void cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD, 1005 const ComObjPtr<Medium> &aSource, 1006 const ComObjPtr<Medium> &aTarget, 1007 const MediaList &aChildrenToReparent, 1008 MediumLockList *aMediumLockList, 1009 const ComObjPtr<MediumAttachment> &aReplaceHda, 1010 const Guid &aSnapshotId); 1011 999 1012 HRESULT lockMedia(); 1000 1013 void unlockMedia(); -
trunk/src/VBox/Main/include/MediumImpl.h
-
Property svn:keywords
changed from
Date Revision Author Id
toAuthor Date Id Revision
r27831 r28401 1 1 /* $Id$ */ 2 2 3 /** @file 3 4 * … … 25 26 26 27 #include "VirtualBoxBase.h" 28 #include "MediumLock.h" 27 29 28 30 class Progress; … … 45 47 { 46 48 public: 47 class MergeChain;48 class ImageChain;49 50 49 VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(Medium) 51 50 … … 144 143 MediumVariant_T aVariant, 145 144 IProgress **aProgress); 146 STDMETHOD(MergeTo)(I N_BSTR aTargetId, IProgress **aProgress);145 STDMETHOD(MergeTo)(IMedium *aTarget, IProgress **aProgress); 147 146 STDMETHOD(CloneTo)(IMedium *aTarget, MediumVariant_T aVariant, 148 147 IMedium *aParent, IProgress **aProgress); … … 155 154 const MediaList& getChildren() const; 156 155 156 // unsafe methods for internal purposes only (ensure there is 157 // a caller and a read lock before calling them!) 157 158 const Guid& getId() const; 158 159 MediumState_T getState() const; … … 160 161 const Utf8Str& getLocationFull() const; 161 162 uint64_t getSize() const; 163 MediumType_T getType() const; 164 Utf8Str getName(); 162 165 163 166 HRESULT attachTo(const Guid &aMachineId, … … 184 187 HRESULT compareLocationTo(const char *aLocation, int &aResult); 185 188 186 /** 187 * Shortcut to #deleteStorage() that doesn't wait for operation completion 188 * and implies the progress object will be used for waiting. 189 */ 190 HRESULT deleteStorageNoWait(ComObjPtr<Progress> &aProgress) 191 { return deleteStorage(&aProgress, false /* aWait */, NULL /* pfNeedsSaveSettings */); } 192 193 /** 194 * Shortcut to #deleteStorage() that wait for operation completion by 195 * blocking the current thread. 196 */ 197 HRESULT deleteStorageAndWait(ComObjPtr<Progress> *aProgress, bool *pfNeedsSaveSettings) 198 { return deleteStorage(aProgress, true /* aWait */, pfNeedsSaveSettings); } 199 200 /** 201 * Shortcut to #createDiffStorage() that doesn't wait for operation 202 * completion and implies the progress object will be used for waiting. 203 */ 204 HRESULT createDiffStorageNoWait(ComObjPtr<Medium> &aTarget, 205 MediumVariant_T aVariant, 206 ComObjPtr<Progress> &aProgress) 207 { return createDiffStorage(aTarget, aVariant, &aProgress, false /* aWait */, NULL /* pfNeedsSaveSettings*/ ); } 208 209 /** 210 * Shortcut to #createDiffStorage() that wait for operation completion by 211 * blocking the current thread. 212 */ 213 HRESULT createDiffStorageAndWait(ComObjPtr<Medium> &aTarget, 214 MediumVariant_T aVariant, 215 bool *pfNeedsSaveSettings) 216 { return createDiffStorage(aTarget, aVariant, NULL /*aProgress*/, true /* aWait */, pfNeedsSaveSettings); } 217 218 HRESULT prepareMergeTo(Medium *aTarget, MergeChain * &aChain, 219 bool aIgnoreAttachments = false); 220 221 /** 222 * Shortcut to #mergeTo() that doesn't wait for operation completion and 223 * implies the progress object will be used for waiting. 224 */ 225 HRESULT mergeToNoWait(MergeChain *aChain, 226 ComObjPtr<Progress> &aProgress) 227 { return mergeTo(aChain, &aProgress, false /* aWait */, NULL /*pfNeedsSaveSettings*/); } 228 229 /** 230 * Shortcut to #mergeTo() that wait for operation completion by 231 * blocking the current thread. 232 */ 233 HRESULT mergeToAndWait(MergeChain *aChain, 234 ComObjPtr<Progress> *aProgress, 235 bool *pfNeedsSaveSettings) 236 { return mergeTo(aChain, aProgress, true /* aWait */, pfNeedsSaveSettings); } 237 238 void cancelMergeTo(MergeChain *aChain); 239 240 Utf8Str getName(); 241 242 HRESULT prepareDiscard(MergeChain * &aChain); 243 HRESULT discard(ComObjPtr<Progress> &aProgress, ULONG ulWeight, MergeChain *aChain, bool *pfNeedsSaveSettings); 244 void cancelDiscard(MergeChain *aChain); 189 HRESULT createMediumLockList(bool fMediumWritable, Medium *pToBeParent, MediumLockList &mediumLockList); 190 191 HRESULT createDiffStorage(ComObjPtr<Medium> &aTarget, 192 MediumVariant_T aVariant, 193 MediumLockList *pMediumLockList, 194 ComObjPtr<Progress> *aProgress, 195 bool aWait, 196 bool *pfNeedsSaveSettings); 197 198 HRESULT deleteStorage(ComObjPtr<Progress> *aProgress, bool aWait, bool *pfNeedsSaveSettings); 199 HRESULT markForDeletion(); 200 HRESULT unmarkForDeletion(); 201 202 HRESULT prepareMergeTo(const ComObjPtr<Medium> &pTarget, 203 const Guid *aMachineId, 204 const Guid *aSnapshotId, 205 bool &fMergeForward, 206 ComObjPtr<Medium> &pParentForTarget, 207 MediaList &aChildrenToReparent, 208 MediumLockList * &aMediumLockList); 209 HRESULT mergeTo(const ComObjPtr<Medium> &pTarget, 210 bool &fMergeForward, 211 ComObjPtr<Medium> pParentForTarget, 212 const MediaList &aChildrenToReparent, 213 MediumLockList *aMediumLockList, 214 ComObjPtr<Progress> *aProgress, 215 bool aWait, 216 bool *pfNeedsSaveSettings); 217 void cancelMergeTo(const MediaList &aChildrenToReparent, 218 MediumLockList *aMediumLockList); 245 219 246 220 /** Returns a preferred format for a differencing hard disk. */ 247 221 Bstr preferredDiffFormat(); 248 249 // unsafe inline public methods for internal purposes only (ensure there is250 // a caller and a read lock before calling them!)251 MediumType_T getType() const;252 222 253 223 /** For com::SupportErrorInfoImpl. */ … … 272 242 273 243 HRESULT setStateError(); 274 275 HRESULT deleteStorage(ComObjPtr<Progress> *aProgress, bool aWait, bool *pfNeedsSaveSettings);276 277 HRESULT createDiffStorage(ComObjPtr<Medium> &aTarget,278 MediumVariant_T aVariant,279 ComObjPtr<Progress> *aProgress,280 bool aWait,281 bool *pfNeedsSaveSettings);282 283 HRESULT mergeTo(MergeChain *aChain,284 ComObjPtr<Progress> *aProgress,285 bool aWait,286 bool *pfNeedsSaveSettings);287 244 288 245 HRESULT setLocation(const Utf8Str &aLocation, const Utf8Str &aFormat = Utf8Str()); … … 321 278 HRESULT runNow(Medium::Task *pTask, bool *pfNeedsSaveSettings); 322 279 323 HRESULT task ThreadCreateBase(Medium::CreateBaseTask &task);324 HRESULT task ThreadCreateDiff(Medium::CreateDiffTask &task);325 HRESULT task ThreadMerge(Medium::MergeTask &task);326 HRESULT task ThreadClone(Medium::CloneTask &task);327 HRESULT task ThreadDelete(Medium::DeleteTask &task);328 HRESULT task ThreadReset(Medium::ResetTask &task);329 HRESULT task ThreadCompact(Medium::CompactTask &task);280 HRESULT taskCreateBaseHandler(Medium::CreateBaseTask &task); 281 HRESULT taskCreateDiffHandler(Medium::CreateDiffTask &task); 282 HRESULT taskMergeHandler(Medium::MergeTask &task); 283 HRESULT taskCloneHandler(Medium::CloneTask &task); 284 HRESULT taskDeleteHandler(Medium::DeleteTask &task); 285 HRESULT taskResetHandler(Medium::ResetTask &task); 286 HRESULT taskCompactHandler(Medium::CompactTask &task); 330 287 331 288 struct Data; // opaque data struct, defined in MediumImpl.cpp -
Property svn:keywords
changed from
-
trunk/src/VBox/Main/include/SnapshotImpl.h
r26044 r28401 1 /* $Id$ */ 2 1 3 /** @file 2 4 * … … 5 7 6 8 /* 7 * Copyright (C) 2006-20 09Sun Microsystems, Inc.9 * Copyright (C) 2006-2010 Sun Microsystems, Inc. 8 10 * 9 11 * This file is part of VirtualBox Open Source Edition (OSE), as … … 70 72 void uninit(); 71 73 72 void begin Discard();74 void beginSnapshotDelete(); 73 75 74 76 void deparent();
Note:
See TracChangeset
for help on using the changeset viewer.