diff options
Diffstat (limited to 'src/VBox/Main/src-server/MediumImpl.cpp')
| -rw-r--r-- | src/VBox/Main/src-server/MediumImpl.cpp | 145 |
1 files changed, 141 insertions, 4 deletions
diff --git a/src/VBox/Main/src-server/MediumImpl.cpp b/src/VBox/Main/src-server/MediumImpl.cpp index e942227e7..85c36fefd 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; @@ -973,8 +973,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 */ @@ -4452,6 +4473,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. * @@ -5534,7 +5661,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) @@ -5542,7 +5674,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(); |
