summaryrefslogtreecommitdiff
path: root/src/VBox/Main/src-server/SnapshotImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/SnapshotImpl.cpp')
-rw-r--r--src/VBox/Main/src-server/SnapshotImpl.cpp499
1 files changed, 361 insertions, 138 deletions
diff --git a/src/VBox/Main/src-server/SnapshotImpl.cpp b/src/VBox/Main/src-server/SnapshotImpl.cpp
index 4740b122..411cdd56 100644
--- a/src/VBox/Main/src-server/SnapshotImpl.cpp
+++ b/src/VBox/Main/src-server/SnapshotImpl.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2006-2012 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -29,6 +29,7 @@
// to remove them and put that code in shared code in MachineImplcpp
#include "SharedFolderImpl.h"
#include "USBControllerImpl.h"
+#include "USBDeviceFiltersImpl.h"
#include "VirtualBoxImpl.h"
#include "AutoCaller.h"
@@ -41,6 +42,7 @@
#include <VBox/settings.h>
+
////////////////////////////////////////////////////////////////////////////////
//
// Snapshot private data definition
@@ -113,7 +115,7 @@ HRESULT Snapshot::init(VirtualBox *aVirtualBox,
{
LogFlowThisFunc(("uuid=%s aParent->uuid=%s\n", aId.toString().c_str(), (aParent) ? aParent->m->uuid.toString().c_str() : ""));
- ComAssertRet(!aId.isEmpty() && !aName.isEmpty() && aMachine, E_INVALIDARG);
+ ComAssertRet(!aId.isZero() && aId.isValid() && !aName.isEmpty() && aMachine, E_INVALIDARG);
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
@@ -327,7 +329,8 @@ STDMETHODIMP Snapshot::COMSETTER(Name)(IN_BSTR aName)
// prohibit setting a UUID only as the machine name, or else it can
// never be found by findMachine()
Guid test(aName);
- if (test.isNotEmpty())
+
+ if (!test.isZero() && test.isValid())
return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
AutoCaller autoCaller(this);
@@ -493,6 +496,31 @@ const Utf8Str& Snapshot::getStateFilePath() const
}
/**
+ * Returns the depth in the snapshot tree for this snapshot.
+ *
+ * @note takes the snapshot tree lock
+ */
+
+uint32_t Snapshot::getDepth()
+{
+ AutoCaller autoCaller(this);
+ AssertComRC(autoCaller.rc());
+
+ // snapshots tree is protected by machine lock
+ AutoReadLock alock(m->pMachine COMMA_LOCKVAL_SRC_POS);
+
+ uint32_t cDepth = 0;
+ ComObjPtr<Snapshot> pSnap(this);
+ while (!pSnap.isNull())
+ {
+ pSnap = pSnap->m->pParent;
+ cDepth++;
+ }
+
+ return cDepth;
+}
+
+/**
* Returns the number of direct child snapshots, without grandchildren.
* Does not recurse.
* @return
@@ -788,11 +816,19 @@ HRESULT Snapshot::saveSnapshotImpl(settings::Snapshot &data, bool aAttrsOnly)
it != m->llChildren.end();
++it)
{
- settings::Snapshot snap;
- rc = (*it)->saveSnapshotImpl(snap, aAttrsOnly);
- if (FAILED(rc)) return rc;
+ // Use the heap to reduce the stack footprint. Each recursion needs
+ // over 1K, and there can be VMs with deeply nested snapshots. The
+ // stack can be quite small, especially with XPCOM.
- data.llChildSnapshots.push_back(snap);
+ settings::Snapshot *snap = new settings::Snapshot();
+ rc = (*it)->saveSnapshotImpl(*snap, aAttrsOnly);
+ if (FAILED(rc))
+ {
+ delete snap;
+ return rc;
+ }
+ data.llChildSnapshots.push_back(*snap);
+ delete snap;
}
}
@@ -954,7 +990,8 @@ HRESULT SnapshotMachine::init(SessionMachine *aSessionMachine,
LogFlowThisFuncEnter();
LogFlowThisFunc(("mName={%s}\n", aSessionMachine->mUserData->s.strName.c_str()));
- AssertReturn(aSessionMachine && !Guid(aSnapshotId).isEmpty(), E_INVALIDARG);
+ Guid l_guid(aSnapshotId);
+ AssertReturn(aSessionMachine && (!l_guid.isZero() && l_guid.isValid()), E_INVALIDARG);
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
@@ -1040,8 +1077,22 @@ HRESULT SnapshotMachine::init(SessionMachine *aSessionMachine,
unconst(mAudioAdapter).createObject();
mAudioAdapter->initCopy(this, pMachine->mAudioAdapter);
- unconst(mUSBController).createObject();
- mUSBController->initCopy(this, pMachine->mUSBController);
+ /* create copies of all USB controllers (mUSBControllerData
+ * after attaching a copy contains just references to original objects) */
+ mUSBControllers.allocate();
+ for (USBControllerList::const_iterator
+ it = aSessionMachine->mUSBControllers->begin();
+ it != aSessionMachine->mUSBControllers->end();
+ ++it)
+ {
+ ComObjPtr<USBController> ctrl;
+ ctrl.createObject();
+ ctrl->initCopy(this, *it);
+ mUSBControllers->push_back(ctrl);
+ }
+
+ unconst(mUSBDeviceFilters).createObject();
+ mUSBDeviceFilters->initCopy(this, pMachine->mUSBDeviceFilters);
mNetworkAdapters.resize(pMachine->mNetworkAdapters.size());
for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
@@ -1095,7 +1146,8 @@ HRESULT SnapshotMachine::initFromSettings(Machine *aMachine,
LogFlowThisFuncEnter();
LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
- AssertReturn(aMachine && !Guid(aSnapshotId).isEmpty(), E_INVALIDARG);
+ Guid l_guid(aSnapshotId);
+ AssertReturn(aMachine && (!l_guid.isZero() && l_guid.isValid()), E_INVALIDARG);
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
@@ -1122,6 +1174,7 @@ HRESULT SnapshotMachine::initFromSettings(Machine *aMachine,
mHWData.allocate();
mMediaData.allocate();
mStorageControllers.allocate();
+ mUSBControllers.allocate();
/* SSData is always unique for SnapshotMachine */
mSSData.allocate();
@@ -1138,8 +1191,8 @@ HRESULT SnapshotMachine::initFromSettings(Machine *aMachine,
unconst(mAudioAdapter).createObject();
mAudioAdapter->init(this);
- unconst(mUSBController).createObject();
- mUSBController->init(this);
+ unconst(mUSBDeviceFilters).createObject();
+ mUSBDeviceFilters->init(this);
mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
@@ -1381,7 +1434,7 @@ struct SessionMachine::DeleteSnapshotTask
*
* When the console is done, it calls SessionMachine::EndTakingSnapshot().
*
- * @note Locks mParent + this object for writing.
+ * @note Locks mParent + this object for writing.
*
* @param aInitiator in: The console on which Console::TakeSnapshot was called.
* @param aName in: The name for the new snapshot.
@@ -1416,6 +1469,14 @@ STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator,
AssertReturn(mConsoleTaskData.mLastState == MachineState_Null, E_FAIL);
AssertReturn(mConsoleTaskData.mSnapshot.isNull(), E_FAIL);
+ if ( mData->mCurrentSnapshot
+ && mData->mCurrentSnapshot->getDepth() >= SETTINGS_SNAPSHOT_DEPTH_MAX)
+ {
+ return setError(VBOX_E_INVALID_OBJECT_STATE,
+ tr("Cannot take another snapshot for machine '%s', because it exceeds the maximum snapshot depth limit. Please delete some earlier snapshot which you no longer need"),
+ mUserData->s.strName.c_str());
+ }
+
if ( !fTakingSnapshotOnline
&& mData->mMachineState != MachineState_Saved
)
@@ -1435,8 +1496,16 @@ STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator,
Utf8Str strStateFilePath;
/* stateFilePath is null when the machine is not online nor saved */
if (fTakingSnapshotOnline)
- // creating a new online snapshot: then we need a fresh saved state file
- composeSavedStateFilename(strStateFilePath);
+ {
+ Bstr value;
+ HRESULT rc = GetExtraData(Bstr("VBoxInternal2/ForceTakeSnapshotWithoutState").raw(),
+ value.asOutParam());
+ if (FAILED(rc) || value != "1")
+ {
+ // creating a new online snapshot: we need a fresh saved state file
+ composeSavedStateFilename(strStateFilePath);
+ }
+ }
else if (mData->mMachineState == MachineState_Saved)
// taking an online snapshot from machine in "saved" state: then use existing state file
strStateFilePath = mSSData->strStateFilePath;
@@ -1499,9 +1568,9 @@ STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator,
if (FAILED(rc))
throw rc;
- // if we got this far without an error, then save the media registries
- // that got modified for the diff images
- mParent->saveModifiedRegistries();
+ // MUST NOT save the settings or the media registry here, because
+ // this causes trouble with rolling back settings if the user cancels
+ // taking the snapshot after the diff images have been created.
}
catch (HRESULT hrc)
{
@@ -1608,11 +1677,14 @@ STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess)
/* inform callbacks */
mParent->onSnapshotTaken(mData->mUuid,
mConsoleTaskData.mSnapshot->getId());
+ machineLock.release();
}
else
{
/* delete all differencing hard disks created (this will also attach
* their parents back by rolling back mMediaData) */
+ machineLock.release();
+
rollbackMedia();
mData->mFirstSnapshot = pOldFirstSnap; // might have been changed above
@@ -1624,15 +1696,19 @@ STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess)
// snapshot means that a new saved state file was created, which we must
// clean up now
RTFileDelete(mConsoleTaskData.mSnapshot->getStateFilePath().c_str());
+ machineLock.acquire();
+
mConsoleTaskData.mSnapshot->uninit();
+ machineLock.release();
+
}
/* clear out the snapshot data */
mConsoleTaskData.mLastState = MachineState_Null;
mConsoleTaskData.mSnapshot.setNull();
- machineLock.release();
+ /* machineLock has been released already */
mParent->saveModifiedRegistries();
@@ -2027,7 +2103,7 @@ void SessionMachine::restoreSnapshotHandler(RestoreSnapshotTask &aTask)
////////////////////////////////////////////////////////////////////////////////
/**
- * Implementation for IInternalMachineControl::deleteSnapshot().
+ * Implementation for IInternalMachineControl::DeleteSnapshot().
*
* Gets called from Console::DeleteSnapshot(), and that's basically the
* only thing Console does initially. Deleting a snapshot happens entirely on
@@ -2053,7 +2129,9 @@ STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator,
Guid startId(aStartId);
Guid endId(aEndId);
- AssertReturn(aInitiator && !startId.isEmpty() && !endId.isEmpty(), E_INVALIDARG);
+
+ AssertReturn(aInitiator && !startId.isZero() && !endId.isZero() && startId.isValid() && endId.isValid(), E_INVALIDARG);
+
AssertReturn(aMachineState && aProgress, E_POINTER);
/** @todo implement the "and all children" and "range" variants */
@@ -2086,10 +2164,15 @@ STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator,
size_t childrenCount = pSnapshot->getChildrenCount();
if (childrenCount > 1)
return setError(VBOX_E_INVALID_OBJECT_STATE,
- tr("Snapshot '%s' of the machine '%s' cannot be deleted. because it has %d child snapshots, which is more than the one snapshot allowed for deletion"),
+ tr("Snapshot '%s' of the machine '%s' cannot be deleted, because it has %d child snapshots, which is more than the one snapshot allowed for deletion"),
pSnapshot->getName().c_str(),
mUserData->s.strName.c_str(),
childrenCount);
+ if (pSnapshot == mData->mCurrentSnapshot && childrenCount >= 1)
+ return setError(VBOX_E_INVALID_OBJECT_STATE,
+ tr("Snapshot '%s' of the machine '%s' cannot be deleted, because it is the current snapshot and has one child snapshot"),
+ pSnapshot->getName().c_str(),
+ mUserData->s.strName.c_str());
/* If the snapshot being deleted is the current one, ensure current
* settings are committed and saved.
@@ -2215,18 +2298,20 @@ struct MediumDeleteRec
const ComObjPtr<MediumAttachment> &aOnlineMediumAttachment,
bool fMergeForward,
const ComObjPtr<Medium> &aParentForTarget,
- const MediaList &aChildrenToReparent,
+ MediumLockList *aChildrenToReparent,
bool fNeedsOnlineMerge,
- MediumLockList *aMediumLockList)
+ MediumLockList *aMediumLockList,
+ const ComPtr<IToken> &aHDLockToken)
: mpHD(aHd),
mpSource(aSource),
mpTarget(aTarget),
mpOnlineMediumAttachment(aOnlineMediumAttachment),
mfMergeForward(fMergeForward),
mpParentForTarget(aParentForTarget),
- mChildrenToReparent(aChildrenToReparent),
+ mpChildrenToReparent(aChildrenToReparent),
mfNeedsOnlineMerge(fNeedsOnlineMerge),
- mpMediumLockList(aMediumLockList)
+ mpMediumLockList(aMediumLockList),
+ mpHDLockToken(aHDLockToken)
{}
MediumDeleteRec(const ComObjPtr<Medium> &aHd,
@@ -2235,9 +2320,10 @@ struct MediumDeleteRec
const ComObjPtr<MediumAttachment> &aOnlineMediumAttachment,
bool fMergeForward,
const ComObjPtr<Medium> &aParentForTarget,
- const MediaList &aChildrenToReparent,
+ MediumLockList *aChildrenToReparent,
bool fNeedsOnlineMerge,
MediumLockList *aMediumLockList,
+ const ComPtr<IToken> &aHDLockToken,
const Guid &aMachineId,
const Guid &aSnapshotId)
: mpHD(aHd),
@@ -2246,9 +2332,10 @@ struct MediumDeleteRec
mpOnlineMediumAttachment(aOnlineMediumAttachment),
mfMergeForward(fMergeForward),
mpParentForTarget(aParentForTarget),
- mChildrenToReparent(aChildrenToReparent),
+ mpChildrenToReparent(aChildrenToReparent),
mfNeedsOnlineMerge(fNeedsOnlineMerge),
mpMediumLockList(aMediumLockList),
+ mpHDLockToken(aHDLockToken),
mMachineId(aMachineId),
mSnapshotId(aSnapshotId)
{}
@@ -2259,9 +2346,11 @@ struct MediumDeleteRec
ComObjPtr<MediumAttachment> mpOnlineMediumAttachment;
bool mfMergeForward;
ComObjPtr<Medium> mpParentForTarget;
- MediaList mChildrenToReparent;
+ MediumLockList *mpChildrenToReparent;
bool mfNeedsOnlineMerge;
MediumLockList *mpMediumLockList;
+ /** optional lock token, used only in case mpHD is not merged/deleted */
+ ComPtr<IToken> mpHDLockToken;
/* these are for reattaching the hard disk in case of a failure: */
Guid mMachineId;
Guid mSnapshotId;
@@ -2371,11 +2460,12 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
ComObjPtr<Medium> pSource;
bool fMergeForward = false;
ComObjPtr<Medium> pParentForTarget;
- MediaList childrenToReparent;
+ MediumLockList *pChildrenToReparent = NULL;
bool fNeedsOnlineMerge = false;
bool fOnlineMergePossible = aTask.m_fDeleteOnline;
MediumLockList *pMediumLockList = NULL;
MediumLockList *pVMMALockList = NULL;
+ ComPtr<IToken> pHDLockToken;
ComObjPtr<MediumAttachment> pOnlineMediumAttachment;
if (fOnlineMergePossible)
{
@@ -2408,9 +2498,10 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
fOnlineMergePossible,
pVMMALockList, pSource, pTarget,
fMergeForward, pParentForTarget,
- childrenToReparent,
+ pChildrenToReparent,
fNeedsOnlineMerge,
- pMediumLockList);
+ pMediumLockList,
+ pHDLockToken);
treeLock.acquire();
if (FAILED(rc))
throw rc;
@@ -2433,7 +2524,6 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
// then do a backward merge, i.e. merge its only child onto the
// base disk. Here we need then to update the attachment that
// refers to the child and have it point to the parent instead
- Assert(pHD->getParent().isNull());
Assert(pHD->getChildren().size() == 1);
ComObjPtr<Medium> pReplaceHD = pHD->getChildren().front();
@@ -2454,7 +2544,7 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
if (pSnapshotId)
replaceSnapshotId = *pSnapshotId;
- if (!replaceMachineId.isEmpty())
+ if (replaceMachineId.isValid() && !replaceMachineId.isZero())
{
// Adjust the backreferences, otherwise merging will assert.
// Note that the medium attachment object stays associated
@@ -2467,9 +2557,10 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
pOnlineMediumAttachment,
fMergeForward,
pParentForTarget,
- childrenToReparent,
+ pChildrenToReparent,
fNeedsOnlineMerge,
pMediumLockList,
+ pHDLockToken,
replaceMachineId,
replaceSnapshotId));
}
@@ -2478,9 +2569,116 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
pOnlineMediumAttachment,
fMergeForward,
pParentForTarget,
- childrenToReparent,
+ pChildrenToReparent,
fNeedsOnlineMerge,
- pMediumLockList));
+ pMediumLockList,
+ pHDLockToken));
+ }
+
+ {
+ /*check available place on the storage*/
+ RTFOFF pcbTotal = 0;
+ RTFOFF pcbFree = 0;
+ uint32_t pcbBlock = 0;
+ uint32_t pcbSector = 0;
+ std::multimap<uint32_t,uint64_t> neededStorageFreeSpace;
+ std::map<uint32_t,const char*> serialMapToStoragePath;
+
+ MediumDeleteRecList::const_iterator it_md = toDelete.begin();
+
+ while (it_md != toDelete.end())
+ {
+ uint64_t diskSize = 0;
+ uint32_t pu32Serial = 0;
+ ComObjPtr<Medium> pSource_local = it_md->mpSource;
+ ComObjPtr<Medium> pTarget_local = it_md->mpTarget;
+ ComPtr<IMediumFormat> pTargetFormat;
+
+ {
+ if ( pSource_local.isNull()
+ || pSource_local == pTarget_local)
+ {
+ ++it_md;
+ continue;
+ }
+ }
+
+ rc = pTarget_local->COMGETTER(MediumFormat)(pTargetFormat.asOutParam());
+ if (FAILED(rc))
+ throw rc;
+
+ if(pTarget_local->isMediumFormatFile())
+ {
+ int vrc = RTFsQuerySerial(pTarget_local->getLocationFull().c_str(), &pu32Serial);
+ if (RT_FAILURE(vrc))
+ {
+ rc = setError(E_FAIL,
+ tr(" Unable to merge storage '%s'. Can't get storage UID "),
+ pTarget_local->getLocationFull().c_str());
+ throw rc;
+ }
+
+ pSource_local->COMGETTER(Size)((LONG64*)&diskSize);
+
+ /* store needed free space in multimap */
+ neededStorageFreeSpace.insert(std::make_pair(pu32Serial,diskSize));
+ /* linking storage UID with snapshot path, it is a helper container (just for easy finding needed path) */
+ serialMapToStoragePath.insert(std::make_pair(pu32Serial,pTarget_local->getLocationFull().c_str()));
+ }
+
+ ++it_md;
+ }
+
+ while (!neededStorageFreeSpace.empty())
+ {
+ std::pair<std::multimap<uint32_t,uint64_t>::iterator,std::multimap<uint32_t,uint64_t>::iterator> ret;
+ uint64_t commonSourceStoragesSize = 0;
+
+ /* find all records in multimap with identical storage UID*/
+ ret = neededStorageFreeSpace.equal_range(neededStorageFreeSpace.begin()->first);
+ std::multimap<uint32_t,uint64_t>::const_iterator it_ns = ret.first;
+
+ for (; it_ns != ret.second ; ++it_ns)
+ {
+ commonSourceStoragesSize += it_ns->second;
+ }
+
+ /* find appropriate path by storage UID*/
+ std::map<uint32_t,const char*>::const_iterator it_sm = serialMapToStoragePath.find(ret.first->first);
+ /* get info about a storage */
+ if (it_sm == serialMapToStoragePath.end())
+ {
+ LogFlowThisFunc((" Path to the storage wasn't found...\n "));
+
+ rc = setError(E_INVALIDARG,
+ tr(" Unable to merge storage '%s'. Path to the storage wasn't found. "),
+ it_sm->second);
+ throw rc;
+ }
+
+ int vrc = RTFsQuerySizes(it_sm->second, &pcbTotal, &pcbFree,&pcbBlock, &pcbSector);
+ if (RT_FAILURE(vrc))
+ {
+ rc = setError(E_FAIL,
+ tr(" Unable to merge storage '%s'. Can't get the storage size. "),
+ it_sm->second);
+ throw rc;
+ }
+
+ if (commonSourceStoragesSize > (uint64_t)pcbFree)
+ {
+ LogFlowThisFunc((" Not enough free space to merge...\n "));
+
+ rc = setError(E_OUTOFMEMORY,
+ tr(" Unable to merge storage '%s' - not enough free storage space. "),
+ it_sm->second);
+ throw rc;
+ }
+
+ neededStorageFreeSpace.erase(ret.first, ret.second);
+ }
+
+ serialMapToStoragePath.clear();
}
// we can release the locks now since the machine state is MachineState_DeletingSnapshot
@@ -2572,16 +2770,22 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
bool fNeedsSave = false;
if (it->mfNeedsOnlineMerge)
{
+ // Put the medium merge information (MediumDeleteRec) where
+ // SessionMachine::FinishOnlineMergeMedium can get at it.
+ // This callback will arrive while onlineMergeMedium is
+ // still executing, and there can't be two tasks.
+ mConsoleTaskData.mDeleteSnapshotInfo = (void *)&(*it);
// online medium merge, in the direction decided earlier
rc = onlineMergeMedium(it->mpOnlineMediumAttachment,
it->mpSource,
it->mpTarget,
it->mfMergeForward,
it->mpParentForTarget,
- it->mChildrenToReparent,
+ it->mpChildrenToReparent,
it->mpMediumLockList,
aTask.pProgress,
&fNeedsSave);
+ mConsoleTaskData.mDeleteSnapshotInfo = NULL;
}
else
{
@@ -2589,7 +2793,7 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
rc = it->mpSource->mergeTo(it->mpTarget,
it->mfMergeForward,
it->mpParentForTarget,
- it->mChildrenToReparent,
+ it->mpChildrenToReparent,
it->mpMediumLockList,
&aTask.pProgress,
true /* aWait */);
@@ -2708,7 +2912,9 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
mParent->markRegistryModified(getId());
}
}
- catch (HRESULT aRC) { rc = aRC; }
+ catch (HRESULT aRC) {
+ rc = aRC;
+ }
if (FAILED(rc))
{
@@ -2726,10 +2932,10 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
++it)
{
cancelDeleteSnapshotMedium(it->mpHD, it->mpSource,
- it->mChildrenToReparent,
+ it->mpChildrenToReparent,
it->mfNeedsOnlineMerge,
- it->mpMediumLockList, it->mMachineId,
- it->mSnapshotId);
+ it->mpMediumLockList, it->mpHDLockToken,
+ it->mMachineId, it->mSnapshotId);
}
}
@@ -2780,14 +2986,16 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
* @param aTarget Target hard disk for merge (out).
* @param aMergeForward Merge direction decision (out).
* @param aParentForTarget New parent if target needs to be reparented (out).
- * @param aChildrenToReparent Children which have to be reparented to the
- * target (out).
+ * @param aChildrenToReparent MediumLockList with children which have to be
+ * reparented to the target (out).
* @param fNeedsOnlineMerge Whether this merge needs to be done online (out).
* If this is set to @a true then the @a aVMMALockList
* parameter has been modified and is returned as
* @a aMediumLockList.
* @param aMediumLockList Where to store the created medium lock list (may
* return NULL if no real merge is necessary).
+ * @param aHDLockToken Where to store the write lock token for aHD, in case
+ * it is not merged or deleted (out).
*
* @note Caller must hold media tree lock for writing. This locks this object
* and every medium object on the merge chain for writing.
@@ -2801,9 +3009,10 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD
ComObjPtr<Medium> &aTarget,
bool &aMergeForward,
ComObjPtr<Medium> &aParentForTarget,
- MediaList &aChildrenToReparent,
+ MediumLockList * &aChildrenToReparent,
bool &fNeedsOnlineMerge,
- MediumLockList * &aMediumLockList)
+ MediumLockList * &aMediumLockList,
+ ComPtr<IToken> &aHDLockToken)
{
Assert(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
Assert(!fOnlineMergePossible || VALID_PTR(aVMMALockList));
@@ -2816,6 +3025,7 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD
&& type != MediumType_Shareable
&& type != MediumType_Readonly, E_FAIL);
+ aChildrenToReparent = NULL;
aMediumLockList = NULL;
fNeedsOnlineMerge = false;
@@ -2832,7 +3042,7 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD
/* lock only, to prevent any usage until the snapshot deletion
* is completed */
alock.release();
- return aHD->LockWrite(NULL);
+ return aHD->LockWrite(aHDLockToken.asOutParam());
}
/* the differencing hard disk w/o children will be deleted, protect it
@@ -2863,7 +3073,7 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD
* deletion, so lock only, to prevent any usage */
childLock.release();
alock.release();
- return aHD->LockWrite(NULL);
+ return aHD->LockWrite(aHDLockToken.asOutParam());
}
aSource = pChild;
@@ -2871,9 +3081,30 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD
}
else
{
- /* forward merge */
- aSource = aHD;
- aTarget = pChild;
+ /* Determine best merge direction. */
+ bool fMergeForward = true;
+
+ childLock.release();
+ alock.release();
+ HRESULT rc = aHD->queryPreferredMergeDirection(pChild, fMergeForward);
+ alock.acquire();
+ childLock.acquire();
+
+ if (FAILED(rc) && rc != E_FAIL)
+ return rc;
+
+ if (fMergeForward)
+ {
+ aSource = aHD;
+ aTarget = pChild;
+ LogFlowFunc(("Forward merging selected\n"));
+ }
+ else
+ {
+ aSource = pChild;
+ aTarget = aHD;
+ LogFlowFunc(("Backward merging selected\n"));
+ }
}
HRESULT rc;
@@ -2939,39 +3170,37 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD
if (fOnlineMergePossible)
{
/* we will lock the children of the source for reparenting */
- for (MediaList::const_iterator it = aChildrenToReparent.begin();
- it != aChildrenToReparent.end();
- ++it)
+ if (aChildrenToReparent && !aChildrenToReparent->IsEmpty())
{
- ComObjPtr<Medium> pMedium = *it;
- AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
- if (pMedium->getState() == MediumState_Created)
- {
- mediumLock.release();
- childLock.release();
- alock.release();
- rc = pMedium->LockWrite(NULL);
- alock.acquire();
- childLock.acquire();
- mediumLock.acquire();
- if (FAILED(rc))
- throw rc;
- }
- else
+ /* Cannot just call aChildrenToReparent->Lock(), as one of
+ * the children is the one under which the current state of
+ * the VM is located, and this means it is already locked
+ * (for reading). Note that no special unlocking is needed,
+ * because cancelMergeTo will unlock everything locked in
+ * its context (using the unlock on destruction), and both
+ * cancelDeleteSnapshotMedium (in case something fails) and
+ * FinishOnlineMergeMedium re-define the read/write lock
+ * state of everything which the VM need, search for the
+ * UpdateLock method calls. */
+ childLock.release();
+ alock.release();
+ rc = aChildrenToReparent->Lock(true /* fSkipOverLockedMedia */);
+ alock.acquire();
+ childLock.acquire();
+ MediumLockList::Base::iterator childrenToReparentBegin = aChildrenToReparent->GetBegin();
+ MediumLockList::Base::iterator childrenToReparentEnd = aChildrenToReparent->GetEnd();
+ for (MediumLockList::Base::iterator it = childrenToReparentBegin;
+ it != childrenToReparentEnd;
+ ++it)
{
- mediumLock.release();
- childLock.release();
- alock.release();
- rc = aVMMALockList->Update(pMedium, true);
- alock.acquire();
- childLock.acquire();
- mediumLock.acquire();
- if (FAILED(rc))
+ ComObjPtr<Medium> pMedium = it->GetMedium();
+ AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
+ if (!it->IsLocked())
{
mediumLock.release();
childLock.release();
alock.release();
- rc = pMedium->LockWrite(NULL);
+ rc = aVMMALockList->Update(pMedium, true);
alock.acquire();
childLock.acquire();
mediumLock.acquire();
@@ -3058,6 +3287,7 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD
* @param aChildrenToReparent Children to unlock.
* @param fNeedsOnlineMerge Whether this merge needs to be done online.
* @param aMediumLockList Medium locks to cancel.
+ * @param aHDLockToken Optional write lock token for aHD.
* @param aMachineId Machine id to attach the medium to.
* @param aSnapshotId Snapshot id to attach the medium to.
*
@@ -3065,9 +3295,10 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD
*/
void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,
const ComObjPtr<Medium> &aSource,
- const MediaList &aChildrenToReparent,
+ MediumLockList *aChildrenToReparent,
bool fNeedsOnlineMerge,
MediumLockList *aMediumLockList,
+ const ComPtr<IToken> &aHDLockToken,
const Guid &aMachineId,
const Guid &aSnapshotId)
{
@@ -3079,8 +3310,12 @@ void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,
if (aHD->getParent().isNull())
{
- HRESULT rc = aHD->UnlockWrite(NULL);
- AssertComRC(rc);
+ Assert(!aHDLockToken.isNull());
+ if (!aHDLockToken.isNull())
+ {
+ HRESULT rc = aHDLockToken->Abandon();
+ AssertComRC(rc);
+ }
}
else
{
@@ -3128,7 +3363,7 @@ void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,
}
}
- if (!aMachineId.isEmpty())
+ if (aMachineId.isValid() && !aMachineId.isZero())
{
// reattach the source media to the snapshot
HRESULT rc = aSource->addBackReference(aMachineId, aSnapshotId);
@@ -3147,8 +3382,8 @@ void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,
* @param aTarget Target hard disk for merge.
* @param aMergeForward Merge direction.
* @param aParentForTarget New parent if target needs to be reparented.
- * @param aChildrenToReparent Children which have to be reparented to the
- * target.
+ * @param aChildrenToReparent Medium lock list with children which have to be
+ * reparented to the target.
* @param aMediumLockList Where to store the created medium lock list (may
* return NULL if no real merge is necessary).
* @param aProgress Progress indicator.
@@ -3159,7 +3394,7 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe
const ComObjPtr<Medium> &aTarget,
bool fMergeForward,
const ComObjPtr<Medium> &aParentForTarget,
- const MediaList &aChildrenToReparent,
+ MediumLockList *aChildrenToReparent,
MediumLockList *aMediumLockList,
ComObjPtr<Progress> &aProgress,
bool *pfNeedsMachineSaveSettings)
@@ -3168,6 +3403,9 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe
AssertReturn(aTarget != NULL, E_FAIL);
AssertReturn(aSource != aTarget, E_FAIL);
AssertReturn(aMediumLockList != NULL, E_FAIL);
+ NOREF(fMergeForward);
+ NOREF(aParentForTarget);
+ NOREF(aChildrenToReparent);
HRESULT rc = S_OK;
@@ -3208,12 +3446,6 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe
ComAssertThrow( uSourceIdx != (unsigned)-1
&& uTargetIdx != (unsigned)-1, E_FAIL);
- // For forward merges, tell the VM what images need to have their
- // parent UUID updated. This cannot be done in VBoxSVC, as opening
- // the required parent images is not safe while the VM is running.
- // For backward merges this will be simply an array of size 0.
- com::SafeIfaceArray<IMedium> childrenToReparent(aChildrenToReparent);
-
ComPtr<IInternalSessionControl> directControl;
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
@@ -3229,9 +3461,6 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe
// updating the medium attachment, chain linking and state.
rc = directControl->OnlineMergeMedium(aMediumAttachment,
uSourceIdx, uTargetIdx,
- aSource, aTarget,
- fMergeForward, aParentForTarget,
- ComSafeArrayAsInParam(childrenToReparent),
aProgress);
if (FAILED(rc))
throw rc;
@@ -3247,7 +3476,7 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe
}
/**
- * Implementation for IInternalMachineControl::finishOnlineMergeMedium().
+ * Implementation for IInternalMachineControl::FinishOnlineMergeMedium().
*
* Gets called after the successful completion of an online merge from
* Console::onlineMergeMedium(), which gets invoked indirectly above in
@@ -3256,17 +3485,11 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe
* This updates the medium information and medium state so that the VM
* can continue with the updated state of the medium chain.
*/
-STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumAttachment,
- IMedium *aSource,
- IMedium *aTarget,
- BOOL aMergeForward,
- IMedium *aParentForTarget,
- ComSafeArrayIn(IMedium *, aChildrenToReparent))
+STDMETHODIMP SessionMachine::FinishOnlineMergeMedium()
{
HRESULT rc = S_OK;
- ComObjPtr<Medium> pSource(static_cast<Medium *>(aSource));
- ComObjPtr<Medium> pTarget(static_cast<Medium *>(aTarget));
- ComObjPtr<Medium> pParentForTarget(static_cast<Medium *>(aParentForTarget));
+ MediumDeleteRec *pDeleteRec = (MediumDeleteRec *)mConsoleTaskData.mDeleteSnapshotInfo;
+ AssertReturn(pDeleteRec, E_FAIL);
bool fSourceHasChildren = false;
// all hard disks but the target were successfully deleted by
@@ -3279,36 +3502,35 @@ STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumA
// we delete the last reference to the no longer existing medium object.
ComObjPtr<Medium> targetChild;
- if (aMergeForward)
+ if (pDeleteRec->mfMergeForward)
{
// first, unregister the target since it may become a base
// hard disk which needs re-registration
- rc = mParent->unregisterMedium(pTarget);
+ rc = mParent->unregisterMedium(pDeleteRec->mpTarget);
AssertComRC(rc);
// then, reparent it and disconnect the deleted branch at
// both ends (chain->parent() is source's parent)
- pTarget->deparent();
- pTarget->setParent(pParentForTarget);
- if (pParentForTarget)
- pSource->deparent();
+ pDeleteRec->mpTarget->deparent();
+ pDeleteRec->mpTarget->setParent(pDeleteRec->mpParentForTarget);
+ if (pDeleteRec->mpParentForTarget)
+ pDeleteRec->mpSource->deparent();
// then, register again
- rc = mParent->registerMedium(pTarget, &pTarget, DeviceType_HardDisk);
+ rc = mParent->registerMedium(pDeleteRec->mpTarget, &pDeleteRec->mpTarget, DeviceType_HardDisk);
AssertComRC(rc);
}
else
{
- Assert(pTarget->getChildren().size() == 1);
- targetChild = pTarget->getChildren().front();
+ Assert(pDeleteRec->mpTarget->getChildren().size() == 1);
+ targetChild = pDeleteRec->mpTarget->getChildren().front();
// disconnect the deleted branch at the elder end
targetChild->deparent();
// Update parent UUIDs of the source's children, reparent them and
// disconnect the deleted branch at the younger end
- com::SafeIfaceArray<IMedium> childrenToReparent(ComSafeArrayInArg(aChildrenToReparent));
- if (childrenToReparent.size() > 0)
+ if (pDeleteRec->mpChildrenToReparent && !pDeleteRec->mpChildrenToReparent->IsEmpty())
{
fSourceHasChildren = true;
// Fix the parent UUID of the images which needs to be moved to
@@ -3316,35 +3538,35 @@ STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumA
// but only for reading since the VM is paused. If anything fails
// we must continue. The worst possible result is that the images
// need manual fixing via VBoxManage to adjust the parent UUID.
- MediaList toReparent;
- for (size_t i = 0; i < childrenToReparent.size(); i++)
- {
- Medium *pMedium = static_cast<Medium *>(childrenToReparent[i]);
- toReparent.push_back(pMedium);
- }
treeLock.release();
- pTarget->fixParentUuidOfChildren(toReparent);
+ pDeleteRec->mpTarget->fixParentUuidOfChildren(pDeleteRec->mpChildrenToReparent);
+ // The childen are still write locked, unlock them now and don't
+ // rely on the destructor doing it very late.
+ pDeleteRec->mpChildrenToReparent->Unlock();
treeLock.acquire();
// obey {parent,child} lock order
- AutoWriteLock sourceLock(pSource COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock sourceLock(pDeleteRec->mpSource COMMA_LOCKVAL_SRC_POS);
- for (size_t i = 0; i < childrenToReparent.size(); i++)
+ MediumLockList::Base::iterator childrenBegin = pDeleteRec->mpChildrenToReparent->GetBegin();
+ MediumLockList::Base::iterator childrenEnd = pDeleteRec->mpChildrenToReparent->GetEnd();
+ for (MediumLockList::Base::iterator it = childrenBegin;
+ it != childrenEnd;
+ ++it)
{
- Medium *pMedium = static_cast<Medium *>(childrenToReparent[i]);
+ Medium *pMedium = it->GetMedium();
AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
pMedium->deparent(); // removes pMedium from source
- pMedium->setParent(pTarget);
+ pMedium->setParent(pDeleteRec->mpTarget);
}
}
}
/* unregister and uninitialize all hard disks removed by the merge */
MediumLockList *pMediumLockList = NULL;
- MediumAttachment *pMediumAttachment = static_cast<MediumAttachment *>(aMediumAttachment);
- rc = mData->mSession.mLockedMedia.Get(pMediumAttachment, pMediumLockList);
- const ComObjPtr<Medium> &pLast = aMergeForward ? pTarget : pSource;
+ rc = mData->mSession.mLockedMedia.Get(pDeleteRec->mpOnlineMediumAttachment, pMediumLockList);
+ const ComObjPtr<Medium> &pLast = pDeleteRec->mfMergeForward ? pDeleteRec->mpTarget : pDeleteRec->mpSource;
AssertReturn(SUCCEEDED(rc) && pMediumLockList, E_FAIL);
MediumLockList::Base::iterator lockListBegin =
pMediumLockList->GetBegin();
@@ -3360,7 +3582,7 @@ STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumA
const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
/* The target and all images not merged (readonly) are skipped */
- if ( pMedium == pTarget
+ if ( pMedium == pDeleteRec->mpTarget
|| pMedium->getState() == MediumState_LockedRead)
{
++it;
@@ -3382,10 +3604,10 @@ STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumA
* caller may still hold an AutoCaller instance for it
* and therefore we cannot uninit() it (it's therefore
* the caller's responsibility) */
- if (pMedium == aSource)
+ if (pMedium == pDeleteRec->mpSource)
{
- Assert(pSource->getChildren().size() == 0);
- Assert(pSource->getFirstMachineBackrefId() == NULL);
+ Assert(pDeleteRec->mpSource->getChildren().size() == 0);
+ Assert(pDeleteRec->mpSource->getFirstMachineBackrefId() == NULL);
}
/* Delete the medium lock list entry, which also releases the
@@ -3421,11 +3643,12 @@ STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumA
* source has no children) then update the medium associated with the
* attachment, as the previously associated one (source) is now deleted.
* Without the immediate update the VM could not continue running. */
- if (!aMergeForward && !fSourceHasChildren)
+ if (!pDeleteRec->mfMergeForward && !fSourceHasChildren)
{
- AutoWriteLock attLock(pMediumAttachment COMMA_LOCKVAL_SRC_POS);
- pMediumAttachment->updateMedium(pTarget);
+ AutoWriteLock attLock(pDeleteRec->mpOnlineMediumAttachment COMMA_LOCKVAL_SRC_POS);
+ pDeleteRec->mpOnlineMediumAttachment->updateMedium(pDeleteRec->mpTarget);
}
return S_OK;
}
+