diff options
Diffstat (limited to 'src/VBox/Main/src-server/MediumImpl.cpp')
-rw-r--r-- | src/VBox/Main/src-server/MediumImpl.cpp | 820 |
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); |