summaryrefslogtreecommitdiff
path: root/src/VBox/Main/src-server/MediumImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/MediumImpl.cpp')
-rw-r--r--src/VBox/Main/src-server/MediumImpl.cpp820
1 files changed, 525 insertions, 295 deletions
diff --git a/src/VBox/Main/src-server/MediumImpl.cpp b/src/VBox/Main/src-server/MediumImpl.cpp
index 64edc67f..3e5da5b3 100644
--- a/src/VBox/Main/src-server/MediumImpl.cpp
+++ b/src/VBox/Main/src-server/MediumImpl.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2008-2012 Oracle Corporation
+ * Copyright (C) 2008-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;
@@ -16,6 +16,7 @@
*/
#include "MediumImpl.h"
+#include "TokenImpl.h"
#include "ProgressImpl.h"
#include "SystemPropertiesImpl.h"
#include "VirtualBoxImpl.h"
@@ -68,9 +69,9 @@ struct BackRef
BackRef(const Guid &aMachineId,
const Guid &aSnapshotId = Guid::Empty)
: machineId(aMachineId),
- fInCurState(aSnapshotId.isEmpty())
+ fInCurState(aSnapshotId.isZero())
{
- if (!aSnapshotId.isEmpty())
+ if (aSnapshotId.isValid() && !aSnapshotId.isZero())
llSnapshotIds.push_back(aSnapshotId);
}
@@ -514,7 +515,7 @@ public:
Medium *aTarget,
bool fMergeForward,
Medium *aParentForTarget,
- const MediaList &aChildrenToReparent,
+ MediumLockList *aChildrenToReparent,
Progress *aProgress,
MediumLockList *aMediumLockList,
bool fKeepMediumLockList = false)
@@ -522,55 +523,29 @@ public:
mTarget(aTarget),
mfMergeForward(fMergeForward),
mParentForTarget(aParentForTarget),
- mChildrenToReparent(aChildrenToReparent),
+ mpChildrenToReparent(aChildrenToReparent),
mpMediumLockList(aMediumLockList),
mTargetCaller(aTarget),
mParentForTargetCaller(aParentForTarget),
- mfChildrenCaller(false),
mfKeepMediumLockList(fKeepMediumLockList)
{
AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
- for (MediaList::const_iterator it = mChildrenToReparent.begin();
- it != mChildrenToReparent.end();
- ++it)
- {
- HRESULT rc2 = (*it)->addCaller();
- if (FAILED(rc2))
- {
- mRC = E_FAIL;
- for (MediaList::const_iterator it2 = mChildrenToReparent.begin();
- it2 != it;
- --it2)
- {
- (*it2)->releaseCaller();
- }
- return;
- }
- }
- mfChildrenCaller = true;
}
~MergeTask()
{
if (!mfKeepMediumLockList && mpMediumLockList)
delete mpMediumLockList;
- if (mfChildrenCaller)
- {
- for (MediaList::const_iterator it = mChildrenToReparent.begin();
- it != mChildrenToReparent.end();
- ++it)
- {
- (*it)->releaseCaller();
- }
- }
+ if (mpChildrenToReparent)
+ delete mpChildrenToReparent;
}
const ComObjPtr<Medium> mTarget;
bool mfMergeForward;
- /* When mChildrenToReparent is empty then mParentForTarget is non-null.
- * In other words: they are used in different cases. */
+ /* When mpChildrenToReparent is null then mParentForTarget is non-null and
+ * vice versa. In other words: they are used in different cases. */
const ComObjPtr<Medium> mParentForTarget;
- MediaList mChildrenToReparent;
+ MediumLockList *mpChildrenToReparent;
MediumLockList *mpMediumLockList;
private:
@@ -578,7 +553,6 @@ private:
AutoCaller mTargetCaller;
AutoCaller mParentForTargetCaller;
- bool mfChildrenCaller;
bool mfKeepMediumLockList;
};
@@ -941,7 +915,7 @@ HRESULT Medium::init(VirtualBox *aVirtualBox,
unconst(m->pVirtualBox) = aVirtualBox;
- if (!uuidMachineRegistry.isEmpty())
+ if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
m->llRegistryIDs.push_back(uuidMachineRegistry);
/* no storage yet */
@@ -958,7 +932,7 @@ HRESULT Medium::init(VirtualBox *aVirtualBox,
rc = setLocation(aLocation);
if (FAILED(rc)) return rc;
- if (!(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateFixed
+ if (!(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateFixed
| MediumFormatCapabilities_CreateDynamic))
)
{
@@ -973,8 +947,29 @@ HRESULT Medium::init(VirtualBox *aVirtualBox,
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
ComObjPtr<Medium> pMedium;
+
+ /*
+ * Check whether the UUID is taken already and create a new one
+ * if required.
+ * Try this only a limited amount of times in case the PRNG is broken
+ * in some way to prevent an endless loop.
+ */
+ for (unsigned i = 0; i < 5; i++)
+ {
+ bool fInUse;
+
+ fInUse = m->pVirtualBox->isMediaUuidInUse(m->id, DeviceType_HardDisk);
+ if (fInUse)
+ {
+ // create new UUID
+ unconst(m->id).create();
+ }
+ else
+ break;
+ }
+
rc = m->pVirtualBox->registerMedium(this, &pMedium, DeviceType_HardDisk);
- Assert(this == pMedium);
+ Assert(this == pMedium || FAILED(rc));
}
/* Confirm a successful initialization when it's the case */
@@ -1086,7 +1081,7 @@ HRESULT Medium::init(VirtualBox *aVirtualBox,
}
else
{
- AssertStmt(!m->id.isEmpty(),
+ AssertStmt(!m->id.isZero(),
alock.release(); autoCaller.release(); uninit(); return E_FAIL);
/* storage format must be detected by Medium::queryInfo if the
@@ -1141,7 +1136,7 @@ HRESULT Medium::init(VirtualBox *aVirtualBox,
unconst(m->pVirtualBox) = aVirtualBox;
- if (!uuidMachineRegistry.isEmpty())
+ if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
m->llRegistryIDs.push_back(uuidMachineRegistry);
/* register with VirtualBox/parent early, since uninit() will
@@ -1218,7 +1213,7 @@ HRESULT Medium::init(VirtualBox *aVirtualBox,
}
Utf8Str strFull;
- if (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
+ if (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
{
// compose full path of the medium, if it's not fully qualified...
// slightly convoluted logic here. If the caller has given us a
@@ -1510,20 +1505,29 @@ STDMETHODIMP Medium::COMGETTER(State)(MediumState_T *aState)
return S_OK;
}
-STDMETHODIMP Medium::COMGETTER(Variant)(ULONG *aVariant)
+STDMETHODIMP Medium::COMGETTER(Variant)(ComSafeArrayOut(MediumVariant_T, aVariant))
{
- CheckComArgOutPointerValid(aVariant);
+ CheckComArgOutSafeArrayPointerValid(aVariant);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- *aVariant = m->variant;
+
+ SafeArray<MediumVariant_T> variants(sizeof(MediumVariant_T)*8);
+
+ for (ULONG i = 0; i < variants.size(); ++i)
+ {
+ ULONG temp = m->variant;
+ temp &= 1<<i;
+ variants [i] = (MediumVariant_T)temp;
+ }
+
+ variants.detachTo(ComSafeArrayOutArg(aVariant));
return S_OK;
}
-
STDMETHODIMP Medium::COMGETTER(Location)(BSTR *aLocation)
{
CheckComArgOutPointerValid(aLocation);
@@ -1538,27 +1542,6 @@ STDMETHODIMP Medium::COMGETTER(Location)(BSTR *aLocation)
return S_OK;
}
-STDMETHODIMP Medium::COMSETTER(Location)(IN_BSTR aLocation)
-{
- CheckComArgStrNotEmptyOrNull(aLocation);
-
- AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
- /// @todo NEWMEDIA for file names, add the default extension if no extension
- /// is present (using the information from the VD backend which also implies
- /// that one more parameter should be passed to setLocation() requesting
- /// that functionality since it is only allowed when called from this method
-
- /// @todo NEWMEDIA rename the file and set m->location on success, then save
- /// the global registry (and local registries of portable VMs referring to
- /// this medium), this will also require to add the mRegistered flag to data
-
- ReturnComNotImplemented();
-}
-
STDMETHODIMP Medium::COMGETTER(Name)(BSTR *aName)
{
CheckComArgOutPointerValid(aName);
@@ -1863,30 +1846,14 @@ STDMETHODIMP Medium::COMGETTER(LogicalSize)(LONG64 *aLogicalSize)
{
CheckComArgOutPointerValid(aLogicalSize);
- {
- AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
- /* we access mParent */
- AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
-
- AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
- if (m->pParent.isNull())
- {
- *aLogicalSize = m->logicalSize;
-
- return S_OK;
- }
- }
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
- /* We assume that some backend may decide to return a meaningless value in
- * response to VDGetSize() for differencing media and therefore always
- * ask the base medium ourselves. */
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- /* base() will do callers/locking */
+ *aLogicalSize = m->logicalSize;
- return getBase()->COMGETTER(LogicalSize)(aLogicalSize);
+ return S_OK;
}
STDMETHODIMP Medium::COMGETTER(AutoReset)(BOOL *aAutoReset)
@@ -1999,8 +1966,8 @@ STDMETHODIMP Medium::SetIds(BOOL aSetImageId,
else
{
imageId = Guid(aImageId);
- if (imageId.isEmpty())
- return setError(E_INVALIDARG, tr("Argument %s is empty"), "aImageId");
+ if (!imageId.isValid())
+ return setError(E_INVALIDARG, tr("Argument %s is invalid"), "aImageId");
}
}
if (aSetParentId)
@@ -2060,7 +2027,7 @@ STDMETHODIMP Medium::RefreshState(MediumState_T *aState)
STDMETHODIMP Medium::GetSnapshotIds(IN_BSTR aMachineId,
ComSafeArrayOut(BSTR, aSnapshotIds))
{
- CheckComArgExpr(aMachineId, Guid(aMachineId).isEmpty() == false);
+ CheckComArgExpr(aMachineId, Guid(aMachineId).isValid());
CheckComArgOutSafeArrayPointerValid(aSnapshotIds);
AutoCaller autoCaller(this);
@@ -2108,12 +2075,10 @@ STDMETHODIMP Medium::GetSnapshotIds(IN_BSTR aMachineId,
return S_OK;
}
-/**
- * @note @a aState may be NULL if the state value is not needed (only for
- * in-process calls).
- */
-STDMETHODIMP Medium::LockRead(MediumState_T *aState)
+STDMETHODIMP Medium::LockRead(IToken **aToken)
{
+ CheckComArgNotNull(aToken);
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
@@ -2137,10 +2102,6 @@ STDMETHODIMP Medium::LockRead(MediumState_T *aState)
}
}
- /* return the current state before */
- if (aState)
- *aState = m->state;
-
HRESULT rc = S_OK;
switch (m->state)
@@ -2160,6 +2121,19 @@ STDMETHODIMP Medium::LockRead(MediumState_T *aState)
LogFlowThisFunc(("Okay - prev state=%d readers=%d\n", m->state, m->readers));
m->state = MediumState_LockedRead;
+ ComObjPtr<MediumLockToken> pToken;
+ rc = pToken.createObject();
+ if (SUCCEEDED(rc))
+ rc = pToken->init(this, false /* fWrite */);
+ if (FAILED(rc))
+ {
+ --m->readers;
+ if (m->readers == 0)
+ m->state = m->preLockState;
+ return rc;
+ }
+
+ pToken.queryInterfaceTo(aToken);
break;
}
default:
@@ -2177,7 +2151,7 @@ STDMETHODIMP Medium::LockRead(MediumState_T *aState)
* @note @a aState may be NULL if the state value is not needed (only for
* in-process calls).
*/
-STDMETHODIMP Medium::UnlockRead(MediumState_T *aState)
+HRESULT Medium::unlockRead(MediumState_T *aState)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
@@ -2224,12 +2198,10 @@ STDMETHODIMP Medium::UnlockRead(MediumState_T *aState)
return rc;
}
-/**
- * @note @a aState may be NULL if the state value is not needed (only for
- * in-process calls).
- */
-STDMETHODIMP Medium::LockWrite(MediumState_T *aState)
+STDMETHODIMP Medium::LockWrite(IToken **aToken)
{
+ CheckComArgNotNull(aToken);
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
@@ -2253,10 +2225,6 @@ STDMETHODIMP Medium::LockWrite(MediumState_T *aState)
}
}
- /* return the current state before */
- if (aState)
- *aState = m->state;
-
HRESULT rc = S_OK;
switch (m->state)
@@ -2268,6 +2236,18 @@ STDMETHODIMP Medium::LockWrite(MediumState_T *aState)
LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
m->state = MediumState_LockedWrite;
+
+ ComObjPtr<MediumLockToken> pToken;
+ rc = pToken.createObject();
+ if (SUCCEEDED(rc))
+ rc = pToken->init(this, true /* fWrite */);
+ if (FAILED(rc))
+ {
+ m->state = m->preLockState;
+ return rc;
+ }
+
+ pToken.queryInterfaceTo(aToken);
break;
}
default:
@@ -2285,7 +2265,7 @@ STDMETHODIMP Medium::LockWrite(MediumState_T *aState)
* @note @a aState may be NULL if the state value is not needed (only for
* in-process calls).
*/
-STDMETHODIMP Medium::UnlockWrite(MediumState_T *aState)
+HRESULT Medium::unlockWrite(MediumState_T *aState)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
@@ -2351,8 +2331,14 @@ STDMETHODIMP Medium::GetProperty(IN_BSTR aName, BSTR *aValue)
settings::StringsMap::const_iterator it = m->mapProperties.find(Utf8Str(aName));
if (it == m->mapProperties.end())
- return setError(VBOX_E_OBJECT_NOT_FOUND,
- tr("Property '%ls' does not exist"), aName);
+ {
+ if (!Utf8Str(aName).startsWith("Special/"))
+ return setError(VBOX_E_OBJECT_NOT_FOUND,
+ tr("Property '%ls' does not exist"), aName);
+ else
+ /* be more silent here */
+ return VBOX_E_OBJECT_NOT_FOUND;
+ }
it->second.cloneTo(aValue);
@@ -2377,13 +2363,32 @@ STDMETHODIMP Medium::SetProperty(IN_BSTR aName, IN_BSTR aValue)
return setStateError();
}
- settings::StringsMap::iterator it = m->mapProperties.find(Utf8Str(aName));
- if (it == m->mapProperties.end())
- return setError(VBOX_E_OBJECT_NOT_FOUND,
- tr("Property '%ls' does not exist"),
- aName);
-
- it->second = aValue;
+ Utf8Str strName(aName);
+ Utf8Str strValue(aValue);
+ settings::StringsMap::iterator it = m->mapProperties.find(strName);
+ if (!strName.startsWith("Special/"))
+ {
+ if (it == m->mapProperties.end())
+ return setError(VBOX_E_OBJECT_NOT_FOUND,
+ tr("Property '%s' does not exist"),
+ strName.c_str());
+ it->second = strValue;
+ }
+ else
+ {
+ if (it == m->mapProperties.end())
+ {
+ if (!strValue.isEmpty())
+ m->mapProperties[strName] = strValue;
+ }
+ else
+ {
+ if (!strValue.isEmpty())
+ it->second = aValue;
+ else
+ m->mapProperties.erase(it);
+ }
+ }
// save the settings
mlock.release();
@@ -2446,9 +2451,11 @@ STDMETHODIMP Medium::SetProperties(ComSafeArrayIn(IN_BSTR, aNames),
i < names.size();
++i)
{
- if (m->mapProperties.find(Utf8Str(names[i])) == m->mapProperties.end())
+ Utf8Str strName(names[i]);
+ if ( !strName.startsWith("Special/")
+ && m->mapProperties.find(strName) == m->mapProperties.end())
return setError(VBOX_E_OBJECT_NOT_FOUND,
- tr("Property '%ls' does not exist"), names[i]);
+ tr("Property '%s' does not exist"), strName.c_str());
}
/* second pass: assign */
@@ -2456,10 +2463,29 @@ STDMETHODIMP Medium::SetProperties(ComSafeArrayIn(IN_BSTR, aNames),
i < names.size();
++i)
{
- settings::StringsMap::iterator it = m->mapProperties.find(Utf8Str(names[i]));
- AssertReturn(it != m->mapProperties.end(), E_FAIL);
-
- it->second = Utf8Str(values[i]);
+ Utf8Str strName(names[i]);
+ Utf8Str strValue(values[i]);
+ settings::StringsMap::iterator it = m->mapProperties.find(strName);
+ if (!strName.startsWith("Special/"))
+ {
+ AssertReturn(it != m->mapProperties.end(), E_FAIL);
+ it->second = strValue;
+ }
+ else
+ {
+ if (it == m->mapProperties.end())
+ {
+ if (!strValue.isEmpty())
+ m->mapProperties[strName] = strValue;
+ }
+ else
+ {
+ if (!strValue.isEmpty())
+ it->second = strValue;
+ else
+ m->mapProperties.erase(it);
+ }
+ }
}
// save the settings
@@ -2471,9 +2497,10 @@ STDMETHODIMP Medium::SetProperties(ComSafeArrayIn(IN_BSTR, aNames),
}
STDMETHODIMP Medium::CreateBaseStorage(LONG64 aLogicalSize,
- ULONG aVariant,
+ ComSafeArrayIn(MediumVariant_T, aVariant),
IProgress **aProgress)
{
+ CheckComArgSafeArrayNotNull(aVariant);
CheckComArgOutPointerValid(aProgress);
if (aLogicalSize < 0)
return setError(E_INVALIDARG, tr("The medium size argument (%lld) is negative"), aLogicalSize);
@@ -2482,21 +2509,32 @@ STDMETHODIMP Medium::CreateBaseStorage(LONG64 aLogicalSize,
if (FAILED(autoCaller.rc())) return autoCaller.rc();
HRESULT rc = S_OK;
- ComObjPtr <Progress> pProgress;
+ ComObjPtr<Progress> pProgress;
Medium::Task *pTask = NULL;
try
{
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- aVariant = (MediumVariant_T)((unsigned)aVariant & (unsigned)~MediumVariant_Diff);
- if ( !(aVariant & MediumVariant_Fixed)
- && !(m->formatObj->getCapabilities() & MediumFormatCapabilities_CreateDynamic))
+ ULONG mediumVariantFlags = 0;
+
+ if (aVariant)
+ {
+ com::SafeArray<MediumVariant_T> variants(ComSafeArrayInArg(aVariant));
+ for (size_t i = 0; i < variants.size(); i++)
+ mediumVariantFlags |= variants[i];
+ }
+
+ mediumVariantFlags &= ((unsigned)~MediumVariant_Diff);
+
+ if ( !(mediumVariantFlags & MediumVariant_Fixed)
+ && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateDynamic))
throw setError(VBOX_E_NOT_SUPPORTED,
tr("Medium format '%s' does not support dynamic storage creation"),
m->strFormat.c_str());
- if ( (aVariant & MediumVariant_Fixed)
- && !(m->formatObj->getCapabilities() & MediumFormatCapabilities_CreateDynamic))
+
+ if ( (mediumVariantFlags & MediumVariant_Fixed)
+ && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateDynamic))
throw setError(VBOX_E_NOT_SUPPORTED,
tr("Medium format '%s' does not support fixed storage creation"),
m->strFormat.c_str());
@@ -2507,7 +2545,7 @@ STDMETHODIMP Medium::CreateBaseStorage(LONG64 aLogicalSize,
pProgress.createObject();
rc = pProgress->init(m->pVirtualBox,
static_cast<IMedium*>(this),
- (aVariant & MediumVariant_Fixed)
+ (mediumVariantFlags & MediumVariant_Fixed)
? BstrFmt(tr("Creating fixed medium storage unit '%s'"), m->strLocationFull.c_str()).raw()
: BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
TRUE /* aCancelable */);
@@ -2516,7 +2554,8 @@ STDMETHODIMP Medium::CreateBaseStorage(LONG64 aLogicalSize,
/* setup task object to carry out the operation asynchronously */
pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize,
- (MediumVariant_T)aVariant);
+ (MediumVariant_T)mediumVariantFlags);
+ //(MediumVariant_T)aVariant);
rc = pTask->rc();
AssertComRC(rc);
if (FAILED(rc))
@@ -2560,11 +2599,12 @@ STDMETHODIMP Medium::DeleteStorage(IProgress **aProgress)
}
STDMETHODIMP Medium::CreateDiffStorage(IMedium *aTarget,
- ULONG aVariant,
- IProgress **aProgress)
+ ComSafeArrayIn(MediumVariant_T, aVariant),
+ IProgress **aProgress)
{
CheckComArgNotNull(aTarget);
CheckComArgOutPointerValid(aProgress);
+ CheckComArgSafeArrayNotNull(aVariant);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
@@ -2625,9 +2665,18 @@ STDMETHODIMP Medium::CreateDiffStorage(IMedium *aTarget,
alock.release();
- ComObjPtr <Progress> pProgress;
+ ComObjPtr<Progress> pProgress;
+
+ ULONG mediumVariantFlags = 0;
+
+ if (aVariant)
+ {
+ com::SafeArray<MediumVariant_T> variants(ComSafeArrayInArg(aVariant));
+ for (size_t i = 0; i < variants.size(); i++)
+ mediumVariantFlags |= variants[i];
+ }
- rc = createDiffStorage(diff, (MediumVariant_T)aVariant, pMediumLockList,
+ rc = createDiffStorage(diff, (MediumVariant_T)mediumVariantFlags, pMediumLockList,
&pProgress, false /* aWait */);
if (FAILED(rc))
delete pMediumLockList;
@@ -2650,21 +2699,21 @@ STDMETHODIMP Medium::MergeTo(IMedium *aTarget, IProgress **aProgress)
bool fMergeForward = false;
ComObjPtr<Medium> pParentForTarget;
- MediaList childrenToReparent;
+ MediumLockList *pChildrenToReparent = NULL;
MediumLockList *pMediumLockList = NULL;
HRESULT rc = S_OK;
rc = prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward,
- pParentForTarget, childrenToReparent, pMediumLockList);
+ pParentForTarget, pChildrenToReparent, pMediumLockList);
if (FAILED(rc)) return rc;
- ComObjPtr <Progress> pProgress;
+ ComObjPtr<Progress> pProgress;
- rc = mergeTo(pTarget, fMergeForward, pParentForTarget, childrenToReparent,
+ rc = mergeTo(pTarget, fMergeForward, pParentForTarget, pChildrenToReparent,
pMediumLockList, &pProgress, false /* aWait */);
if (FAILED(rc))
- cancelMergeTo(childrenToReparent, pMediumLockList);
+ cancelMergeTo(pChildrenToReparent, pMediumLockList);
else
pProgress.queryInterfaceTo(aProgress);
@@ -2672,23 +2721,29 @@ STDMETHODIMP Medium::MergeTo(IMedium *aTarget, IProgress **aProgress)
}
STDMETHODIMP Medium::CloneToBase(IMedium *aTarget,
- ULONG aVariant,
- IProgress **aProgress)
+ ComSafeArrayIn(MediumVariant_T, aVariant),
+ IProgress **aProgress)
{
int rc = S_OK;
CheckComArgNotNull(aTarget);
CheckComArgOutPointerValid(aProgress);
- rc = CloneTo(aTarget, aVariant, NULL, aProgress);
+ CheckComArgSafeArrayNotNull(aVariant);
+
+ com::SafeArray<MediumVariant_T> variants(ComSafeArrayInArg(aVariant));
+
+ rc = CloneTo(aTarget, ComSafeArrayAsInParam(variants), NULL, aProgress);
return rc;
}
STDMETHODIMP Medium::CloneTo(IMedium *aTarget,
- ULONG aVariant,
+ ComSafeArrayIn(MediumVariant_T, aVariant),
IMedium *aParent,
IProgress **aProgress)
{
CheckComArgNotNull(aTarget);
CheckComArgOutPointerValid(aProgress);
+ CheckComArgSafeArrayNotNull(aVariant);
+
ComAssertRet(aTarget != this, E_INVALIDARG);
AutoCaller autoCaller(this);
@@ -2786,9 +2841,18 @@ STDMETHODIMP Medium::CloneTo(IMedium *aTarget,
throw rc;
}
+ ULONG mediumVariantFlags = 0;
+
+ if (aVariant)
+ {
+ com::SafeArray<MediumVariant_T> variants(ComSafeArrayInArg(aVariant));
+ for (size_t i = 0; i < variants.size(); i++)
+ mediumVariantFlags |= variants[i];
+ }
+
/* setup task object to carry out the operation asynchronously */
pTask = new Medium::CloneTask(this, pProgress, pTarget,
- (MediumVariant_T)aVariant,
+ (MediumVariant_T)mediumVariantFlags,
pParent, UINT32_MAX, UINT32_MAX,
pSourceMediumLockList, pTargetMediumLockList);
rc = pTask->rc();
@@ -2814,6 +2878,29 @@ STDMETHODIMP Medium::CloneTo(IMedium *aTarget,
return rc;
}
+STDMETHODIMP Medium::SetLocation(IN_BSTR aLocation, IProgress **aProgress)
+{
+ CheckComArgStrNotEmptyOrNull(aLocation);
+ CheckComArgOutPointerValid(aProgress);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ /// @todo NEWMEDIA for file names, add the default extension if no extension
+ /// is present (using the information from the VD backend which also implies
+ /// that one more parameter should be passed to setLocation() requesting
+ /// that functionality since it is only allowed when called from this method
+
+ /// @todo NEWMEDIA rename the file and set m->location on success, then save
+ /// the global registry (and local registries of portable VMs referring to
+ /// this medium), this will also require to add the mRegistered flag to data
+
+ *aProgress = NULL;
+ ReturnComNotImplemented();
+}
+
STDMETHODIMP Medium::Compact(IProgress **aProgress)
{
CheckComArgOutPointerValid(aProgress);
@@ -2822,7 +2909,7 @@ STDMETHODIMP Medium::Compact(IProgress **aProgress)
if (FAILED(autoCaller.rc())) return autoCaller.rc();
HRESULT rc = S_OK;
- ComObjPtr <Progress> pProgress;
+ ComObjPtr<Progress> pProgress;
Medium::Task *pTask = NULL;
try
@@ -2895,7 +2982,7 @@ STDMETHODIMP Medium::Resize(LONG64 aLogicalSize, IProgress **aProgress)
if (FAILED(autoCaller.rc())) return autoCaller.rc();
HRESULT rc = S_OK;
- ComObjPtr <Progress> pProgress;
+ ComObjPtr<Progress> pProgress;
Medium::Task *pTask = NULL;
try
@@ -2968,7 +3055,7 @@ STDMETHODIMP Medium::Reset(IProgress **aProgress)
if (FAILED(autoCaller.rc())) return autoCaller.rc();
HRESULT rc = S_OK;
- ComObjPtr <Progress> pProgress;
+ ComObjPtr<Progress> pProgress;
Medium::Task *pTask = NULL;
try
@@ -3038,17 +3125,8 @@ STDMETHODIMP Medium::Reset(IProgress **aProgress)
if (SUCCEEDED(rc))
pProgress.queryInterfaceTo(aProgress);
}
- else
- {
- /* Note: on success, the task will unlock this */
- {
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- HRESULT rc2 = UnlockWrite(NULL);
- AssertComRC(rc2);
- }
- if (pTask != NULL)
- delete pTask;
- }
+ else if (pTask != NULL)
+ delete pTask;
LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
@@ -3150,7 +3228,7 @@ const ComObjPtr<MediumFormat>& Medium::getMediumFormat() const
bool Medium::isMediumFormatFile() const
{
if ( m->formatObj
- && (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
+ && (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
)
return true;
return false;
@@ -3382,6 +3460,9 @@ void Medium::markRegistriesModified()
llRegistryIDs = m->llRegistryIDs;
}
+ /* Save the error information now, the implicit restore when this goes
+ * out of scope will throw away spurious additional errors created below. */
+ ErrorInfoKeeper eik;
for (GuidList::const_iterator it = llRegistryIDs.begin();
it != llRegistryIDs.end();
++it)
@@ -3400,7 +3481,7 @@ void Medium::markRegistriesModified()
HRESULT Medium::addBackReference(const Guid &aMachineId,
const Guid &aSnapshotId /*= Guid::Empty*/)
{
- AssertReturn(!aMachineId.isEmpty(), E_FAIL);
+ AssertReturn(aMachineId.isValid(), E_FAIL);
LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
@@ -3442,7 +3523,8 @@ HRESULT Medium::addBackReference(const Guid &aMachineId,
// if the caller has not supplied a snapshot ID, then we're attaching
// to a machine a medium which represents the machine's current state,
// so set the flag
- if (aSnapshotId.isEmpty())
+
+ if (aSnapshotId.isZero())
{
/* sanity: no duplicate attachments */
if (it->fInCurState)
@@ -3479,7 +3561,8 @@ HRESULT Medium::addBackReference(const Guid &aMachineId,
}
it->llSnapshotIds.push_back(aSnapshotId);
- it->fInCurState = false;
+ // Do not touch fInCurState, as the image may be attached to the current
+ // state *and* a snapshot, otherwise we lose the current state association!
LogFlowThisFuncLeave();
@@ -3497,7 +3580,7 @@ HRESULT Medium::addBackReference(const Guid &aMachineId,
HRESULT Medium::removeBackReference(const Guid &aMachineId,
const Guid &aSnapshotId /*= Guid::Empty*/)
{
- AssertReturn(!aMachineId.isEmpty(), E_FAIL);
+ AssertReturn(aMachineId.isValid(), E_FAIL);
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
@@ -3509,7 +3592,7 @@ HRESULT Medium::removeBackReference(const Guid &aMachineId,
BackRef::EqualsTo(aMachineId));
AssertReturn(it != m->backRefs.end(), E_FAIL);
- if (aSnapshotId.isEmpty())
+ if (aSnapshotId.isZero())
{
/* remove the current state attachment */
it->fInCurState = false;
@@ -4091,7 +4174,7 @@ Utf8Str Medium::getPreferredDiffFormat()
AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
/* check that our own format supports diffs */
- if (!(m->formatObj->getCapabilities() & MediumFormatCapabilities_Differencing))
+ if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
{
/* use the default format if not */
Utf8Str tmp;
@@ -4224,7 +4307,7 @@ HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
COMMA_LOCKVAL_SRC_POS);
LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
- if ( !(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
+ if ( !(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
| MediumFormatCapabilities_CreateFixed)))
throw setError(VBOX_E_NOT_SUPPORTED,
tr("Medium format '%s' does not support storage deletion"),
@@ -4451,6 +4534,112 @@ HRESULT Medium::unmarkLockedForDeletion()
}
/**
+ * Queries the preferred merge direction from this to the other medium, i.e.
+ * the one which requires the least amount of I/O and therefore time and
+ * disk consumption.
+ *
+ * @returns Status code.
+ * @retval E_FAIL in case determining the merge direction fails for some reason,
+ * for example if getting the size of the media fails. There is no
+ * error set though and the caller is free to continue to find out
+ * what was going wrong later. Leaves fMergeForward unset.
+ * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
+ * An error is set.
+ * @param pOther The other medium to merge with.
+ * @param fMergeForward Resulting preferred merge direction (out).
+ */
+HRESULT Medium::queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
+ bool &fMergeForward)
+{
+ AssertReturn(pOther != NULL, E_FAIL);
+ AssertReturn(pOther != this, E_FAIL);
+
+ AutoCaller autoCaller(this);
+ AssertComRCReturnRC(autoCaller.rc());
+
+ AutoCaller otherCaller(pOther);
+ AssertComRCReturnRC(otherCaller.rc());
+
+ HRESULT rc = S_OK;
+ bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
+
+ try
+ {
+ // locking: we need the tree lock first because we access parent pointers
+ AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
+
+ /* more sanity checking and figuring out the current merge direction */
+ ComObjPtr<Medium> pMedium = getParent();
+ while (!pMedium.isNull() && pMedium != pOther)
+ pMedium = pMedium->getParent();
+ if (pMedium == pOther)
+ fThisParent = false;
+ else
+ {
+ pMedium = pOther->getParent();
+ while (!pMedium.isNull() && pMedium != this)
+ pMedium = pMedium->getParent();
+ if (pMedium == this)
+ fThisParent = true;
+ else
+ {
+ Utf8Str tgtLoc;
+ {
+ AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
+ tgtLoc = pOther->getLocationFull();
+ }
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ throw setError(VBOX_E_INVALID_OBJECT_STATE,
+ tr("Media '%s' and '%s' are unrelated"),
+ m->strLocationFull.c_str(), tgtLoc.c_str());
+ }
+ }
+
+ /*
+ * Figure out the preferred merge direction. The current way is to
+ * get the current sizes of file based images and select the merge
+ * direction depending on the size.
+ *
+ * Can't use the VD API to get current size here as the media might
+ * be write locked by a running VM. Resort to RTFileQuerySize().
+ */
+ int vrc = VINF_SUCCESS;
+ uint64_t cbMediumThis = 0;
+ uint64_t cbMediumOther = 0;
+
+ if (isMediumFormatFile() && pOther->isMediumFormatFile())
+ {
+ vrc = RTFileQuerySize(this->getLocationFull().c_str(), &cbMediumThis);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTFileQuerySize(pOther->getLocationFull().c_str(),
+ &cbMediumOther);
+ }
+
+ if (RT_FAILURE(vrc))
+ rc = E_FAIL;
+ else
+ {
+ /*
+ * Check which merge direction might be more optimal.
+ * This method is not bullet proof of course as there might
+ * be overlapping blocks in the images so the file size is
+ * not the best indicator but it is good enough for our purpose
+ * and everything else is too complicated, especially when the
+ * media are used by a running VM.
+ */
+ bool fMergeIntoThis = cbMediumThis > cbMediumOther;
+ fMergeForward = fMergeIntoThis ^ fThisParent;
+ }
+ }
+ }
+ catch (HRESULT aRC) { rc = aRC; }
+
+ return rc;
+}
+
+/**
* Prepares this (source) medium, target medium and all intermediate media
* for the merge operation.
*
@@ -4470,8 +4659,9 @@ HRESULT Medium::unmarkLockedForDeletion()
* later you must call #cancelMergeTo().
* @param fMergeForward Resulting merge direction (out).
* @param pParentForTarget New parent for target medium after merge (out).
- * @param aChildrenToReparent List of children of the source which will have
- * to be reparented to the target after merge (out).
+ * @param aChildrenToReparent Medium lock list containing all children of the
+ * source which will have to be reparented to the target
+ * after merge (out).
* @param aMediumLockList Medium locking information (out).
*
* @note Locks medium tree for reading. Locks this object, aTarget and all
@@ -4483,7 +4673,7 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
bool fLockMedia,
bool &fMergeForward,
ComObjPtr<Medium> &pParentForTarget,
- MediaList &aChildrenToReparent,
+ MediumLockList * &aChildrenToReparent,
MediumLockList * &aMediumLockList)
{
AssertReturn(pTarget != NULL, E_FAIL);
@@ -4498,7 +4688,8 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
HRESULT rc = S_OK;
fMergeForward = false;
pParentForTarget.setNull();
- aChildrenToReparent.clear();
+ Assert(aChildrenToReparent == NULL);
+ aChildrenToReparent = NULL;
Assert(aMediumLockList == NULL);
aMediumLockList = NULL;
@@ -4587,9 +4778,9 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
if ( m->backRefs.size() != 0
&& ( !aMachineId
|| m->backRefs.size() != 1
- || aMachineId->isEmpty()
+ || aMachineId->isZero()
|| *getFirstMachineBackrefId() != *aMachineId
- || ( (!aSnapshotId || !aSnapshotId->isEmpty())
+ || ( (!aSnapshotId || !aSnapshotId->isZero())
&& *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
throw setError(VBOX_E_OBJECT_IN_USE,
tr("Medium '%s' is attached to %d virtual machines"),
@@ -4681,19 +4872,21 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
else
{
/* we will need to reparent children of the source */
+ aChildrenToReparent = new MediumLockList();
for (MediaList::const_iterator it = getChildren().begin();
it != getChildren().end();
++it)
{
pMedium = *it;
- if (fLockMedia)
- {
- rc = pMedium->LockWrite(NULL);
- if (FAILED(rc))
- throw rc;
- }
-
- aChildrenToReparent.push_back(pMedium);
+ aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
+ }
+ if (fLockMedia && aChildrenToReparent)
+ {
+ treeLock.release();
+ rc = aChildrenToReparent->Lock();
+ treeLock.acquire();
+ if (FAILED(rc))
+ throw rc;
}
}
for (pLast = pLastIntermediate;
@@ -4752,8 +4945,16 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
if (FAILED(rc))
{
- delete aMediumLockList;
- aMediumLockList = NULL;
+ if (aMediumLockList)
+ {
+ delete aMediumLockList;
+ aMediumLockList = NULL;
+ }
+ if (aChildrenToReparent)
+ {
+ delete aChildrenToReparent;
+ aChildrenToReparent = NULL;
+ }
}
return rc;
@@ -4838,9 +5039,9 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
bool fMergeForward,
const ComObjPtr<Medium> &pParentForTarget,
- const MediaList &aChildrenToReparent,
+ MediumLockList *aChildrenToReparent,
MediumLockList *aMediumLockList,
- ComObjPtr <Progress> *aProgress,
+ ComObjPtr<Progress> *aProgress,
bool aWait)
{
AssertReturn(pTarget != NULL, E_FAIL);
@@ -4855,7 +5056,7 @@ HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
AssertComRCReturnRC(targetCaller.rc());
HRESULT rc = S_OK;
- ComObjPtr <Progress> pProgress;
+ ComObjPtr<Progress> pProgress;
Medium::Task *pTask = NULL;
try
@@ -4927,7 +5128,7 @@ HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
*
* @note Locks the media from the chain for writing.
*/
-void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
+void Medium::cancelMergeTo(MediumLockList *aChildrenToReparent,
MediumLockList *aMediumLockList)
{
AutoCaller autoCaller(this);
@@ -4959,23 +5160,17 @@ void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
/* the destructor will do the work */
delete aMediumLockList;
- /* unlock the children which had to be reparented */
- for (MediaList::const_iterator it = aChildrenToReparent.begin();
- it != aChildrenToReparent.end();
- ++it)
- {
- const ComObjPtr<Medium> &pMedium = *it;
-
- AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
- pMedium->UnlockWrite(NULL);
- }
+ /* unlock the children which had to be reparented, the destructor will do
+ * the work */
+ if (aChildrenToReparent)
+ delete aChildrenToReparent;
}
/**
* Fix the parent UUID of all children to point to this medium as their
* parent.
*/
-HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
+HRESULT Medium::fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
{
Assert(!isWriteLockOnCurrentThread());
Assert(!m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
@@ -5016,16 +5211,19 @@ HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
throw vrc;
}
- for (MediaList::const_iterator it = childrenToReparent.begin();
- it != childrenToReparent.end();
+ MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
+ MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
+ for (MediumLockList::Base::iterator it = childrenBegin;
+ it != childrenEnd;
++it)
{
+ Medium *pMedium = it->GetMedium();
/* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
vrc = VDOpen(hdd,
- (*it)->m->strFormat.c_str(),
- (*it)->m->strLocationFull.c_str(),
+ pMedium->m->strFormat.c_str(),
+ pMedium->m->strLocationFull.c_str(),
VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
- (*it)->m->vdImageIfaces);
+ pMedium->m->vdImageIfaces);
if (RT_FAILURE(vrc))
throw vrc;
@@ -5036,8 +5234,6 @@ HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
vrc = VDClose(hdd, false /* fDelete */);
if (RT_FAILURE(vrc))
throw vrc;
-
- (*it)->UnlockWrite(NULL);
}
}
catch (HRESULT aRC) { rc = aRC; }
@@ -5433,7 +5629,7 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
/* are we dealing with a new medium constructed using the existing
* location? */
- bool isImport = m->id.isEmpty();
+ bool isImport = m->id.isZero();
unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
/* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
@@ -5453,10 +5649,11 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
/* Lock the medium, which makes the behavior much more consistent */
alock.release();
+ ComPtr<IToken> pToken;
if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
- rc = LockRead(NULL);
+ rc = LockRead(pToken.asOutParam());
else
- rc = LockWrite(NULL);
+ rc = LockWrite(pToken.asOutParam());
if (FAILED(rc)) return rc;
alock.acquire();
@@ -5524,7 +5721,7 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
throw S_OK;
}
- if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
+ if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
{
/* Modify the UUIDs if necessary. The associated fields are
* not modified by other code, so no need to copy. */
@@ -5533,7 +5730,12 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
alock.acquire();
vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
alock.release();
- ComAssertRCThrow(vrc, E_FAIL);
+ if (RT_FAILURE(vrc))
+ {
+ lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
+ location.c_str(), vdError(vrc).c_str());
+ throw S_OK;
+ }
mediumId = m->uuidImage;
}
if (fSetParentId)
@@ -5541,7 +5743,12 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
alock.acquire();
vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
alock.release();
- ComAssertRCThrow(vrc, E_FAIL);
+ if (RT_FAILURE(vrc))
+ {
+ lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
+ location.c_str(), vdError(vrc).c_str());
+ throw S_OK;
+ }
}
/* zap the information, these are no long-term members */
alock.acquire();
@@ -5558,13 +5765,13 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
{
mediumId = uuid;
- if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
+ if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
// only when importing a VDMK that has no UUID, create one in memory
mediumId.create();
}
else
{
- Assert(!mediumId.isEmpty());
+ Assert(!mediumId.isZero());
if (mediumId != uuid)
{
@@ -5626,7 +5833,7 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
* Since such images don't support random writes they will not
* be created for diff images. Only an overly smart user might
* manually create this case. Too bad for him. */
- if ( isImport
+ if ( (isImport || fSetParentId)
&& !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
{
/* the parent must be known to us. Note that we freely
@@ -5636,24 +5843,43 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
* threads yet (and init() will fail if this method reports
* MediumState_Inaccessible) */
- Guid id = parentId;
ComObjPtr<Medium> pParent;
- rc = m->pVirtualBox->findHardDiskById(id, false /* aSetError */, &pParent);
+ if (RTUuidIsNull(&parentId))
+ rc = VBOX_E_OBJECT_NOT_FOUND;
+ else
+ rc = m->pVirtualBox->findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
if (FAILED(rc))
{
- lastAccessError = Utf8StrFmt(
- tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
- &parentId, location.c_str(),
- m->pVirtualBox->settingsFilePath().c_str());
- throw S_OK;
+ if (fSetImageId && !fSetParentId)
+ {
+ /* If the image UUID gets changed for an existing
+ * image then the parent UUID can be stale. In such
+ * cases clear the parent information. The parent
+ * information may/will be re-set later if the
+ * API client wants to adjust a complete medium
+ * hierarchy one by one. */
+ rc = S_OK;
+ alock.acquire();
+ RTUuidClear(&parentId);
+ vrc = VDSetParentUuid(hdd, 0, &parentId);
+ alock.release();
+ ComAssertRCThrow(vrc, E_FAIL);
+ }
+ else
+ {
+ lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
+ &parentId, location.c_str(),
+ m->pVirtualBox->settingsFilePath().c_str());
+ throw S_OK;
+ }
}
/* we set mParent & children() */
treeLock.acquire();
- Assert(m->pParent.isNull());
- m->pParent = pParent;
- m->pParent->m->llChildren.push_back(this);
+ if (m->pParent)
+ deparent();
+ setParent(pParent);
treeLock.release();
}
@@ -5765,13 +5991,9 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
else
m->preLockState = MediumState_Inaccessible;
- HRESULT rc2;
- if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
- rc2 = UnlockRead(NULL);
- else
- rc2 = UnlockWrite(NULL);
- if (SUCCEEDED(rc) && FAILED(rc2))
- rc = rc2;
+ pToken->Abandon();
+ pToken.setNull();
+
if (FAILED(rc)) return rc;
/* If this is a base image which incorrectly has a parent UUID set,
@@ -5782,7 +6004,7 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
* the diff. Not acceptable. */
if (fRepairImageZeroParentUuid)
{
- rc = LockWrite(NULL);
+ rc = LockWrite(pToken.asOutParam());
if (FAILED(rc)) return rc;
alock.release();
@@ -5820,9 +6042,8 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
rc = aRC;
}
- rc = UnlockWrite(NULL);
- if (SUCCEEDED(rc) && FAILED(rc2))
- rc = rc2;
+ pToken->Abandon();
+ pToken.setNull();
if (FAILED(rc)) return rc;
}
@@ -6013,7 +6234,7 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation,
AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
|| ( autoCaller.state() == InInit
&& m->state != MediumState_NotCreated
- && m->id.isEmpty()
+ && m->id.isZero()
&& m->strFormat.isEmpty()
&& m->formatObj.isNull()),
E_FAIL);
@@ -6023,7 +6244,7 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation,
bool isImport = m->strFormat.isEmpty();
if ( isImport
- || ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
+ || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
&& !m->hostDrive))
{
Guid id;
@@ -6033,7 +6254,7 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation,
if (m->state == MediumState_NotCreated)
{
/* must be a file (formatObj must be already known) */
- Assert(m->formatObj->getCapabilities() & MediumFormatCapabilities_File);
+ Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
if (RTPathFilename(aLocation.c_str()) == NULL)
{
@@ -6041,11 +6262,11 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation,
* slash), generate a new UUID + file name if the state allows
* this */
- ComAssertMsgRet(!m->formatObj->getFileExtensions().empty(),
+ ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
E_FAIL);
- Utf8Str strExt = m->formatObj->getFileExtensions().front();
+ Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
ComAssertMsgRet(!strExt.isEmpty(),
("Default extension must not be empty\n"),
E_FAIL);
@@ -6059,7 +6280,7 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation,
// we must always have full paths now (if it refers to a file)
if ( ( m->formatObj.isNull()
- || m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
+ || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
&& !RTPathStartsWithRoot(locationFull.c_str()))
return setError(VBOX_E_FILE_ERROR,
tr("The given path '%s' is not fully qualified"),
@@ -6145,7 +6366,7 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation,
m->strLocationFull = locationFull;
/* is it still a file? */
- if ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
+ if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
&& (m->state == MediumState_NotCreated)
)
/* assign a new UUID (this UUID will be used when calling
@@ -6193,8 +6414,8 @@ HRESULT Medium::setFormat(const Utf8Str &aFormat)
Assert(m->mapProperties.empty());
- for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
- it != m->formatObj->getProperties().end();
+ for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
+ it != m->formatObj->i_getProperties().end();
++it)
{
m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
@@ -6572,7 +6793,8 @@ HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
/* The object may request a specific UUID (through a special form of
* the setLocation() argument). Otherwise we have to generate it */
Guid id = m->id;
- fGenerateUuid = id.isEmpty();
+
+ fGenerateUuid = id.isZero();
if (fGenerateUuid)
{
id.create();
@@ -6582,7 +6804,7 @@ HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
Utf8Str format(m->strFormat);
Utf8Str location(m->strLocationFull);
- uint64_t capabilities = m->formatObj->getCapabilities();
+ uint64_t capabilities = m->formatObj->i_getCapabilities();
ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
| MediumFormatCapabilities_CreateDynamic), E_FAIL);
Assert(m->state == MediumState_Creating);
@@ -6709,7 +6931,8 @@ HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
/* The object may request a specific UUID (through a special form of
* the setLocation() argument). Otherwise we have to generate it */
Guid targetId = pTarget->m->id;
- fGenerateUuid = targetId.isEmpty();
+
+ fGenerateUuid = targetId.isZero();
if (fGenerateUuid)
{
targetId.create();
@@ -6721,7 +6944,7 @@ HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
Utf8Str targetFormat(pTarget->m->strFormat);
Utf8Str targetLocation(pTarget->m->strLocationFull);
- uint64_t capabilities = pTarget->m->formatObj->getCapabilities();
+ uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
Assert(pTarget->m->state == MediumState_Creating);
@@ -6762,7 +6985,7 @@ HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
vrc = VDOpen(hdd,
pMedium->m->strFormat.c_str(),
pMedium->m->strLocationFull.c_str(),
- VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
+ VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
pMedium->m->vdImageIfaces);
if (RT_FAILURE(vrc))
throw setError(VBOX_E_FILE_ERROR,
@@ -6988,18 +7211,21 @@ HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
/* we need to update UUIDs of all source's children
* which cannot be part of the container at once so
* add each one in there individually */
- if (task.mChildrenToReparent.size() > 0)
+ if (task.mpChildrenToReparent)
{
- for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
- it != task.mChildrenToReparent.end();
+ MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
+ MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
+ for (MediumLockList::Base::iterator it = childrenBegin;
+ it != childrenEnd;
++it)
{
+ Medium *pMedium = it->GetMedium();
/* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
vrc = VDOpen(hdd,
- (*it)->m->strFormat.c_str(),
- (*it)->m->strLocationFull.c_str(),
+ pMedium->m->strFormat.c_str(),
+ pMedium->m->strLocationFull.c_str(),
VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
- (*it)->m->vdImageIfaces);
+ pMedium->m->vdImageIfaces);
if (RT_FAILURE(vrc))
throw vrc;
@@ -7011,8 +7237,6 @@ HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
vrc = VDClose(hdd, false /* fDelete */);
if (RT_FAILURE(vrc))
throw vrc;
-
- (*it)->UnlockWrite(NULL);
}
}
}
@@ -7075,16 +7299,18 @@ HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
/* reparent source's children and disconnect the deleted
* branch at the younger end */
- if (task.mChildrenToReparent.size() > 0)
+ if (task.mpChildrenToReparent)
{
/* obey {parent,child} lock order */
AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
- for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
- it != task.mChildrenToReparent.end();
- it++)
+ MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
+ MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
+ for (MediumLockList::Base::iterator it = childrenBegin;
+ it != childrenEnd;
+ ++it)
{
- Medium *pMedium = *it;
+ Medium *pMedium = it->GetMedium();
AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
pMedium->deparent(); // removes pMedium from source
@@ -7170,10 +7396,7 @@ HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
* in the mergeTo() docs. The latter also implies that we
* don't own the merge chain, so release it in this case. */
if (task.isAsync())
- {
- Assert(task.mChildrenToReparent.size() == 0);
- cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
- }
+ cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
}
return mrc;
@@ -7213,7 +7436,8 @@ HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
/* The object may request a specific UUID (through a special form of
* the setLocation() argument). Otherwise we have to generate it */
Guid targetId = pTarget->m->id;
- fGenerateUuid = targetId.isEmpty();
+
+ fGenerateUuid = targetId.isZero();
if (fGenerateUuid)
{
targetId.create();
@@ -7258,7 +7482,7 @@ HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
Utf8Str targetFormat(pTarget->m->strFormat);
Utf8Str targetLocation(pTarget->m->strLocationFull);
- uint64_t capabilities = pTarget->m->formatObj->getCapabilities();
+ uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
Assert( pTarget->m->state == MediumState_Creating
|| pTarget->m->state == MediumState_LockedWrite);
@@ -7407,7 +7631,8 @@ HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
ComObjPtr<Medium> pMedium;
mrc = pParent->m->pVirtualBox->registerMedium(pTarget, &pMedium,
DeviceType_HardDisk);
- Assert(pTarget == pMedium);
+ Assert( FAILED(mrc)
+ || pTarget == pMedium);
eik.fetch();
if (FAILED(mrc))
@@ -7421,7 +7646,8 @@ HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
ComObjPtr<Medium> pMedium;
mrc = m->pVirtualBox->registerMedium(pTarget, &pMedium,
DeviceType_HardDisk);
- Assert(pTarget == pMedium);
+ Assert( FAILED(mrc)
+ || pTarget == pMedium);
eik.fetch();
}
}
@@ -7672,15 +7898,8 @@ HRESULT Medium::taskResetHandler(Medium::ResetTask &task)
m->logicalSize = logicalSize;
m->variant = variant;
- if (task.isAsync())
- {
- /* unlock ourselves when done */
- HRESULT rc2 = UnlockWrite(NULL);
- AssertComRC(rc2);
- }
-
- /* Note that in sync mode, it's the caller's responsibility to
- * unlock the medium. */
+ /* Everything is explicitly unlocked when the task exits,
+ * as the task destruction also destroys the media chain. */
return rc;
}
@@ -7736,7 +7955,7 @@ HRESULT Medium::taskCompactHandler(Medium::CompactTask &task)
vrc = VDOpen(hdd,
pMedium->m->strFormat.c_str(),
pMedium->m->strLocationFull.c_str(),
- m->uOpenFlagsDef | (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
+ m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
pMedium->m->vdImageIfaces);
if (RT_FAILURE(vrc))
throw setError(VBOX_E_FILE_ERROR,
@@ -7792,13 +8011,14 @@ HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task)
{
HRESULT rc = S_OK;
- /* Lock all in {parent,child} order. The lock is also used as a
- * signal from the task initiator (which releases it only after
- * RTThreadCreate()) that we can start the job. */
- AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
+ uint64_t size = 0, logicalSize = 0;
try
{
+ /* The lock is also used as a signal from the task initiator (which
+ * releases it only after RTThreadCreate()) that we can start the job */
+ AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
+
PVBOXHDD hdd;
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
ComAssertRCThrow(vrc, E_FAIL);
@@ -7833,7 +8053,7 @@ HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task)
vrc = VDOpen(hdd,
pMedium->m->strFormat.c_str(),
pMedium->m->strLocationFull.c_str(),
- m->uOpenFlagsDef | (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
+ m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
pMedium->m->vdImageIfaces);
if (RT_FAILURE(vrc))
throw setError(VBOX_E_FILE_ERROR,
@@ -7867,6 +8087,8 @@ HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task)
location.c_str(),
vdError(vrc).c_str());
}
+ size = VDGetFileSize(hdd, VD_LAST_IMAGE);
+ logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
}
catch (HRESULT aRC) { rc = aRC; }
@@ -7874,6 +8096,13 @@ HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task)
}
catch (HRESULT aRC) { rc = aRC; }
+ if (SUCCEEDED(rc))
+ {
+ AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
+ m->size = size;
+ m->logicalSize = logicalSize;
+ }
+
/* Everything is explicitly unlocked when the task exits,
* as the task destruction also destroys the media chain. */
@@ -7936,9 +8165,9 @@ HRESULT Medium::taskExportHandler(Medium::ExportTask &task)
vdError(vrc).c_str());
}
- Utf8Str targetFormat(task.mFormat->getId());
+ Utf8Str targetFormat(task.mFormat->i_getId());
Utf8Str targetLocation(task.mFilename);
- uint64_t capabilities = task.mFormat->getCapabilities();
+ uint64_t capabilities = task.mFormat->i_getCapabilities();
Assert(m->state == MediumState_LockedRead);
@@ -8031,7 +8260,8 @@ HRESULT Medium::taskImportHandler(Medium::ImportTask &task)
/* The object may request a specific UUID (through a special form of
* the setLocation() argument). Otherwise we have to generate it */
Guid targetId = m->id;
- fGenerateUuid = targetId.isEmpty();
+
+ fGenerateUuid = targetId.isZero();
if (fGenerateUuid)
{
targetId.create();
@@ -8048,7 +8278,7 @@ HRESULT Medium::taskImportHandler(Medium::ImportTask &task)
{
/* Open source medium. */
vrc = VDOpen(hdd,
- task.mFormat->getId().c_str(),
+ task.mFormat->i_getId().c_str(),
task.mFilename.c_str(),
VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
task.mVDImageIfaces);
@@ -8060,7 +8290,7 @@ HRESULT Medium::taskImportHandler(Medium::ImportTask &task)
Utf8Str targetFormat(m->strFormat);
Utf8Str targetLocation(m->strLocationFull);
- uint64_t capabilities = task.mFormat->getCapabilities();
+ uint64_t capabilities = task.mFormat->i_getCapabilities();
Assert( m->state == MediumState_Creating
|| m->state == MediumState_LockedWrite);