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.cpp145
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();