Index: /trunk/src/VBox/Main/MediumImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/MediumImpl.cpp	(revision 33907)
+++ /trunk/src/VBox/Main/MediumImpl.cpp	(revision 33908)
@@ -2943,4 +2943,125 @@
 
 /**
+ * Returns the medium device type. Must have caller + locking!
+ * @return
+ */
+DeviceType_T Medium::getDeviceType() const
+{
+    return m->devType;
+}
+
+/**
+ * Returns the medium type. Must have caller + locking!
+ * @return
+ */
+MediumType_T Medium::getType() const
+{
+    return m->type;
+}
+
+/**
+ * Returns a short version of the location attribute.
+ *
+ * @note Must be called from under this object's read or write lock.
+ */
+Utf8Str Medium::getName()
+{
+    Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
+    return name;
+}
+
+/**
+ * This adds the given UUID to the list of media registries in which this
+ * medium should be registered. The UUID can either be a machine UUID,
+ * to add a machine registry, or the global registry UUID as returned by
+ * VirtualBox::getGlobalRegistryId().
+ *
+ * Note that for hard disks, this method does nothing if the medium is
+ * already in another registry to avoid having hard disks in more than
+ * one registry, which causes trouble with keeping diff images in sync.
+ * See getFirstRegistryMachineId() for details.
+ *
+ * @param id
+ * @param pfNeedsSaveSettings If != NULL, is set to true if a new reference was added and saveSettings for either the machine or global XML is needed.
+ * @return true if the registry was added.
+ */
+bool Medium::addRegistry(const Guid& id,
+                         bool *pfNeedsSaveSettings)
+{
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return false;
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (    m->devType == DeviceType_HardDisk
+         && m->llRegistryIDs.size() > 0
+       )
+        return false;
+
+    // no need to add the UUID twice
+    for (GuidList::const_iterator it = m->llRegistryIDs.begin();
+         it != m->llRegistryIDs.end();
+         ++it)
+    {
+        if ((*it) == id)
+            return false;
+    }
+
+    m->llRegistryIDs.push_back(id);
+    if (pfNeedsSaveSettings)
+        *pfNeedsSaveSettings = true;
+    return true;
+}
+
+/**
+ * Returns true if id is in the list of media registries for this medium.
+ * @param id
+ * @return
+ */
+bool Medium::isInRegistry(const Guid& id)
+{
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return false;
+
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    for (GuidList::const_iterator it = m->llRegistryIDs.begin();
+         it != m->llRegistryIDs.end();
+         ++it)
+    {
+        if (*it == id)
+            return true;
+    }
+
+    return false;
+}
+
+/**
+ * Internal method to return the medium's first registry machine (i.e. the machine in whose
+ * machine XML this medium is listed).
+ *
+ * Every medium must now (4.0) reside in at least one media registry, which is identified by
+ * a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
+ * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
+ * object if the machine is old and still needs the global registry in VirtualBox.xml.
+ *
+ * By definition, hard disks may only be in one media registry, in which all its children
+ * will be stored as well. Otherwise we run into problems with having keep multiple registries
+ * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
+ * case, only VM2's registry is used for the disk in question.)
+ *
+ * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
+ * the user.
+ *
+ * Must have caller + locking!
+ *
+ * @return
+ */
+const Guid& Medium::getFirstRegistryMachineId() const
+{
+    return m->llRegistryIDs.front();
+}
+
+/**
  * Adds the given machine and optionally the snapshot to the list of the objects
  * this medium is attached to.
@@ -3425,4 +3546,150 @@
 
 /**
+ * Creates a new differencing storage unit using the format of the given target
+ * medium and the location. Note that @c aTarget must be NotCreated.
+ *
+ * The @a aMediumLockList parameter contains the associated medium lock list,
+ * which must be in locked state. If @a aWait is @c true then the caller is
+ * responsible for unlocking.
+ *
+ * If @a aProgress is not NULL but the object it points to is @c null then a
+ * new progress object will be created and assigned to @a *aProgress on
+ * success, otherwise the existing progress object is used. If @a aProgress is
+ * NULL, then no progress object is created/used at all.
+ *
+ * When @a aWait is @c false, this method will create a thread to perform the
+ * create operation asynchronously and will return immediately. Otherwise, it
+ * will perform the operation on the calling thread and will not return to the
+ * caller until the operation is completed. Note that @a aProgress cannot be
+ * NULL when @a aWait is @c false (this method will assert in this case).
+ *
+ * @param aTarget           Target medium.
+ * @param aVariant          Precise medium variant to create.
+ * @param aMediumLockList   List of media which should be locked.
+ * @param aProgress         Where to find/store a Progress object to track
+ *                          operation completion.
+ * @param aWait             @c true if this method should block instead of
+ *                          creating an asynchronous thread.
+ * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
+ *                          initialized to false and that will be set to true
+ *                          by this function if the caller should invoke
+ *                          VirtualBox::saveSettings() because the global
+ *                          settings have changed. This only works in "wait"
+ *                          mode; otherwise saveSettings is called
+ *                          automatically by the thread that was created,
+ *                          and this parameter is ignored.
+ *
+ * @note Locks this object and @a aTarget for writing.
+ */
+HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
+                                  MediumVariant_T aVariant,
+                                  MediumLockList *aMediumLockList,
+                                  ComObjPtr<Progress> *aProgress,
+                                  bool aWait,
+                                  bool *pfNeedsGlobalSaveSettings)
+{
+    AssertReturn(!aTarget.isNull(), E_FAIL);
+    AssertReturn(aMediumLockList, E_FAIL);
+    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoCaller targetCaller(aTarget);
+    if (FAILED(targetCaller.rc())) return targetCaller.rc();
+
+    HRESULT rc = S_OK;
+    ComObjPtr<Progress> pProgress;
+    Medium::Task *pTask = NULL;
+
+    try
+    {
+        AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
+
+        ComAssertThrow(   m->type != MediumType_Writethrough
+                       && m->type != MediumType_Shareable
+                       && m->type != MediumType_Readonly, E_FAIL);
+        ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
+
+        if (aTarget->m->state != MediumState_NotCreated)
+            throw aTarget->setStateError();
+
+        /* Check that the medium is not attached to the current state of
+         * any VM referring to it. */
+        for (BackRefList::const_iterator it = m->backRefs.begin();
+             it != m->backRefs.end();
+             ++it)
+        {
+            if (it->fInCurState)
+            {
+                /* Note: when a VM snapshot is being taken, all normal media
+                 * attached to the VM in the current state will be, as an
+                 * exception, also associated with the snapshot which is about
+                 * to create (see SnapshotMachine::init()) before deassociating
+                 * them from the current state (which takes place only on
+                 * success in Machine::fixupHardDisks()), so that the size of
+                 * snapshotIds will be 1 in this case. The extra condition is
+                 * used to filter out this legal situation. */
+                if (it->llSnapshotIds.size() == 0)
+                    throw setError(VBOX_E_INVALID_OBJECT_STATE,
+                                   tr("Medium '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing media based on it may be created until it is detached"),
+                                   m->strLocationFull.c_str(), it->machineId.raw());
+
+                Assert(it->llSnapshotIds.size() == 1);
+            }
+        }
+
+        if (aProgress != NULL)
+        {
+            /* use the existing progress object... */
+            pProgress = *aProgress;
+
+            /* ...but create a new one if it is null */
+            if (pProgress.isNull())
+            {
+                pProgress.createObject();
+                rc = pProgress->init(m->pVirtualBox,
+                                     static_cast<IMedium*>(this),
+                                     BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
+                                     TRUE /* aCancelable */);
+                if (FAILED(rc))
+                    throw rc;
+            }
+        }
+
+        /* setup task object to carry out the operation sync/async */
+        pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
+                                           aMediumLockList,
+                                           aWait /* fKeepMediumLockList */);
+        rc = pTask->rc();
+        AssertComRC(rc);
+        if (FAILED(rc))
+             throw rc;
+
+        /* register a task (it will deregister itself when done) */
+        ++m->numCreateDiffTasks;
+        Assert(m->numCreateDiffTasks != 0); /* overflow? */
+
+        aTarget->m->state = MediumState_Creating;
+    }
+    catch (HRESULT aRC) { rc = aRC; }
+
+    if (SUCCEEDED(rc))
+    {
+        if (aWait)
+            rc = runNow(pTask, pfNeedsGlobalSaveSettings);
+        else
+            rc = startThread(pTask);
+
+        if (SUCCEEDED(rc) && aProgress != NULL)
+            *aProgress = pProgress;
+    }
+    else if (pTask != NULL)
+        delete pTask;
+
+    return rc;
+}
+
+/**
  * Returns a preferred format for differencing media.
  */
@@ -3446,122 +3713,1649 @@
 
 /**
- * Returns the medium device type. Must have caller + locking!
+ * Implementation for the public Medium::Close() with the exception of calling
+ * VirtualBox::saveSettings(), in case someone wants to call this for several
+ * media.
+ *
+ * After this returns with success, uninit() has been called on the medium, and
+ * the object is no longer usable ("not ready" state).
+ *
+ * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
+ *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
+ *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
+ *                and this parameter is ignored.
+ * @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here
+ *                   upon which the Medium instance gets uninitialized.
  * @return
  */
-DeviceType_T Medium::getDeviceType() const
-{
-    return m->devType;
-}
-
-/**
- * Returns the medium type. Must have caller + locking!
+HRESULT Medium::close(bool *pfNeedsGlobalSaveSettings, AutoCaller &autoCaller)
+{
+    // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
+    AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
+                                  this->lockHandle()
+                                  COMMA_LOCKVAL_SRC_POS);
+
+    LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str()));
+
+    bool wasCreated = true;
+
+    switch (m->state)
+    {
+        case MediumState_NotCreated:
+            wasCreated = false;
+            break;
+        case MediumState_Created:
+        case MediumState_Inaccessible:
+            break;
+        default:
+            return setStateError();
+    }
+
+    if (m->backRefs.size() != 0)
+        return setError(VBOX_E_OBJECT_IN_USE,
+                        tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
+                        m->strLocationFull.c_str(), m->backRefs.size());
+
+    // perform extra media-dependent close checks
+    HRESULT rc = canClose();
+    if (FAILED(rc)) return rc;
+
+    if (wasCreated)
+    {
+        // remove from the list of known media before performing actual
+        // uninitialization (to keep the media registry consistent on
+        // failure to do so)
+        rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings);
+        if (FAILED(rc)) return rc;
+    }
+
+    // leave the AutoCaller, as otherwise uninit() will simply hang
+    autoCaller.release();
+
+    // Keep the locks held until after uninit, as otherwise the consistency
+    // of the medium tree cannot be guaranteed.
+    uninit();
+
+    LogFlowFuncLeave();
+
+    return rc;
+}
+
+/**
+ * Deletes the medium storage unit.
+ *
+ * If @a aProgress is not NULL but the object it points to is @c null then a new
+ * progress object will be created and assigned to @a *aProgress on success,
+ * otherwise the existing progress object is used. If Progress is NULL, then no
+ * progress object is created/used at all.
+ *
+ * When @a aWait is @c false, this method will create a thread to perform the
+ * delete operation asynchronously and will return immediately. Otherwise, it
+ * will perform the operation on the calling thread and will not return to the
+ * caller until the operation is completed. Note that @a aProgress cannot be
+ * NULL when @a aWait is @c false (this method will assert in this case).
+ *
+ * @param aProgress     Where to find/store a Progress object to track operation
+ *                      completion.
+ * @param aWait         @c true if this method should block instead of creating
+ *                      an asynchronous thread.
+ * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
+ *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
+ *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
+ *                and this parameter is ignored.
+ *
+ * @note Locks mVirtualBox and this object for writing. Locks medium tree for
+ *       writing.
+ */
+HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
+                              bool aWait,
+                              bool *pfNeedsGlobalSaveSettings)
+{
+    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    HRESULT rc = S_OK;
+    ComObjPtr<Progress> pProgress;
+    Medium::Task *pTask = NULL;
+
+    try
+    {
+        /* we're accessing the media tree, and canClose() needs it too */
+        AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
+                                      this->lockHandle()
+                                      COMMA_LOCKVAL_SRC_POS);
+        LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
+
+        if (    !(m->formatObj->getCapabilities() & (   MediumFormatCapabilities_CreateDynamic
+                                                      | MediumFormatCapabilities_CreateFixed)))
+            throw setError(VBOX_E_NOT_SUPPORTED,
+                           tr("Medium format '%s' does not support storage deletion"),
+                           m->strFormat.c_str());
+
+        /* Note that we are fine with Inaccessible state too: a) for symmetry
+         * with create calls and b) because it doesn't really harm to try, if
+         * it is really inaccessible, the delete operation will fail anyway.
+         * Accepting Inaccessible state is especially important because all
+         * registered media are initially Inaccessible upon VBoxSVC startup
+         * until COMGETTER(RefreshState) is called. Accept Deleting state
+         * because some callers need to put the medium in this state early
+         * to prevent races. */
+        switch (m->state)
+        {
+            case MediumState_Created:
+            case MediumState_Deleting:
+            case MediumState_Inaccessible:
+                break;
+            default:
+                throw setStateError();
+        }
+
+        if (m->backRefs.size() != 0)
+        {
+            Utf8Str strMachines;
+            for (BackRefList::const_iterator it = m->backRefs.begin();
+                it != m->backRefs.end();
+                ++it)
+            {
+                const BackRef &b = *it;
+                if (strMachines.length())
+                    strMachines.append(", ");
+                strMachines.append(b.machineId.toString().c_str());
+            }
+#ifdef DEBUG
+            dumpBackRefs();
+#endif
+            throw setError(VBOX_E_OBJECT_IN_USE,
+                           tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
+                           m->strLocationFull.c_str(),
+                           m->backRefs.size(),
+                           strMachines.c_str());
+        }
+
+        rc = canClose();
+        if (FAILED(rc))
+            throw rc;
+
+        /* go to Deleting state, so that the medium is not actually locked */
+        if (m->state != MediumState_Deleting)
+        {
+            rc = markForDeletion();
+            if (FAILED(rc))
+                throw rc;
+        }
+
+        /* Build the medium lock list. */
+        MediumLockList *pMediumLockList(new MediumLockList());
+        rc = createMediumLockList(true /* fFailIfInaccessible */,
+                                  true /* fMediumLockWrite */,
+                                  NULL,
+                                  *pMediumLockList);
+        if (FAILED(rc))
+        {
+            delete pMediumLockList;
+            throw rc;
+        }
+
+        rc = pMediumLockList->Lock();
+        if (FAILED(rc))
+        {
+            delete pMediumLockList;
+            throw setError(rc,
+                           tr("Failed to lock media when deleting '%s'"),
+                           getLocationFull().c_str());
+        }
+
+        /* try to remove from the list of known media before performing
+         * actual deletion (we favor the consistency of the media registry
+         * which would have been broken if unregisterWithVirtualBox() failed
+         * after we successfully deleted the storage) */
+        rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings);
+        if (FAILED(rc))
+            throw rc;
+        // no longer need lock
+        multilock.release();
+
+        if (aProgress != NULL)
+        {
+            /* use the existing progress object... */
+            pProgress = *aProgress;
+
+            /* ...but create a new one if it is null */
+            if (pProgress.isNull())
+            {
+                pProgress.createObject();
+                rc = pProgress->init(m->pVirtualBox,
+                                     static_cast<IMedium*>(this),
+                                     BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
+                                     FALSE /* aCancelable */);
+                if (FAILED(rc))
+                    throw rc;
+            }
+        }
+
+        /* setup task object to carry out the operation sync/async */
+        pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
+        rc = pTask->rc();
+        AssertComRC(rc);
+        if (FAILED(rc))
+            throw rc;
+    }
+    catch (HRESULT aRC) { rc = aRC; }
+
+    if (SUCCEEDED(rc))
+    {
+        if (aWait)
+            rc = runNow(pTask, NULL /* pfNeedsGlobalSaveSettings*/);
+        else
+            rc = startThread(pTask);
+
+        if (SUCCEEDED(rc) && aProgress != NULL)
+            *aProgress = pProgress;
+
+    }
+    else
+    {
+        if (pTask)
+            delete pTask;
+
+        /* Undo deleting state if necessary. */
+        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+        unmarkForDeletion();
+    }
+
+    return rc;
+}
+
+/**
+ * Mark a medium for deletion.
+ *
+ * @note Caller must hold the write lock on this medium!
+ */
+HRESULT Medium::markForDeletion()
+{
+    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
+    switch (m->state)
+    {
+        case MediumState_Created:
+        case MediumState_Inaccessible:
+            m->preLockState = m->state;
+            m->state = MediumState_Deleting;
+            return S_OK;
+        default:
+            return setStateError();
+    }
+}
+
+/**
+ * Removes the "mark for deletion".
+ *
+ * @note Caller must hold the write lock on this medium!
+ */
+HRESULT Medium::unmarkForDeletion()
+{
+    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
+    switch (m->state)
+    {
+        case MediumState_Deleting:
+            m->state = m->preLockState;
+            return S_OK;
+        default:
+            return setStateError();
+    }
+}
+
+/**
+ * Mark a medium for deletion which is in locked state.
+ *
+ * @note Caller must hold the write lock on this medium!
+ */
+HRESULT Medium::markLockedForDeletion()
+{
+    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
+    if (   (   m->state == MediumState_LockedRead
+            || m->state == MediumState_LockedWrite)
+        && m->preLockState == MediumState_Created)
+    {
+        m->preLockState = MediumState_Deleting;
+        return S_OK;
+    }
+    else
+        return setStateError();
+}
+
+/**
+ * Removes the "mark for deletion" for a medium in locked state.
+ *
+ * @note Caller must hold the write lock on this medium!
+ */
+HRESULT Medium::unmarkLockedForDeletion()
+{
+    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
+    if (   (   m->state == MediumState_LockedRead
+            || m->state == MediumState_LockedWrite)
+        && m->preLockState == MediumState_Deleting)
+    {
+        m->preLockState = MediumState_Created;
+        return S_OK;
+    }
+    else
+        return setStateError();
+}
+
+/**
+ * Prepares this (source) medium, target medium and all intermediate media
+ * for the merge operation.
+ *
+ * This method is to be called prior to calling the #mergeTo() to perform
+ * necessary consistency checks and place involved media to appropriate
+ * states. If #mergeTo() is not called or fails, the state modifications
+ * performed by this method must be undone by #cancelMergeTo().
+ *
+ * See #mergeTo() for more information about merging.
+ *
+ * @param pTarget       Target medium.
+ * @param aMachineId    Allowed machine attachment. NULL means do not check.
+ * @param aSnapshotId   Allowed snapshot attachment. NULL or empty UUID means
+ *                      do not check.
+ * @param fLockMedia    Flag whether to lock the medium lock list or not.
+ *                      If set to false and the medium lock list locking fails
+ *                      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 aMediumLockList Medium locking information (out).
+ *
+ * @note Locks medium tree for reading. Locks this object, aTarget and all
+ *       intermediate media for writing.
+ */
+HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
+                               const Guid *aMachineId,
+                               const Guid *aSnapshotId,
+                               bool fLockMedia,
+                               bool &fMergeForward,
+                               ComObjPtr<Medium> &pParentForTarget,
+                               MediaList &aChildrenToReparent,
+                               MediumLockList * &aMediumLockList)
+{
+    AssertReturn(pTarget != NULL, E_FAIL);
+    AssertReturn(pTarget != this, E_FAIL);
+
+    AutoCaller autoCaller(this);
+    AssertComRCReturnRC(autoCaller.rc());
+
+    AutoCaller targetCaller(pTarget);
+    AssertComRCReturnRC(targetCaller.rc());
+
+    HRESULT rc = S_OK;
+    fMergeForward = false;
+    pParentForTarget.setNull();
+    aChildrenToReparent.clear();
+    Assert(aMediumLockList == NULL);
+    aMediumLockList = NULL;
+
+    try
+    {
+        // locking: we need the tree lock first because we access parent pointers
+        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
+
+        /* more sanity checking and figuring out the merge direction */
+        ComObjPtr<Medium> pMedium = getParent();
+        while (!pMedium.isNull() && pMedium != pTarget)
+            pMedium = pMedium->getParent();
+        if (pMedium == pTarget)
+            fMergeForward = false;
+        else
+        {
+            pMedium = pTarget->getParent();
+            while (!pMedium.isNull() && pMedium != this)
+                pMedium = pMedium->getParent();
+            if (pMedium == this)
+                fMergeForward = true;
+            else
+            {
+                Utf8Str tgtLoc;
+                {
+                    AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
+                    tgtLoc = pTarget->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());
+            }
+        }
+
+        /* Build the lock list. */
+        aMediumLockList = new MediumLockList();
+        if (fMergeForward)
+            rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
+                                               true /* fMediumLockWrite */,
+                                               NULL,
+                                               *aMediumLockList);
+        else
+            rc = createMediumLockList(true /* fFailIfInaccessible */,
+                                      false /* fMediumLockWrite */,
+                                      NULL,
+                                      *aMediumLockList);
+        if (FAILED(rc))
+            throw rc;
+
+        /* Sanity checking, must be after lock list creation as it depends on
+         * valid medium states. The medium objects must be accessible. Only
+         * do this if immediate locking is requested, otherwise it fails when
+         * we construct a medium lock list for an already running VM. Snapshot
+         * deletion uses this to simplify its life. */
+        if (fLockMedia)
+        {
+            {
+                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+                if (m->state != MediumState_Created)
+                    throw setStateError();
+            }
+            {
+                AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
+                if (pTarget->m->state != MediumState_Created)
+                    throw pTarget->setStateError();
+            }
+        }
+
+        /* check medium attachment and other sanity conditions */
+        if (fMergeForward)
+        {
+            AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+            if (getChildren().size() > 1)
+            {
+                throw setError(VBOX_E_INVALID_OBJECT_STATE,
+                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
+                               m->strLocationFull.c_str(), getChildren().size());
+            }
+            /* One backreference is only allowed if the machine ID is not empty
+             * and it matches the machine the medium is attached to (including
+             * the snapshot ID if not empty). */
+            if (   m->backRefs.size() != 0
+                && (   !aMachineId
+                    || m->backRefs.size() != 1
+                    || aMachineId->isEmpty()
+                    || *getFirstMachineBackrefId() != *aMachineId
+                    || (   (!aSnapshotId || !aSnapshotId->isEmpty())
+                        && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
+                throw setError(VBOX_E_OBJECT_IN_USE,
+                               tr("Medium '%s' is attached to %d virtual machines"),
+                               m->strLocationFull.c_str(), m->backRefs.size());
+            if (m->type == MediumType_Immutable)
+                throw setError(VBOX_E_INVALID_OBJECT_STATE,
+                               tr("Medium '%s' is immutable"),
+                               m->strLocationFull.c_str());
+        }
+        else
+        {
+            AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
+            if (pTarget->getChildren().size() > 1)
+            {
+                throw setError(VBOX_E_OBJECT_IN_USE,
+                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
+                               pTarget->m->strLocationFull.c_str(),
+                               pTarget->getChildren().size());
+            }
+            if (pTarget->m->type == MediumType_Immutable)
+                throw setError(VBOX_E_INVALID_OBJECT_STATE,
+                               tr("Medium '%s' is immutable"),
+                               pTarget->m->strLocationFull.c_str());
+        }
+        ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
+        ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
+        for (pLast = pLastIntermediate;
+             !pLast.isNull() && pLast != pTarget && pLast != this;
+             pLast = pLast->getParent())
+        {
+            AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
+            if (pLast->getChildren().size() > 1)
+            {
+                throw setError(VBOX_E_OBJECT_IN_USE,
+                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
+                               pLast->m->strLocationFull.c_str(),
+                               pLast->getChildren().size());
+            }
+            if (pLast->m->backRefs.size() != 0)
+                throw setError(VBOX_E_OBJECT_IN_USE,
+                               tr("Medium '%s' is attached to %d virtual machines"),
+                               pLast->m->strLocationFull.c_str(),
+                               pLast->m->backRefs.size());
+
+        }
+
+        /* Update medium states appropriately */
+        if (m->state == MediumState_Created)
+        {
+            rc = markForDeletion();
+            if (FAILED(rc))
+                throw rc;
+        }
+        else
+        {
+            if (fLockMedia)
+                throw setStateError();
+            else if (   m->state == MediumState_LockedWrite
+                     || m->state == MediumState_LockedRead)
+            {
+                /* Either mark it for deletion in locked state or allow
+                 * others to have done so. */
+                if (m->preLockState == MediumState_Created)
+                    markLockedForDeletion();
+                else if (m->preLockState != MediumState_Deleting)
+                    throw setStateError();
+            }
+            else
+                throw setStateError();
+        }
+
+        if (fMergeForward)
+        {
+            /* we will need parent to reparent target */
+            pParentForTarget = m->pParent;
+        }
+        else
+        {
+            /* we will need to reparent children of the source */
+            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);
+            }
+        }
+        for (pLast = pLastIntermediate;
+             !pLast.isNull() && pLast != pTarget && pLast != this;
+             pLast = pLast->getParent())
+        {
+            AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
+            if (pLast->m->state == MediumState_Created)
+            {
+                rc = pLast->markForDeletion();
+                if (FAILED(rc))
+                    throw rc;
+            }
+            else
+                throw pLast->setStateError();
+        }
+
+        /* Tweak the lock list in the backward merge case, as the target
+         * isn't marked to be locked for writing yet. */
+        if (!fMergeForward)
+        {
+            MediumLockList::Base::iterator lockListBegin =
+                aMediumLockList->GetBegin();
+            MediumLockList::Base::iterator lockListEnd =
+                aMediumLockList->GetEnd();
+            lockListEnd--;
+            for (MediumLockList::Base::iterator it = lockListBegin;
+                 it != lockListEnd;
+                 ++it)
+            {
+                MediumLock &mediumLock = *it;
+                if (mediumLock.GetMedium() == pTarget)
+                {
+                    HRESULT rc2 = mediumLock.UpdateLock(true);
+                    AssertComRC(rc2);
+                    break;
+                }
+            }
+        }
+
+        if (fLockMedia)
+        {
+            rc = aMediumLockList->Lock();
+            if (FAILED(rc))
+            {
+                AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
+                throw setError(rc,
+                               tr("Failed to lock media when merging to '%s'"),
+                               pTarget->getLocationFull().c_str());
+            }
+        }
+    }
+    catch (HRESULT aRC) { rc = aRC; }
+
+    if (FAILED(rc))
+    {
+        delete aMediumLockList;
+        aMediumLockList = NULL;
+    }
+
+    return rc;
+}
+
+/**
+ * Merges this medium to the specified medium which must be either its
+ * direct ancestor or descendant.
+ *
+ * Given this medium is SOURCE and the specified medium is TARGET, we will
+ * get two variants of the merge operation:
+ *
+ *                forward merge
+ *                ------------------------->
+ *  [Extra] <- SOURCE <- Intermediate <- TARGET
+ *  Any        Del       Del             LockWr
+ *
+ *
+ *                            backward merge
+ *                <-------------------------
+ *             TARGET <- Intermediate <- SOURCE <- [Extra]
+ *             LockWr    Del             Del       LockWr
+ *
+ * Each diagram shows the involved media on the media chain where
+ * SOURCE and TARGET belong. Under each medium there is a state value which
+ * the medium must have at a time of the mergeTo() call.
+ *
+ * The media in the square braces may be absent (e.g. when the forward
+ * operation takes place and SOURCE is the base medium, or when the backward
+ * merge operation takes place and TARGET is the last child in the chain) but if
+ * they present they are involved too as shown.
+ *
+ * Neither the source medium nor intermediate media may be attached to
+ * any VM directly or in the snapshot, otherwise this method will assert.
+ *
+ * The #prepareMergeTo() method must be called prior to this method to place all
+ * involved to necessary states and perform other consistency checks.
+ *
+ * If @a aWait is @c true then this method will perform the operation on the
+ * calling thread and will not return to the caller until the operation is
+ * completed. When this method succeeds, all intermediate medium objects in
+ * the chain will be uninitialized, the state of the target medium (and all
+ * involved extra media) will be restored. @a aMediumLockList will not be
+ * deleted, whether the operation is successful or not. The caller has to do
+ * this if appropriate. Note that this (source) medium is not uninitialized
+ * because of possible AutoCaller instances held by the caller of this method
+ * on the current thread. It's therefore the responsibility of the caller to
+ * call Medium::uninit() after releasing all callers.
+ *
+ * If @a aWait is @c false then this method will create a thread to perform the
+ * operation asynchronously and will return immediately. If the operation
+ * succeeds, the thread will uninitialize the source medium object and all
+ * intermediate medium objects in the chain, reset the state of the target
+ * medium (and all involved extra media) and delete @a aMediumLockList.
+ * If the operation fails, the thread will only reset the states of all
+ * involved media and delete @a aMediumLockList.
+ *
+ * When this method fails (regardless of the @a aWait mode), it is a caller's
+ * responsibility to undo state changes and delete @a aMediumLockList using
+ * #cancelMergeTo().
+ *
+ * If @a aProgress is not NULL but the object it points to is @c null then a new
+ * progress object will be created and assigned to @a *aProgress on success,
+ * otherwise the existing progress object is used. If Progress is NULL, then no
+ * progress object is created/used at all. Note that @a aProgress cannot be
+ * NULL when @a aWait is @c false (this method will assert in this case).
+ *
+ * @param pTarget       Target medium.
+ * @param fMergeForward Merge direction.
+ * @param pParentForTarget New parent for target medium after merge.
+ * @param aChildrenToReparent List of children of the source which will have
+ *                      to be reparented to the target after merge.
+ * @param aMediumLockList Medium locking information.
+ * @param aProgress     Where to find/store a Progress object to track operation
+ *                      completion.
+ * @param aWait         @c true if this method should block instead of creating
+ *                      an asynchronous thread.
+ * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
+ *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
+ *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
+ *                and this parameter is ignored.
+ *
+ * @note Locks the tree lock for writing. Locks the media from the chain
+ *       for writing.
+ */
+HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
+                        bool fMergeForward,
+                        const ComObjPtr<Medium> &pParentForTarget,
+                        const MediaList &aChildrenToReparent,
+                        MediumLockList *aMediumLockList,
+                        ComObjPtr <Progress> *aProgress,
+                        bool aWait,
+                        bool *pfNeedsGlobalSaveSettings)
+{
+    AssertReturn(pTarget != NULL, E_FAIL);
+    AssertReturn(pTarget != this, E_FAIL);
+    AssertReturn(aMediumLockList != NULL, E_FAIL);
+    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoCaller targetCaller(pTarget);
+    AssertComRCReturnRC(targetCaller.rc());
+
+    HRESULT rc = S_OK;
+    ComObjPtr <Progress> pProgress;
+    Medium::Task *pTask = NULL;
+
+    try
+    {
+        if (aProgress != NULL)
+        {
+            /* use the existing progress object... */
+            pProgress = *aProgress;
+
+            /* ...but create a new one if it is null */
+            if (pProgress.isNull())
+            {
+                Utf8Str tgtName;
+                {
+                    AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
+                    tgtName = pTarget->getName();
+                }
+
+                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+                pProgress.createObject();
+                rc = pProgress->init(m->pVirtualBox,
+                                     static_cast<IMedium*>(this),
+                                     BstrFmt(tr("Merging medium '%s' to '%s'"),
+                                             getName().c_str(),
+                                             tgtName.c_str()).raw(),
+                                     TRUE /* aCancelable */);
+                if (FAILED(rc))
+                    throw rc;
+            }
+        }
+
+        /* setup task object to carry out the operation sync/async */
+        pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
+                                      pParentForTarget, aChildrenToReparent,
+                                      pProgress, aMediumLockList,
+                                      aWait /* fKeepMediumLockList */);
+        rc = pTask->rc();
+        AssertComRC(rc);
+        if (FAILED(rc))
+            throw rc;
+    }
+    catch (HRESULT aRC) { rc = aRC; }
+
+    if (SUCCEEDED(rc))
+    {
+        if (aWait)
+            rc = runNow(pTask, pfNeedsGlobalSaveSettings);
+        else
+            rc = startThread(pTask);
+
+        if (SUCCEEDED(rc) && aProgress != NULL)
+            *aProgress = pProgress;
+    }
+    else if (pTask != NULL)
+        delete pTask;
+
+    return rc;
+}
+
+/**
+ * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
+ * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
+ * the medium objects in @a aChildrenToReparent.
+ *
+ * @param aChildrenToReparent List of children of the source which will have
+ *                      to be reparented to the target after merge.
+ * @param aMediumLockList Medium locking information.
+ *
+ * @note Locks the media from the chain for writing.
+ */
+void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
+                           MediumLockList *aMediumLockList)
+{
+    AutoCaller autoCaller(this);
+    AssertComRCReturnVoid(autoCaller.rc());
+
+    AssertReturnVoid(aMediumLockList != NULL);
+
+    /* Revert media marked for deletion to previous state. */
+    HRESULT rc;
+    MediumLockList::Base::const_iterator mediumListBegin =
+        aMediumLockList->GetBegin();
+    MediumLockList::Base::const_iterator mediumListEnd =
+        aMediumLockList->GetEnd();
+    for (MediumLockList::Base::const_iterator it = mediumListBegin;
+         it != mediumListEnd;
+         ++it)
+    {
+        const MediumLock &mediumLock = *it;
+        const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
+        AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
+
+        if (pMedium->m->state == MediumState_Deleting)
+        {
+            rc = pMedium->unmarkForDeletion();
+            AssertComRC(rc);
+        }
+    }
+
+    /* 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);
+    }
+}
+
+/**
+ * Fix the parent UUID of all children to point to this medium as their
+ * parent.
+ */
+HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
+{
+    MediumLockList mediumLockList;
+    HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
+                                      false /* fMediumLockWrite */,
+                                      this,
+                                      mediumLockList);
+    AssertComRCReturnRC(rc);
+
+    try
+    {
+        PVBOXHDD hdd;
+        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
+        ComAssertRCThrow(vrc, E_FAIL);
+
+        try
+        {
+            MediumLockList::Base::iterator lockListBegin =
+                mediumLockList.GetBegin();
+            MediumLockList::Base::iterator lockListEnd =
+                mediumLockList.GetEnd();
+            for (MediumLockList::Base::iterator it = lockListBegin;
+                 it != lockListEnd;
+                 ++it)
+            {
+                MediumLock &mediumLock = *it;
+                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
+                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
+
+                // open the medium
+                vrc = VDOpen(hdd,
+                             pMedium->m->strFormat.c_str(),
+                             pMedium->m->strLocationFull.c_str(),
+                             VD_OPEN_FLAGS_READONLY,
+                             pMedium->m->vdImageIfaces);
+                if (RT_FAILURE(vrc))
+                    throw vrc;
+            }
+
+            for (MediaList::const_iterator it = childrenToReparent.begin();
+                 it != childrenToReparent.end();
+                 ++it)
+            {
+                /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
+                vrc = VDOpen(hdd,
+                             (*it)->m->strFormat.c_str(),
+                             (*it)->m->strLocationFull.c_str(),
+                             VD_OPEN_FLAGS_INFO,
+                             (*it)->m->vdImageIfaces);
+                if (RT_FAILURE(vrc))
+                    throw vrc;
+
+                vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
+                if (RT_FAILURE(vrc))
+                    throw vrc;
+
+                vrc = VDClose(hdd, false /* fDelete */);
+                if (RT_FAILURE(vrc))
+                    throw vrc;
+
+                (*it)->UnlockWrite(NULL);
+            }
+        }
+        catch (HRESULT aRC) { rc = aRC; }
+        catch (int aVRC)
+        {
+            throw setError(E_FAIL,
+                            tr("Could not update medium UUID references to parent '%s' (%s)"),
+                            m->strLocationFull.c_str(),
+                            vdError(aVRC).c_str());
+        }
+
+        VDDestroy(hdd);
+    }
+    catch (HRESULT aRC) { rc = aRC; }
+
+    return rc;
+}
+
+/**
+ * Used by IAppliance to export disk images.
+ *
+ * @param aFilename             Filename to create (UTF8).
+ * @param aFormat               Medium format for creating @a aFilename.
+ * @param aVariant              Which exact image format variant to use
+ *                              for the destination image.
+ * @param aVDImageIOCallbacks   Pointer to the callback table for a
+ *                              VDINTERFACEIO interface. May be NULL.
+ * @param aVDImageIOUser        Opaque data for the callbacks.
+ * @param aProgress             Progress object to use.
  * @return
- */
-MediumType_T Medium::getType() const
-{
-    return m->type;
-}
-
-/**
- * Returns a short version of the location attribute.
- *
- * @note Must be called from under this object's read or write lock.
- */
-Utf8Str Medium::getName()
-{
-    Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
-    return name;
-}
-
-/**
- * This adds the given UUID to the list of media registries in which this
- * medium should be registered. The UUID can either be a machine UUID,
- * to add a machine registry, or the global registry UUID as returned by
- * VirtualBox::getGlobalRegistryId().
- *
- * Note that for hard disks, this method does nothing if the medium is
- * already in another registry to avoid having hard disks in more than
- * one registry, which causes trouble with keeping diff images in sync.
- * See getFirstRegistryMachineId() for details.
- *
- * @param id
- * @param pfNeedsSaveSettings If != NULL, is set to true if a new reference was added and saveSettings for either the machine or global XML is needed.
- * @return true if the registry was added.
- */
-bool Medium::addRegistry(const Guid& id,
-                         bool *pfNeedsSaveSettings)
-{
+ * @note The source format is defined by the Medium instance.
+ */
+HRESULT Medium::exportFile(const char *aFilename,
+                           const ComObjPtr<MediumFormat> &aFormat,
+                           MediumVariant_T aVariant,
+                           void *aVDImageIOCallbacks, void *aVDImageIOUser,
+                           const ComObjPtr<Progress> &aProgress)
+{
+    AssertPtrReturn(aFilename, E_INVALIDARG);
+    AssertReturn(!aFormat.isNull(), E_INVALIDARG);
+    AssertReturn(!aProgress.isNull(), E_INVALIDARG);
+
     AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return false;
-
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    HRESULT rc = S_OK;
+    Medium::Task *pTask = NULL;
+
+    try
+    {
+        // locking: we need the tree lock first because we access parent pointers
+        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
+        // and we need to write-lock the media involved
+        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+        /* Build the source lock list. */
+        MediumLockList *pSourceMediumLockList(new MediumLockList());
+        rc = createMediumLockList(true /* fFailIfInaccessible */,
+                                  false /* fMediumLockWrite */,
+                                  NULL,
+                                  *pSourceMediumLockList);
+        if (FAILED(rc))
+        {
+            delete pSourceMediumLockList;
+            throw rc;
+        }
+
+        rc = pSourceMediumLockList->Lock();
+        if (FAILED(rc))
+        {
+            delete pSourceMediumLockList;
+            throw setError(rc,
+                           tr("Failed to lock source media '%s'"),
+                           getLocationFull().c_str());
+        }
+
+        /* setup task object to carry out the operation asynchronously */
+        pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
+                                       aVariant, aVDImageIOCallbacks,
+                                       aVDImageIOUser, pSourceMediumLockList);
+        rc = pTask->rc();
+        AssertComRC(rc);
+        if (FAILED(rc))
+            throw rc;
+    }
+    catch (HRESULT aRC) { rc = aRC; }
+
+    if (SUCCEEDED(rc))
+        rc = startThread(pTask);
+    else if (pTask != NULL)
+        delete pTask;
+
+    return rc;
+}
+
+/**
+ * Used by IAppliance to import disk images.
+ *
+ * @param aFilename             Filename to read (UTF8).
+ * @param aFormat               Medium format for reading @a aFilename.
+ * @param aVariant              Which exact image format variant to use
+ *                              for the destination image.
+ * @param aVDImageIOCallbacks   Pointer to the callback table for a
+ *                              VDINTERFACEIO interface. May be NULL.
+ * @param aVDImageIOUser        Opaque data for the callbacks.
+ * @param aParent               Parent medium. May be NULL.
+ * @param aProgress             Progress object to use.
+ * @return
+ * @note The destination format is defined by the Medium instance.
+ */
+HRESULT Medium::importFile(const char *aFilename,
+                           const ComObjPtr<MediumFormat> &aFormat,
+                           MediumVariant_T aVariant,
+                           void *aVDImageIOCallbacks, void *aVDImageIOUser,
+                           const ComObjPtr<Medium> &aParent,
+                           const ComObjPtr<Progress> &aProgress)
+{
+    AssertPtrReturn(aFilename, E_INVALIDARG);
+    AssertReturn(!aFormat.isNull(), E_INVALIDARG);
+    AssertReturn(!aProgress.isNull(), E_INVALIDARG);
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    HRESULT rc = S_OK;
+    Medium::Task *pTask = NULL;
+
+    try
+    {
+        // locking: we need the tree lock first because we access parent pointers
+        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
+        // and we need to write-lock the media involved
+        AutoMultiWriteLock2 alock(this, aParent COMMA_LOCKVAL_SRC_POS);
+
+        if (   m->state != MediumState_NotCreated
+            && m->state != MediumState_Created)
+            throw setStateError();
+
+        /* Build the target lock list. */
+        MediumLockList *pTargetMediumLockList(new MediumLockList());
+        rc = createMediumLockList(true /* fFailIfInaccessible */,
+                                  true /* fMediumLockWrite */,
+                                  aParent,
+                                  *pTargetMediumLockList);
+        if (FAILED(rc))
+        {
+            delete pTargetMediumLockList;
+            throw rc;
+        }
+
+        rc = pTargetMediumLockList->Lock();
+        if (FAILED(rc))
+        {
+            delete pTargetMediumLockList;
+            throw setError(rc,
+                           tr("Failed to lock target media '%s'"),
+                           getLocationFull().c_str());
+        }
+
+        /* setup task object to carry out the operation asynchronously */
+        pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,
+                                       aVariant, aVDImageIOCallbacks,
+                                       aVDImageIOUser, aParent,
+                                       pTargetMediumLockList);
+        rc = pTask->rc();
+        AssertComRC(rc);
+        if (FAILED(rc))
+            throw rc;
+
+        if (m->state == MediumState_NotCreated)
+            m->state = MediumState_Creating;
+    }
+    catch (HRESULT aRC) { rc = aRC; }
+
+    if (SUCCEEDED(rc))
+        rc = startThread(pTask);
+    else if (pTask != NULL)
+        delete pTask;
+
+    return rc;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Private methods
+//
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Queries information from the medium.
+ *
+ * As a result of this call, the accessibility state and data members such as
+ * size and description will be updated with the current information.
+ *
+ * @note This method may block during a system I/O call that checks storage
+ *       accessibility.
+ *
+ * @note Locks medium tree for reading and writing (for new diff media checked
+ *       for the first time). Locks mParent for reading. Locks this object for
+ *       writing.
+ *
+ * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
+ * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs())
+ * @return
+ */
+HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
+{
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-    if (    m->devType == DeviceType_HardDisk
-         && m->llRegistryIDs.size() > 0
+    if (   m->state != MediumState_Created
+        && m->state != MediumState_Inaccessible
+        && m->state != MediumState_LockedRead)
+        return E_FAIL;
+
+    HRESULT rc = S_OK;
+
+    int vrc = VINF_SUCCESS;
+
+    /* check if a blocking queryInfo() call is in progress on some other thread,
+     * and wait for it to finish if so instead of querying data ourselves */
+    if (m->queryInfoRunning)
+    {
+        Assert(   m->state == MediumState_LockedRead
+               || m->state == MediumState_LockedWrite);
+
+        alock.leave();
+        vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
+        alock.enter();
+
+        AssertRC(vrc);
+
+        return S_OK;
+    }
+
+    bool success = false;
+    Utf8Str lastAccessError;
+
+    /* are we dealing with a new medium constructed using the existing
+     * location? */
+    bool isImport = m->id.isEmpty();
+    unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
+
+    /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
+     * media because that would prevent necessary modifications
+     * when opening media of some third-party formats for the first
+     * time in VirtualBox (such as VMDK for which VDOpen() needs to
+     * generate an UUID if it is missing) */
+    if (    (m->hddOpenMode == OpenReadOnly)
+         || m->type == MediumType_Readonly
+         || !isImport
        )
-        return false;
-
-    // no need to add the UUID twice
-    for (GuidList::const_iterator it = m->llRegistryIDs.begin();
-         it != m->llRegistryIDs.end();
-         ++it)
-    {
-        if ((*it) == id)
-            return false;
-    }
-
-    m->llRegistryIDs.push_back(id);
-    if (pfNeedsSaveSettings)
-        *pfNeedsSaveSettings = true;
-    return true;
-}
-
-/**
- * Returns true if id is in the list of media registries for this medium.
- * @param id
- * @return
- */
-bool Medium::isInRegistry(const Guid& id)
-{
-    AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return false;
-
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    for (GuidList::const_iterator it = m->llRegistryIDs.begin();
-         it != m->llRegistryIDs.end();
-         ++it)
-    {
-        if (*it == id)
-            return true;
-    }
-
-    return false;
-}
-
-/**
- * Internal method to return the medium's first registry machine (i.e. the machine in whose
- * machine XML this medium is listed).
- *
- * Every medium must now (4.0) reside in at least one media registry, which is identified by
- * a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
- * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
- * object if the machine is old and still needs the global registry in VirtualBox.xml.
- *
- * By definition, hard disks may only be in one media registry, in which all its children
- * will be stored as well. Otherwise we run into problems with having keep multiple registries
- * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
- * case, only VM2's registry is used for the disk in question.)
- *
- * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
- * the user.
- *
- * Must have caller + locking!
- *
- * @return
- */
-const Guid& Medium::getFirstRegistryMachineId() const
-{
-    return m->llRegistryIDs.front();
+        uOpenFlags |= VD_OPEN_FLAGS_READONLY;
+
+    /* Open shareable medium with the appropriate flags */
+    if (m->type == MediumType_Shareable)
+        uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
+
+    /* Lock the medium, which makes the behavior much more consistent */
+    if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
+        rc = LockRead(NULL);
+    else
+        rc = LockWrite(NULL);
+    if (FAILED(rc)) return rc;
+
+    /* Copies of the input state fields which are not read-only,
+     * as we're dropping the lock. CAUTION: be extremely careful what
+     * you do with the contents of this medium object, as you will
+     * create races if there are concurrent changes. */
+    Utf8Str format(m->strFormat);
+    Utf8Str location(m->strLocationFull);
+    ComObjPtr<MediumFormat> formatObj = m->formatObj;
+
+    /* "Output" values which can't be set because the lock isn't held
+     * at the time the values are determined. */
+    Guid mediumId = m->id;
+    uint64_t mediumSize = 0;
+    uint64_t mediumLogicalSize = 0;
+
+    /* Flag whether a base image has a non-zero parent UUID and thus
+     * need repairing after it was closed again. */
+    bool fRepairImageZeroParentUuid = false;
+
+    /* leave the lock before a lengthy operation */
+    vrc = RTSemEventMultiReset(m->queryInfoSem);
+    AssertRCReturn(vrc, E_FAIL);
+    m->queryInfoRunning = true;
+    alock.leave();
+
+    try
+    {
+        /* skip accessibility checks for host drives */
+        if (m->hostDrive)
+        {
+            success = true;
+            throw S_OK;
+        }
+
+        PVBOXHDD hdd;
+        vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
+        ComAssertRCThrow(vrc, E_FAIL);
+
+        try
+        {
+            /** @todo This kind of opening of media is assuming that diff
+             * media can be opened as base media. Should be documented that
+             * it must work for all medium format backends. */
+            vrc = VDOpen(hdd,
+                         format.c_str(),
+                         location.c_str(),
+                         uOpenFlags,
+                         m->vdImageIfaces);
+            if (RT_FAILURE(vrc))
+            {
+                lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
+                                             location.c_str(), vdError(vrc).c_str());
+                throw S_OK;
+            }
+
+            if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
+            {
+                /* Modify the UUIDs if necessary. The associated fields are
+                 * not modified by other code, so no need to copy. */
+                if (fSetImageId)
+                {
+                    vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
+                    ComAssertRCThrow(vrc, E_FAIL);
+                }
+                if (fSetParentId)
+                {
+                    vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
+                    ComAssertRCThrow(vrc, E_FAIL);
+                }
+                /* zap the information, these are no long-term members */
+                unconst(m->uuidImage).clear();
+                unconst(m->uuidParentImage).clear();
+
+                /* check the UUID */
+                RTUUID uuid;
+                vrc = VDGetUuid(hdd, 0, &uuid);
+                ComAssertRCThrow(vrc, E_FAIL);
+
+                if (isImport)
+                {
+                    mediumId = uuid;
+
+                    if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
+                        // only when importing a VDMK that has no UUID, create one in memory
+                        mediumId.create();
+                }
+                else
+                {
+                    Assert(!mediumId.isEmpty());
+
+                    if (mediumId != uuid)
+                    {
+                        lastAccessError = Utf8StrFmt(
+                                tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
+                                &uuid,
+                                location.c_str(),
+                                mediumId.raw(),
+                                m->pVirtualBox->settingsFilePath().c_str());
+                        throw S_OK;
+                    }
+                }
+            }
+            else
+            {
+                /* the backend does not support storing UUIDs within the
+                 * underlying storage so use what we store in XML */
+
+                /* generate an UUID for an imported UUID-less medium */
+                if (isImport)
+                {
+                    if (fSetImageId)
+                        mediumId = m->uuidImage;
+                    else
+                        mediumId.create();
+                }
+            }
+
+            /* get the medium variant */
+            unsigned uImageFlags;
+            vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
+            ComAssertRCThrow(vrc, E_FAIL);
+            m->variant = (MediumVariant_T)uImageFlags;
+
+            /* check/get the parent uuid and update corresponding state */
+            if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
+            {
+                RTUUID parentId;
+                vrc = VDGetParentUuid(hdd, 0, &parentId);
+                ComAssertRCThrow(vrc, E_FAIL);
+
+                /* streamOptimized VMDK images are only accepted as base
+                 * images, as this allows automatic repair of OVF appliances.
+                 * 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
+                    && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
+                {
+                    /* the parent must be known to us. Note that we freely
+                     * call locking methods of mVirtualBox and parent, as all
+                     * relevant locks must be already held. There may be no
+                     * concurrent access to the just opened medium on other
+                     * 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 (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;
+                    }
+
+                    /* we set mParent & children() */
+                    AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
+
+                    Assert(m->pParent.isNull());
+                    m->pParent = pParent;
+                    m->pParent->m->llChildren.push_back(this);
+                }
+                else
+                {
+                    /* we access mParent */
+                    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
+
+                    /* check that parent UUIDs match. Note that there's no need
+                     * for the parent's AutoCaller (our lifetime is bound to
+                     * it) */
+
+                    if (m->pParent.isNull())
+                    {
+                        /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
+                         * and 3.1.0-3.1.8 there are base images out there
+                         * which have a non-zero parent UUID. No point in
+                         * complaining about them, instead automatically
+                         * repair the problem. Later we can bring back the
+                         * error message, but we should wait until really
+                         * most users have repaired their images, either with
+                         * VBoxFixHdd or this way. */
+#if 1
+                        fRepairImageZeroParentUuid = true;
+#else /* 0 */
+                        lastAccessError = Utf8StrFmt(
+                                tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
+                                location.c_str(),
+                                m->pVirtualBox->settingsFilePath().c_str());
+                        throw S_OK;
+#endif /* 0 */
+                    }
+
+                    AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
+                    if (   !fRepairImageZeroParentUuid
+                        && m->pParent->getState() != MediumState_Inaccessible
+                        && m->pParent->getId() != parentId)
+                    {
+                        lastAccessError = Utf8StrFmt(
+                                tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
+                                &parentId, location.c_str(),
+                                m->pParent->getId().raw(),
+                                m->pVirtualBox->settingsFilePath().c_str());
+                        throw S_OK;
+                    }
+
+                    /// @todo NEWMEDIA what to do if the parent is not
+                    /// accessible while the diff is? Probably nothing. The
+                    /// real code will detect the mismatch anyway.
+                }
+            }
+
+            mediumSize = VDGetFileSize(hdd, 0);
+            mediumLogicalSize = VDGetSize(hdd, 0);
+
+            success = true;
+        }
+        catch (HRESULT aRC)
+        {
+            rc = aRC;
+        }
+
+        VDDestroy(hdd);
+    }
+    catch (HRESULT aRC)
+    {
+        rc = aRC;
+    }
+
+    alock.enter();
+
+    if (isImport)
+        unconst(m->id) = mediumId;
+
+    if (success)
+    {
+        m->size = mediumSize;
+        m->logicalSize = mediumLogicalSize;
+        m->strLastAccessError.setNull();
+    }
+    else
+    {
+        m->strLastAccessError = lastAccessError;
+        LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
+                        location.c_str(), m->strLastAccessError.c_str(),
+                        rc, vrc));
+    }
+
+    /* inform other callers if there are any */
+    RTSemEventMultiSignal(m->queryInfoSem);
+    m->queryInfoRunning = false;
+
+    /* Set the proper state according to the result of the check */
+    if (success)
+        m->preLockState = MediumState_Created;
+    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;
+    if (FAILED(rc)) return rc;
+
+    /* If this is a base image which incorrectly has a parent UUID set,
+     * repair the image now by zeroing the parent UUID. This is only done
+     * when we have structural information from a config file, on import
+     * this is not possible. If someone would accidentally call openMedium
+     * with a diff image before the base is registered this would destroy
+     * the diff. Not acceptable. */
+    if (fRepairImageZeroParentUuid)
+    {
+        rc = LockWrite(NULL);
+        if (FAILED(rc)) return rc;
+
+        alock.leave();
+
+        try
+        {
+            PVBOXHDD hdd;
+            vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
+            ComAssertRCThrow(vrc, E_FAIL);
+
+            try
+            {
+                vrc = VDOpen(hdd,
+                             format.c_str(),
+                             location.c_str(),
+                             uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
+                             m->vdImageIfaces);
+                if (RT_FAILURE(vrc))
+                    throw S_OK;
+
+                RTUUID zeroParentUuid;
+                RTUuidClear(&zeroParentUuid);
+                vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
+                ComAssertRCThrow(vrc, E_FAIL);
+            }
+            catch (HRESULT aRC)
+            {
+                rc = aRC;
+            }
+
+            VDDestroy(hdd);
+        }
+        catch (HRESULT aRC)
+        {
+            rc = aRC;
+        }
+
+        alock.enter();
+
+        rc = UnlockWrite(NULL);
+        if (SUCCEEDED(rc) && FAILED(rc2))
+            rc = rc2;
+        if (FAILED(rc)) return rc;
+    }
+
+    return rc;
+}
+
+/**
+ * Performs extra checks if the medium can be closed and returns S_OK in
+ * this case. Otherwise, returns a respective error message. Called by
+ * Close() under the medium tree lock and the medium lock.
+ *
+ * @note Also reused by Medium::Reset().
+ *
+ * @note Caller must hold the media tree write lock!
+ */
+HRESULT Medium::canClose()
+{
+    Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
+
+    if (getChildren().size() != 0)
+        return setError(VBOX_E_OBJECT_IN_USE,
+                        tr("Cannot close medium '%s' because it has %d child media"),
+                        m->strLocationFull.c_str(), getChildren().size());
+
+    return S_OK;
+}
+
+/**
+ * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
+ *
+ * This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending
+ * on the device type of this medium.
+ *
+ * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
+ *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
+ *
+ * @note Caller must have locked the media tree lock for writing!
+ */
+HRESULT Medium::unregisterWithVirtualBox(bool *pfNeedsGlobalSaveSettings)
+{
+    /* Note that we need to de-associate ourselves from the parent to let
+     * unregisterHardDisk() properly save the registry */
+
+    /* we modify mParent and access children */
+    Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
+
+    Medium *pParentBackup = m->pParent;
+    AssertReturn(getChildren().size() == 0, E_FAIL);
+    if (m->pParent)
+        deparent();
+
+    HRESULT rc = E_FAIL;
+    switch (m->devType)
+    {
+        case DeviceType_DVD:
+            rc = m->pVirtualBox->unregisterImage(this, DeviceType_DVD, pfNeedsGlobalSaveSettings);
+        break;
+
+        case DeviceType_Floppy:
+            rc = m->pVirtualBox->unregisterImage(this, DeviceType_Floppy, pfNeedsGlobalSaveSettings);
+        break;
+
+        case DeviceType_HardDisk:
+            rc = m->pVirtualBox->unregisterHardDisk(this, pfNeedsGlobalSaveSettings);
+        break;
+
+        default:
+        break;
+    }
+
+    if (FAILED(rc))
+    {
+        if (pParentBackup)
+        {
+            // re-associate with the parent as we are still relatives in the registry
+            m->pParent = pParentBackup;
+            m->pParent->m->llChildren.push_back(this);
+        }
+    }
+
+    return rc;
+}
+
+/**
+ * Sets the extended error info according to the current media state.
+ *
+ * @note Must be called from under this object's write or read lock.
+ */
+HRESULT Medium::setStateError()
+{
+    HRESULT rc = E_FAIL;
+
+    switch (m->state)
+    {
+        case MediumState_NotCreated:
+        {
+            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
+                          tr("Storage for the medium '%s' is not created"),
+                          m->strLocationFull.c_str());
+            break;
+        }
+        case MediumState_Created:
+        {
+            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
+                          tr("Storage for the medium '%s' is already created"),
+                          m->strLocationFull.c_str());
+            break;
+        }
+        case MediumState_LockedRead:
+        {
+            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
+                          tr("Medium '%s' is locked for reading by another task"),
+                          m->strLocationFull.c_str());
+            break;
+        }
+        case MediumState_LockedWrite:
+        {
+            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
+                          tr("Medium '%s' is locked for writing by another task"),
+                          m->strLocationFull.c_str());
+            break;
+        }
+        case MediumState_Inaccessible:
+        {
+            /* be in sync with Console::powerUpThread() */
+            if (!m->strLastAccessError.isEmpty())
+                rc = setError(VBOX_E_INVALID_OBJECT_STATE,
+                              tr("Medium '%s' is not accessible. %s"),
+                              m->strLocationFull.c_str(), m->strLastAccessError.c_str());
+            else
+                rc = setError(VBOX_E_INVALID_OBJECT_STATE,
+                              tr("Medium '%s' is not accessible"),
+                              m->strLocationFull.c_str());
+            break;
+        }
+        case MediumState_Creating:
+        {
+            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
+                          tr("Storage for the medium '%s' is being created"),
+                          m->strLocationFull.c_str());
+            break;
+        }
+        case MediumState_Deleting:
+        {
+            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
+                          tr("Storage for the medium '%s' is being deleted"),
+                          m->strLocationFull.c_str());
+            break;
+        }
+        default:
+        {
+            AssertFailed();
+            break;
+        }
+    }
+
+    return rc;
 }
 
@@ -3736,477 +5530,47 @@
 
 /**
- * Queries information from the medium.
- *
- * As a result of this call, the accessibility state and data members such as
- * size and description will be updated with the current information.
- *
- * @note This method may block during a system I/O call that checks storage
- *       accessibility.
- *
- * @note Locks medium tree for reading and writing (for new diff media checked
- *       for the first time). Locks mParent for reading. Locks this object for
- *       writing.
- *
- * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
- * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs())
- * @return
- */
-HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    if (   m->state != MediumState_Created
-        && m->state != MediumState_Inaccessible
-        && m->state != MediumState_LockedRead)
-        return E_FAIL;
-
-    HRESULT rc = S_OK;
-
-    int vrc = VINF_SUCCESS;
-
-    /* check if a blocking queryInfo() call is in progress on some other thread,
-     * and wait for it to finish if so instead of querying data ourselves */
-    if (m->queryInfoRunning)
-    {
-        Assert(   m->state == MediumState_LockedRead
-               || m->state == MediumState_LockedWrite);
-
-        alock.leave();
-        vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
-        alock.enter();
-
-        AssertRC(vrc);
-
-        return S_OK;
-    }
-
-    bool success = false;
-    Utf8Str lastAccessError;
-
-    /* are we dealing with a new medium constructed using the existing
-     * location? */
-    bool isImport = m->id.isEmpty();
-    unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
-
-    /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
-     * media because that would prevent necessary modifications
-     * when opening media of some third-party formats for the first
-     * time in VirtualBox (such as VMDK for which VDOpen() needs to
-     * generate an UUID if it is missing) */
-    if (    (m->hddOpenMode == OpenReadOnly)
-         || m->type == MediumType_Readonly
-         || !isImport
-       )
-        uOpenFlags |= VD_OPEN_FLAGS_READONLY;
-
-    /* Open shareable medium with the appropriate flags */
-    if (m->type == MediumType_Shareable)
-        uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
-
-    /* Lock the medium, which makes the behavior much more consistent */
-    if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
-        rc = LockRead(NULL);
-    else
-        rc = LockWrite(NULL);
-    if (FAILED(rc)) return rc;
-
-    /* Copies of the input state fields which are not read-only,
-     * as we're dropping the lock. CAUTION: be extremely careful what
-     * you do with the contents of this medium object, as you will
-     * create races if there are concurrent changes. */
-    Utf8Str format(m->strFormat);
-    Utf8Str location(m->strLocationFull);
-    ComObjPtr<MediumFormat> formatObj = m->formatObj;
-
-    /* "Output" values which can't be set because the lock isn't held
-     * at the time the values are determined. */
-    Guid mediumId = m->id;
-    uint64_t mediumSize = 0;
-    uint64_t mediumLogicalSize = 0;
-
-    /* Flag whether a base image has a non-zero parent UUID and thus
-     * need repairing after it was closed again. */
-    bool fRepairImageZeroParentUuid = false;
-
-    /* leave the lock before a lengthy operation */
-    vrc = RTSemEventMultiReset(m->queryInfoSem);
-    AssertRCReturn(vrc, E_FAIL);
-    m->queryInfoRunning = true;
-    alock.leave();
-
-    try
-    {
-        /* skip accessibility checks for host drives */
-        if (m->hostDrive)
-        {
-            success = true;
-            throw S_OK;
-        }
-
-        PVBOXHDD hdd;
-        vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
-        ComAssertRCThrow(vrc, E_FAIL);
-
-        try
-        {
-            /** @todo This kind of opening of media is assuming that diff
-             * media can be opened as base media. Should be documented that
-             * it must work for all medium format backends. */
-            vrc = VDOpen(hdd,
-                         format.c_str(),
-                         location.c_str(),
-                         uOpenFlags,
-                         m->vdImageIfaces);
-            if (RT_FAILURE(vrc))
-            {
-                lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
-                                             location.c_str(), vdError(vrc).c_str());
-                throw S_OK;
-            }
-
-            if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
-            {
-                /* Modify the UUIDs if necessary. The associated fields are
-                 * not modified by other code, so no need to copy. */
-                if (fSetImageId)
-                {
-                    vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
-                    ComAssertRCThrow(vrc, E_FAIL);
-                }
-                if (fSetParentId)
-                {
-                    vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
-                    ComAssertRCThrow(vrc, E_FAIL);
-                }
-                /* zap the information, these are no long-term members */
-                unconst(m->uuidImage).clear();
-                unconst(m->uuidParentImage).clear();
-
-                /* check the UUID */
-                RTUUID uuid;
-                vrc = VDGetUuid(hdd, 0, &uuid);
-                ComAssertRCThrow(vrc, E_FAIL);
-
-                if (isImport)
-                {
-                    mediumId = uuid;
-
-                    if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
-                        // only when importing a VDMK that has no UUID, create one in memory
-                        mediumId.create();
-                }
-                else
-                {
-                    Assert(!mediumId.isEmpty());
-
-                    if (mediumId != uuid)
-                    {
-                        lastAccessError = Utf8StrFmt(
-                                tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
-                                &uuid,
-                                location.c_str(),
-                                mediumId.raw(),
-                                m->pVirtualBox->settingsFilePath().c_str());
-                        throw S_OK;
-                    }
-                }
-            }
-            else
-            {
-                /* the backend does not support storing UUIDs within the
-                 * underlying storage so use what we store in XML */
-
-                /* generate an UUID for an imported UUID-less medium */
-                if (isImport)
-                {
-                    if (fSetImageId)
-                        mediumId = m->uuidImage;
-                    else
-                        mediumId.create();
-                }
-            }
-
-            /* get the medium variant */
-            unsigned uImageFlags;
-            vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
-            ComAssertRCThrow(vrc, E_FAIL);
-            m->variant = (MediumVariant_T)uImageFlags;
-
-            /* check/get the parent uuid and update corresponding state */
-            if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
-            {
-                RTUUID parentId;
-                vrc = VDGetParentUuid(hdd, 0, &parentId);
-                ComAssertRCThrow(vrc, E_FAIL);
-
-                /* streamOptimized VMDK images are only accepted as base
-                 * images, as this allows automatic repair of OVF appliances.
-                 * 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
-                    && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
-                {
-                    /* the parent must be known to us. Note that we freely
-                     * call locking methods of mVirtualBox and parent, as all
-                     * relevant locks must be already held. There may be no
-                     * concurrent access to the just opened medium on other
-                     * 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 (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;
-                    }
-
-                    /* we set mParent & children() */
-                    AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
-
-                    Assert(m->pParent.isNull());
-                    m->pParent = pParent;
-                    m->pParent->m->llChildren.push_back(this);
-                }
-                else
-                {
-                    /* we access mParent */
-                    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
-
-                    /* check that parent UUIDs match. Note that there's no need
-                     * for the parent's AutoCaller (our lifetime is bound to
-                     * it) */
-
-                    if (m->pParent.isNull())
-                    {
-                        /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
-                         * and 3.1.0-3.1.8 there are base images out there
-                         * which have a non-zero parent UUID. No point in
-                         * complaining about them, instead automatically
-                         * repair the problem. Later we can bring back the
-                         * error message, but we should wait until really
-                         * most users have repaired their images, either with
-                         * VBoxFixHdd or this way. */
-#if 1
-                        fRepairImageZeroParentUuid = true;
-#else /* 0 */
-                        lastAccessError = Utf8StrFmt(
-                                tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
-                                location.c_str(),
-                                m->pVirtualBox->settingsFilePath().c_str());
-                        throw S_OK;
-#endif /* 0 */
-                    }
-
-                    AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
-                    if (   !fRepairImageZeroParentUuid
-                        && m->pParent->getState() != MediumState_Inaccessible
-                        && m->pParent->getId() != parentId)
-                    {
-                        lastAccessError = Utf8StrFmt(
-                                tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
-                                &parentId, location.c_str(),
-                                m->pParent->getId().raw(),
-                                m->pVirtualBox->settingsFilePath().c_str());
-                        throw S_OK;
-                    }
-
-                    /// @todo NEWMEDIA what to do if the parent is not
-                    /// accessible while the diff is? Probably nothing. The
-                    /// real code will detect the mismatch anyway.
-                }
-            }
-
-            mediumSize = VDGetFileSize(hdd, 0);
-            mediumLogicalSize = VDGetSize(hdd, 0);
-
-            success = true;
-        }
-        catch (HRESULT aRC)
-        {
-            rc = aRC;
-        }
-
-        VDDestroy(hdd);
-    }
-    catch (HRESULT aRC)
-    {
-        rc = aRC;
-    }
-
-    alock.enter();
-
-    if (isImport)
-        unconst(m->id) = mediumId;
-
-    if (success)
-    {
-        m->size = mediumSize;
-        m->logicalSize = mediumLogicalSize;
-        m->strLastAccessError.setNull();
-    }
-    else
-    {
-        m->strLastAccessError = lastAccessError;
-        LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
-                        location.c_str(), m->strLastAccessError.c_str(),
-                        rc, vrc));
-    }
-
-    /* inform other callers if there are any */
-    RTSemEventMultiSignal(m->queryInfoSem);
-    m->queryInfoRunning = false;
-
-    /* Set the proper state according to the result of the check */
-    if (success)
-        m->preLockState = MediumState_Created;
-    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;
-    if (FAILED(rc)) return rc;
-
-    /* If this is a base image which incorrectly has a parent UUID set,
-     * repair the image now by zeroing the parent UUID. This is only done
-     * when we have structural information from a config file, on import
-     * this is not possible. If someone would accidentally call openMedium
-     * with a diff image before the base is registered this would destroy
-     * the diff. Not acceptable. */
-    if (fRepairImageZeroParentUuid)
-    {
-        rc = LockWrite(NULL);
-        if (FAILED(rc)) return rc;
-
-        alock.leave();
-
-        try
-        {
-            PVBOXHDD hdd;
-            vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
-            ComAssertRCThrow(vrc, E_FAIL);
-
-            try
-            {
-                vrc = VDOpen(hdd,
-                             format.c_str(),
-                             location.c_str(),
-                             uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
-                             m->vdImageIfaces);
-                if (RT_FAILURE(vrc))
-                    throw S_OK;
-
-                RTUUID zeroParentUuid;
-                RTUuidClear(&zeroParentUuid);
-                vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
-                ComAssertRCThrow(vrc, E_FAIL);
-            }
-            catch (HRESULT aRC)
-            {
-                rc = aRC;
-            }
-
-            VDDestroy(hdd);
-        }
-        catch (HRESULT aRC)
-        {
-            rc = aRC;
-        }
-
-        alock.enter();
-
-        rc = UnlockWrite(NULL);
-        if (SUCCEEDED(rc) && FAILED(rc2))
-            rc = rc2;
-        if (FAILED(rc)) return rc;
-    }
-
-    return rc;
-}
-
-/**
- * Sets the extended error info according to the current media state.
- *
- * @note Must be called from under this object's write or read lock.
- */
-HRESULT Medium::setStateError()
-{
-    HRESULT rc = E_FAIL;
-
-    switch (m->state)
-    {
-        case MediumState_NotCreated:
-        {
-            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
-                          tr("Storage for the medium '%s' is not created"),
-                          m->strLocationFull.c_str());
-            break;
-        }
-        case MediumState_Created:
-        {
-            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
-                          tr("Storage for the medium '%s' is already created"),
-                          m->strLocationFull.c_str());
-            break;
-        }
-        case MediumState_LockedRead:
-        {
-            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
-                          tr("Medium '%s' is locked for reading by another task"),
-                          m->strLocationFull.c_str());
-            break;
-        }
-        case MediumState_LockedWrite:
-        {
-            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
-                          tr("Medium '%s' is locked for writing by another task"),
-                          m->strLocationFull.c_str());
-            break;
-        }
-        case MediumState_Inaccessible:
-        {
-            /* be in sync with Console::powerUpThread() */
-            if (!m->strLastAccessError.isEmpty())
-                rc = setError(VBOX_E_INVALID_OBJECT_STATE,
-                              tr("Medium '%s' is not accessible. %s"),
-                              m->strLocationFull.c_str(), m->strLastAccessError.c_str());
-            else
-                rc = setError(VBOX_E_INVALID_OBJECT_STATE,
-                              tr("Medium '%s' is not accessible"),
-                              m->strLocationFull.c_str());
-            break;
-        }
-        case MediumState_Creating:
-        {
-            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
-                          tr("Storage for the medium '%s' is being created"),
-                          m->strLocationFull.c_str());
-            break;
-        }
-        case MediumState_Deleting:
-        {
-            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
-                          tr("Storage for the medium '%s' is being deleted"),
-                          m->strLocationFull.c_str());
-            break;
-        }
-        default:
-        {
-            AssertFailed();
-            break;
-        }
-    }
-
-    return rc;
+ * Checks that the format ID is valid and sets it on success.
+ *
+ * Note that this method will caller-reference the format object on success!
+ * This reference must be released somewhere to let the MediumFormat object be
+ * uninitialized.
+ *
+ * @note Must be called from under this object's write lock.
+ */
+HRESULT Medium::setFormat(const Utf8Str &aFormat)
+{
+    /* get the format object first */
+    {
+        SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
+        AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
+
+        unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
+        if (m->formatObj.isNull())
+            return setError(E_INVALIDARG,
+                            tr("Invalid medium storage format '%s'"),
+                            aFormat.c_str());
+
+        /* reference the format permanently to prevent its unexpected
+         * uninitialization */
+        HRESULT rc = m->formatObj->addCaller();
+        AssertComRCReturnRC(rc);
+
+        /* get properties (preinsert them as keys in the map). Note that the
+         * map doesn't grow over the object life time since the set of
+         * properties is meant to be constant. */
+
+        Assert(m->mapProperties.empty());
+
+        for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
+             it != m->formatObj->getProperties().end();
+             ++it)
+        {
+            m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
+        }
+    }
+
+    unconst(m->strFormat) = aFormat;
+
+    return S_OK;
 }
 
@@ -4262,1257 +5626,4 @@
 
 /**
- * Implementation for the public Medium::Close() with the exception of calling
- * VirtualBox::saveSettings(), in case someone wants to call this for several
- * media.
- *
- * After this returns with success, uninit() has been called on the medium, and
- * the object is no longer usable ("not ready" state).
- *
- * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
- *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
- *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
- *                and this parameter is ignored.
- * @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here
- *                   upon which the Medium instance gets uninitialized.
- * @return
- */
-HRESULT Medium::close(bool *pfNeedsGlobalSaveSettings, AutoCaller &autoCaller)
-{
-    // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
-    AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
-                                  this->lockHandle()
-                                  COMMA_LOCKVAL_SRC_POS);
-
-    LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str()));
-
-    bool wasCreated = true;
-
-    switch (m->state)
-    {
-        case MediumState_NotCreated:
-            wasCreated = false;
-            break;
-        case MediumState_Created:
-        case MediumState_Inaccessible:
-            break;
-        default:
-            return setStateError();
-    }
-
-    if (m->backRefs.size() != 0)
-        return setError(VBOX_E_OBJECT_IN_USE,
-                        tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
-                        m->strLocationFull.c_str(), m->backRefs.size());
-
-    // perform extra media-dependent close checks
-    HRESULT rc = canClose();
-    if (FAILED(rc)) return rc;
-
-    if (wasCreated)
-    {
-        // remove from the list of known media before performing actual
-        // uninitialization (to keep the media registry consistent on
-        // failure to do so)
-        rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings);
-        if (FAILED(rc)) return rc;
-    }
-
-    // leave the AutoCaller, as otherwise uninit() will simply hang
-    autoCaller.release();
-
-    // Keep the locks held until after uninit, as otherwise the consistency
-    // of the medium tree cannot be guaranteed.
-    uninit();
-
-    LogFlowFuncLeave();
-
-    return rc;
-}
-
-/**
- * Deletes the medium storage unit.
- *
- * If @a aProgress is not NULL but the object it points to is @c null then a new
- * progress object will be created and assigned to @a *aProgress on success,
- * otherwise the existing progress object is used. If Progress is NULL, then no
- * progress object is created/used at all.
- *
- * When @a aWait is @c false, this method will create a thread to perform the
- * delete operation asynchronously and will return immediately. Otherwise, it
- * will perform the operation on the calling thread and will not return to the
- * caller until the operation is completed. Note that @a aProgress cannot be
- * NULL when @a aWait is @c false (this method will assert in this case).
- *
- * @param aProgress     Where to find/store a Progress object to track operation
- *                      completion.
- * @param aWait         @c true if this method should block instead of creating
- *                      an asynchronous thread.
- * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
- *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
- *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
- *                and this parameter is ignored.
- *
- * @note Locks mVirtualBox and this object for writing. Locks medium tree for
- *       writing.
- */
-HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
-                              bool aWait,
-                              bool *pfNeedsGlobalSaveSettings)
-{
-    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
-
-    AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
-    HRESULT rc = S_OK;
-    ComObjPtr<Progress> pProgress;
-    Medium::Task *pTask = NULL;
-
-    try
-    {
-        /* we're accessing the media tree, and canClose() needs it too */
-        AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
-                                      this->lockHandle()
-                                      COMMA_LOCKVAL_SRC_POS);
-        LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
-
-        if (    !(m->formatObj->getCapabilities() & (   MediumFormatCapabilities_CreateDynamic
-                                                      | MediumFormatCapabilities_CreateFixed)))
-            throw setError(VBOX_E_NOT_SUPPORTED,
-                           tr("Medium format '%s' does not support storage deletion"),
-                           m->strFormat.c_str());
-
-        /* Note that we are fine with Inaccessible state too: a) for symmetry
-         * with create calls and b) because it doesn't really harm to try, if
-         * it is really inaccessible, the delete operation will fail anyway.
-         * Accepting Inaccessible state is especially important because all
-         * registered media are initially Inaccessible upon VBoxSVC startup
-         * until COMGETTER(RefreshState) is called. Accept Deleting state
-         * because some callers need to put the medium in this state early
-         * to prevent races. */
-        switch (m->state)
-        {
-            case MediumState_Created:
-            case MediumState_Deleting:
-            case MediumState_Inaccessible:
-                break;
-            default:
-                throw setStateError();
-        }
-
-        if (m->backRefs.size() != 0)
-        {
-            Utf8Str strMachines;
-            for (BackRefList::const_iterator it = m->backRefs.begin();
-                it != m->backRefs.end();
-                ++it)
-            {
-                const BackRef &b = *it;
-                if (strMachines.length())
-                    strMachines.append(", ");
-                strMachines.append(b.machineId.toString().c_str());
-            }
-#ifdef DEBUG
-            dumpBackRefs();
-#endif
-            throw setError(VBOX_E_OBJECT_IN_USE,
-                           tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
-                           m->strLocationFull.c_str(),
-                           m->backRefs.size(),
-                           strMachines.c_str());
-        }
-
-        rc = canClose();
-        if (FAILED(rc))
-            throw rc;
-
-        /* go to Deleting state, so that the medium is not actually locked */
-        if (m->state != MediumState_Deleting)
-        {
-            rc = markForDeletion();
-            if (FAILED(rc))
-                throw rc;
-        }
-
-        /* Build the medium lock list. */
-        MediumLockList *pMediumLockList(new MediumLockList());
-        rc = createMediumLockList(true /* fFailIfInaccessible */,
-                                  true /* fMediumLockWrite */,
-                                  NULL,
-                                  *pMediumLockList);
-        if (FAILED(rc))
-        {
-            delete pMediumLockList;
-            throw rc;
-        }
-
-        rc = pMediumLockList->Lock();
-        if (FAILED(rc))
-        {
-            delete pMediumLockList;
-            throw setError(rc,
-                           tr("Failed to lock media when deleting '%s'"),
-                           getLocationFull().c_str());
-        }
-
-        /* try to remove from the list of known media before performing
-         * actual deletion (we favor the consistency of the media registry
-         * which would have been broken if unregisterWithVirtualBox() failed
-         * after we successfully deleted the storage) */
-        rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings);
-        if (FAILED(rc))
-            throw rc;
-        // no longer need lock
-        multilock.release();
-
-        if (aProgress != NULL)
-        {
-            /* use the existing progress object... */
-            pProgress = *aProgress;
-
-            /* ...but create a new one if it is null */
-            if (pProgress.isNull())
-            {
-                pProgress.createObject();
-                rc = pProgress->init(m->pVirtualBox,
-                                     static_cast<IMedium*>(this),
-                                     BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
-                                     FALSE /* aCancelable */);
-                if (FAILED(rc))
-                    throw rc;
-            }
-        }
-
-        /* setup task object to carry out the operation sync/async */
-        pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
-        rc = pTask->rc();
-        AssertComRC(rc);
-        if (FAILED(rc))
-            throw rc;
-    }
-    catch (HRESULT aRC) { rc = aRC; }
-
-    if (SUCCEEDED(rc))
-    {
-        if (aWait)
-            rc = runNow(pTask, NULL /* pfNeedsGlobalSaveSettings*/);
-        else
-            rc = startThread(pTask);
-
-        if (SUCCEEDED(rc) && aProgress != NULL)
-            *aProgress = pProgress;
-
-    }
-    else
-    {
-        if (pTask)
-            delete pTask;
-
-        /* Undo deleting state if necessary. */
-        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-        unmarkForDeletion();
-    }
-
-    return rc;
-}
-
-/**
- * Mark a medium for deletion.
- *
- * @note Caller must hold the write lock on this medium!
- */
-HRESULT Medium::markForDeletion()
-{
-    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
-    switch (m->state)
-    {
-        case MediumState_Created:
-        case MediumState_Inaccessible:
-            m->preLockState = m->state;
-            m->state = MediumState_Deleting;
-            return S_OK;
-        default:
-            return setStateError();
-    }
-}
-
-/**
- * Removes the "mark for deletion".
- *
- * @note Caller must hold the write lock on this medium!
- */
-HRESULT Medium::unmarkForDeletion()
-{
-    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
-    switch (m->state)
-    {
-        case MediumState_Deleting:
-            m->state = m->preLockState;
-            return S_OK;
-        default:
-            return setStateError();
-    }
-}
-
-/**
- * Mark a medium for deletion which is in locked state.
- *
- * @note Caller must hold the write lock on this medium!
- */
-HRESULT Medium::markLockedForDeletion()
-{
-    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
-    if (   (   m->state == MediumState_LockedRead
-            || m->state == MediumState_LockedWrite)
-        && m->preLockState == MediumState_Created)
-    {
-        m->preLockState = MediumState_Deleting;
-        return S_OK;
-    }
-    else
-        return setStateError();
-}
-
-/**
- * Removes the "mark for deletion" for a medium in locked state.
- *
- * @note Caller must hold the write lock on this medium!
- */
-HRESULT Medium::unmarkLockedForDeletion()
-{
-    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
-    if (   (   m->state == MediumState_LockedRead
-            || m->state == MediumState_LockedWrite)
-        && m->preLockState == MediumState_Deleting)
-    {
-        m->preLockState = MediumState_Created;
-        return S_OK;
-    }
-    else
-        return setStateError();
-}
-
-/**
- * Creates a new differencing storage unit using the format of the given target
- * medium and the location. Note that @c aTarget must be NotCreated.
- *
- * The @a aMediumLockList parameter contains the associated medium lock list,
- * which must be in locked state. If @a aWait is @c true then the caller is
- * responsible for unlocking.
- *
- * If @a aProgress is not NULL but the object it points to is @c null then a
- * new progress object will be created and assigned to @a *aProgress on
- * success, otherwise the existing progress object is used. If @a aProgress is
- * NULL, then no progress object is created/used at all.
- *
- * When @a aWait is @c false, this method will create a thread to perform the
- * create operation asynchronously and will return immediately. Otherwise, it
- * will perform the operation on the calling thread and will not return to the
- * caller until the operation is completed. Note that @a aProgress cannot be
- * NULL when @a aWait is @c false (this method will assert in this case).
- *
- * @param aTarget           Target medium.
- * @param aVariant          Precise medium variant to create.
- * @param aMediumLockList   List of media which should be locked.
- * @param aProgress         Where to find/store a Progress object to track
- *                          operation completion.
- * @param aWait             @c true if this method should block instead of
- *                          creating an asynchronous thread.
- * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
- *                          initialized to false and that will be set to true
- *                          by this function if the caller should invoke
- *                          VirtualBox::saveSettings() because the global
- *                          settings have changed. This only works in "wait"
- *                          mode; otherwise saveSettings is called
- *                          automatically by the thread that was created,
- *                          and this parameter is ignored.
- *
- * @note Locks this object and @a aTarget for writing.
- */
-HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
-                                  MediumVariant_T aVariant,
-                                  MediumLockList *aMediumLockList,
-                                  ComObjPtr<Progress> *aProgress,
-                                  bool aWait,
-                                  bool *pfNeedsGlobalSaveSettings)
-{
-    AssertReturn(!aTarget.isNull(), E_FAIL);
-    AssertReturn(aMediumLockList, E_FAIL);
-    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
-
-    AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
-    AutoCaller targetCaller(aTarget);
-    if (FAILED(targetCaller.rc())) return targetCaller.rc();
-
-    HRESULT rc = S_OK;
-    ComObjPtr<Progress> pProgress;
-    Medium::Task *pTask = NULL;
-
-    try
-    {
-        AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
-
-        ComAssertThrow(   m->type != MediumType_Writethrough
-                       && m->type != MediumType_Shareable
-                       && m->type != MediumType_Readonly, E_FAIL);
-        ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
-
-        if (aTarget->m->state != MediumState_NotCreated)
-            throw aTarget->setStateError();
-
-        /* Check that the medium is not attached to the current state of
-         * any VM referring to it. */
-        for (BackRefList::const_iterator it = m->backRefs.begin();
-             it != m->backRefs.end();
-             ++it)
-        {
-            if (it->fInCurState)
-            {
-                /* Note: when a VM snapshot is being taken, all normal media
-                 * attached to the VM in the current state will be, as an
-                 * exception, also associated with the snapshot which is about
-                 * to create (see SnapshotMachine::init()) before deassociating
-                 * them from the current state (which takes place only on
-                 * success in Machine::fixupHardDisks()), so that the size of
-                 * snapshotIds will be 1 in this case. The extra condition is
-                 * used to filter out this legal situation. */
-                if (it->llSnapshotIds.size() == 0)
-                    throw setError(VBOX_E_INVALID_OBJECT_STATE,
-                                   tr("Medium '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing media based on it may be created until it is detached"),
-                                   m->strLocationFull.c_str(), it->machineId.raw());
-
-                Assert(it->llSnapshotIds.size() == 1);
-            }
-        }
-
-        if (aProgress != NULL)
-        {
-            /* use the existing progress object... */
-            pProgress = *aProgress;
-
-            /* ...but create a new one if it is null */
-            if (pProgress.isNull())
-            {
-                pProgress.createObject();
-                rc = pProgress->init(m->pVirtualBox,
-                                     static_cast<IMedium*>(this),
-                                     BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
-                                     TRUE /* aCancelable */);
-                if (FAILED(rc))
-                    throw rc;
-            }
-        }
-
-        /* setup task object to carry out the operation sync/async */
-        pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
-                                           aMediumLockList,
-                                           aWait /* fKeepMediumLockList */);
-        rc = pTask->rc();
-        AssertComRC(rc);
-        if (FAILED(rc))
-             throw rc;
-
-        /* register a task (it will deregister itself when done) */
-        ++m->numCreateDiffTasks;
-        Assert(m->numCreateDiffTasks != 0); /* overflow? */
-
-        aTarget->m->state = MediumState_Creating;
-    }
-    catch (HRESULT aRC) { rc = aRC; }
-
-    if (SUCCEEDED(rc))
-    {
-        if (aWait)
-            rc = runNow(pTask, pfNeedsGlobalSaveSettings);
-        else
-            rc = startThread(pTask);
-
-        if (SUCCEEDED(rc) && aProgress != NULL)
-            *aProgress = pProgress;
-    }
-    else if (pTask != NULL)
-        delete pTask;
-
-    return rc;
-}
-
-/**
- * Prepares this (source) medium, target medium and all intermediate media
- * for the merge operation.
- *
- * This method is to be called prior to calling the #mergeTo() to perform
- * necessary consistency checks and place involved media to appropriate
- * states. If #mergeTo() is not called or fails, the state modifications
- * performed by this method must be undone by #cancelMergeTo().
- *
- * See #mergeTo() for more information about merging.
- *
- * @param pTarget       Target medium.
- * @param aMachineId    Allowed machine attachment. NULL means do not check.
- * @param aSnapshotId   Allowed snapshot attachment. NULL or empty UUID means
- *                      do not check.
- * @param fLockMedia    Flag whether to lock the medium lock list or not.
- *                      If set to false and the medium lock list locking fails
- *                      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 aMediumLockList Medium locking information (out).
- *
- * @note Locks medium tree for reading. Locks this object, aTarget and all
- *       intermediate media for writing.
- */
-HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
-                               const Guid *aMachineId,
-                               const Guid *aSnapshotId,
-                               bool fLockMedia,
-                               bool &fMergeForward,
-                               ComObjPtr<Medium> &pParentForTarget,
-                               MediaList &aChildrenToReparent,
-                               MediumLockList * &aMediumLockList)
-{
-    AssertReturn(pTarget != NULL, E_FAIL);
-    AssertReturn(pTarget != this, E_FAIL);
-
-    AutoCaller autoCaller(this);
-    AssertComRCReturnRC(autoCaller.rc());
-
-    AutoCaller targetCaller(pTarget);
-    AssertComRCReturnRC(targetCaller.rc());
-
-    HRESULT rc = S_OK;
-    fMergeForward = false;
-    pParentForTarget.setNull();
-    aChildrenToReparent.clear();
-    Assert(aMediumLockList == NULL);
-    aMediumLockList = NULL;
-
-    try
-    {
-        // locking: we need the tree lock first because we access parent pointers
-        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
-
-        /* more sanity checking and figuring out the merge direction */
-        ComObjPtr<Medium> pMedium = getParent();
-        while (!pMedium.isNull() && pMedium != pTarget)
-            pMedium = pMedium->getParent();
-        if (pMedium == pTarget)
-            fMergeForward = false;
-        else
-        {
-            pMedium = pTarget->getParent();
-            while (!pMedium.isNull() && pMedium != this)
-                pMedium = pMedium->getParent();
-            if (pMedium == this)
-                fMergeForward = true;
-            else
-            {
-                Utf8Str tgtLoc;
-                {
-                    AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
-                    tgtLoc = pTarget->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());
-            }
-        }
-
-        /* Build the lock list. */
-        aMediumLockList = new MediumLockList();
-        if (fMergeForward)
-            rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
-                                               true /* fMediumLockWrite */,
-                                               NULL,
-                                               *aMediumLockList);
-        else
-            rc = createMediumLockList(true /* fFailIfInaccessible */,
-                                      false /* fMediumLockWrite */,
-                                      NULL,
-                                      *aMediumLockList);
-        if (FAILED(rc))
-            throw rc;
-
-        /* Sanity checking, must be after lock list creation as it depends on
-         * valid medium states. The medium objects must be accessible. Only
-         * do this if immediate locking is requested, otherwise it fails when
-         * we construct a medium lock list for an already running VM. Snapshot
-         * deletion uses this to simplify its life. */
-        if (fLockMedia)
-        {
-            {
-                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-                if (m->state != MediumState_Created)
-                    throw setStateError();
-            }
-            {
-                AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
-                if (pTarget->m->state != MediumState_Created)
-                    throw pTarget->setStateError();
-            }
-        }
-
-        /* check medium attachment and other sanity conditions */
-        if (fMergeForward)
-        {
-            AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-            if (getChildren().size() > 1)
-            {
-                throw setError(VBOX_E_INVALID_OBJECT_STATE,
-                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
-                               m->strLocationFull.c_str(), getChildren().size());
-            }
-            /* One backreference is only allowed if the machine ID is not empty
-             * and it matches the machine the medium is attached to (including
-             * the snapshot ID if not empty). */
-            if (   m->backRefs.size() != 0
-                && (   !aMachineId
-                    || m->backRefs.size() != 1
-                    || aMachineId->isEmpty()
-                    || *getFirstMachineBackrefId() != *aMachineId
-                    || (   (!aSnapshotId || !aSnapshotId->isEmpty())
-                        && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
-                throw setError(VBOX_E_OBJECT_IN_USE,
-                               tr("Medium '%s' is attached to %d virtual machines"),
-                               m->strLocationFull.c_str(), m->backRefs.size());
-            if (m->type == MediumType_Immutable)
-                throw setError(VBOX_E_INVALID_OBJECT_STATE,
-                               tr("Medium '%s' is immutable"),
-                               m->strLocationFull.c_str());
-        }
-        else
-        {
-            AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
-            if (pTarget->getChildren().size() > 1)
-            {
-                throw setError(VBOX_E_OBJECT_IN_USE,
-                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
-                               pTarget->m->strLocationFull.c_str(),
-                               pTarget->getChildren().size());
-            }
-            if (pTarget->m->type == MediumType_Immutable)
-                throw setError(VBOX_E_INVALID_OBJECT_STATE,
-                               tr("Medium '%s' is immutable"),
-                               pTarget->m->strLocationFull.c_str());
-        }
-        ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
-        ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
-        for (pLast = pLastIntermediate;
-             !pLast.isNull() && pLast != pTarget && pLast != this;
-             pLast = pLast->getParent())
-        {
-            AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
-            if (pLast->getChildren().size() > 1)
-            {
-                throw setError(VBOX_E_OBJECT_IN_USE,
-                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
-                               pLast->m->strLocationFull.c_str(),
-                               pLast->getChildren().size());
-            }
-            if (pLast->m->backRefs.size() != 0)
-                throw setError(VBOX_E_OBJECT_IN_USE,
-                               tr("Medium '%s' is attached to %d virtual machines"),
-                               pLast->m->strLocationFull.c_str(),
-                               pLast->m->backRefs.size());
-
-        }
-
-        /* Update medium states appropriately */
-        if (m->state == MediumState_Created)
-        {
-            rc = markForDeletion();
-            if (FAILED(rc))
-                throw rc;
-        }
-        else
-        {
-            if (fLockMedia)
-                throw setStateError();
-            else if (   m->state == MediumState_LockedWrite
-                     || m->state == MediumState_LockedRead)
-            {
-                /* Either mark it for deletion in locked state or allow
-                 * others to have done so. */
-                if (m->preLockState == MediumState_Created)
-                    markLockedForDeletion();
-                else if (m->preLockState != MediumState_Deleting)
-                    throw setStateError();
-            }
-            else
-                throw setStateError();
-        }
-
-        if (fMergeForward)
-        {
-            /* we will need parent to reparent target */
-            pParentForTarget = m->pParent;
-        }
-        else
-        {
-            /* we will need to reparent children of the source */
-            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);
-            }
-        }
-        for (pLast = pLastIntermediate;
-             !pLast.isNull() && pLast != pTarget && pLast != this;
-             pLast = pLast->getParent())
-        {
-            AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
-            if (pLast->m->state == MediumState_Created)
-            {
-                rc = pLast->markForDeletion();
-                if (FAILED(rc))
-                    throw rc;
-            }
-            else
-                throw pLast->setStateError();
-        }
-
-        /* Tweak the lock list in the backward merge case, as the target
-         * isn't marked to be locked for writing yet. */
-        if (!fMergeForward)
-        {
-            MediumLockList::Base::iterator lockListBegin =
-                aMediumLockList->GetBegin();
-            MediumLockList::Base::iterator lockListEnd =
-                aMediumLockList->GetEnd();
-            lockListEnd--;
-            for (MediumLockList::Base::iterator it = lockListBegin;
-                 it != lockListEnd;
-                 ++it)
-            {
-                MediumLock &mediumLock = *it;
-                if (mediumLock.GetMedium() == pTarget)
-                {
-                    HRESULT rc2 = mediumLock.UpdateLock(true);
-                    AssertComRC(rc2);
-                    break;
-                }
-            }
-        }
-
-        if (fLockMedia)
-        {
-            rc = aMediumLockList->Lock();
-            if (FAILED(rc))
-            {
-                AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
-                throw setError(rc,
-                               tr("Failed to lock media when merging to '%s'"),
-                               pTarget->getLocationFull().c_str());
-            }
-        }
-    }
-    catch (HRESULT aRC) { rc = aRC; }
-
-    if (FAILED(rc))
-    {
-        delete aMediumLockList;
-        aMediumLockList = NULL;
-    }
-
-    return rc;
-}
-
-/**
- * Merges this medium to the specified medium which must be either its
- * direct ancestor or descendant.
- *
- * Given this medium is SOURCE and the specified medium is TARGET, we will
- * get two variants of the merge operation:
- *
- *                forward merge
- *                ------------------------->
- *  [Extra] <- SOURCE <- Intermediate <- TARGET
- *  Any        Del       Del             LockWr
- *
- *
- *                            backward merge
- *                <-------------------------
- *             TARGET <- Intermediate <- SOURCE <- [Extra]
- *             LockWr    Del             Del       LockWr
- *
- * Each diagram shows the involved media on the media chain where
- * SOURCE and TARGET belong. Under each medium there is a state value which
- * the medium must have at a time of the mergeTo() call.
- *
- * The media in the square braces may be absent (e.g. when the forward
- * operation takes place and SOURCE is the base medium, or when the backward
- * merge operation takes place and TARGET is the last child in the chain) but if
- * they present they are involved too as shown.
- *
- * Neither the source medium nor intermediate media may be attached to
- * any VM directly or in the snapshot, otherwise this method will assert.
- *
- * The #prepareMergeTo() method must be called prior to this method to place all
- * involved to necessary states and perform other consistency checks.
- *
- * If @a aWait is @c true then this method will perform the operation on the
- * calling thread and will not return to the caller until the operation is
- * completed. When this method succeeds, all intermediate medium objects in
- * the chain will be uninitialized, the state of the target medium (and all
- * involved extra media) will be restored. @a aMediumLockList will not be
- * deleted, whether the operation is successful or not. The caller has to do
- * this if appropriate. Note that this (source) medium is not uninitialized
- * because of possible AutoCaller instances held by the caller of this method
- * on the current thread. It's therefore the responsibility of the caller to
- * call Medium::uninit() after releasing all callers.
- *
- * If @a aWait is @c false then this method will create a thread to perform the
- * operation asynchronously and will return immediately. If the operation
- * succeeds, the thread will uninitialize the source medium object and all
- * intermediate medium objects in the chain, reset the state of the target
- * medium (and all involved extra media) and delete @a aMediumLockList.
- * If the operation fails, the thread will only reset the states of all
- * involved media and delete @a aMediumLockList.
- *
- * When this method fails (regardless of the @a aWait mode), it is a caller's
- * responsibility to undo state changes and delete @a aMediumLockList using
- * #cancelMergeTo().
- *
- * If @a aProgress is not NULL but the object it points to is @c null then a new
- * progress object will be created and assigned to @a *aProgress on success,
- * otherwise the existing progress object is used. If Progress is NULL, then no
- * progress object is created/used at all. Note that @a aProgress cannot be
- * NULL when @a aWait is @c false (this method will assert in this case).
- *
- * @param pTarget       Target medium.
- * @param fMergeForward Merge direction.
- * @param pParentForTarget New parent for target medium after merge.
- * @param aChildrenToReparent List of children of the source which will have
- *                      to be reparented to the target after merge.
- * @param aMediumLockList Medium locking information.
- * @param aProgress     Where to find/store a Progress object to track operation
- *                      completion.
- * @param aWait         @c true if this method should block instead of creating
- *                      an asynchronous thread.
- * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
- *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
- *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
- *                and this parameter is ignored.
- *
- * @note Locks the tree lock for writing. Locks the media from the chain
- *       for writing.
- */
-HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
-                        bool fMergeForward,
-                        const ComObjPtr<Medium> &pParentForTarget,
-                        const MediaList &aChildrenToReparent,
-                        MediumLockList *aMediumLockList,
-                        ComObjPtr <Progress> *aProgress,
-                        bool aWait,
-                        bool *pfNeedsGlobalSaveSettings)
-{
-    AssertReturn(pTarget != NULL, E_FAIL);
-    AssertReturn(pTarget != this, E_FAIL);
-    AssertReturn(aMediumLockList != NULL, E_FAIL);
-    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
-
-    AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
-    AutoCaller targetCaller(pTarget);
-    AssertComRCReturnRC(targetCaller.rc());
-
-    HRESULT rc = S_OK;
-    ComObjPtr <Progress> pProgress;
-    Medium::Task *pTask = NULL;
-
-    try
-    {
-        if (aProgress != NULL)
-        {
-            /* use the existing progress object... */
-            pProgress = *aProgress;
-
-            /* ...but create a new one if it is null */
-            if (pProgress.isNull())
-            {
-                Utf8Str tgtName;
-                {
-                    AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
-                    tgtName = pTarget->getName();
-                }
-
-                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-                pProgress.createObject();
-                rc = pProgress->init(m->pVirtualBox,
-                                     static_cast<IMedium*>(this),
-                                     BstrFmt(tr("Merging medium '%s' to '%s'"),
-                                             getName().c_str(),
-                                             tgtName.c_str()).raw(),
-                                     TRUE /* aCancelable */);
-                if (FAILED(rc))
-                    throw rc;
-            }
-        }
-
-        /* setup task object to carry out the operation sync/async */
-        pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
-                                      pParentForTarget, aChildrenToReparent,
-                                      pProgress, aMediumLockList,
-                                      aWait /* fKeepMediumLockList */);
-        rc = pTask->rc();
-        AssertComRC(rc);
-        if (FAILED(rc))
-            throw rc;
-    }
-    catch (HRESULT aRC) { rc = aRC; }
-
-    if (SUCCEEDED(rc))
-    {
-        if (aWait)
-            rc = runNow(pTask, pfNeedsGlobalSaveSettings);
-        else
-            rc = startThread(pTask);
-
-        if (SUCCEEDED(rc) && aProgress != NULL)
-            *aProgress = pProgress;
-    }
-    else if (pTask != NULL)
-        delete pTask;
-
-    return rc;
-}
-
-/**
- * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
- * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
- * the medium objects in @a aChildrenToReparent.
- *
- * @param aChildrenToReparent List of children of the source which will have
- *                      to be reparented to the target after merge.
- * @param aMediumLockList Medium locking information.
- *
- * @note Locks the media from the chain for writing.
- */
-void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
-                           MediumLockList *aMediumLockList)
-{
-    AutoCaller autoCaller(this);
-    AssertComRCReturnVoid(autoCaller.rc());
-
-    AssertReturnVoid(aMediumLockList != NULL);
-
-    /* Revert media marked for deletion to previous state. */
-    HRESULT rc;
-    MediumLockList::Base::const_iterator mediumListBegin =
-        aMediumLockList->GetBegin();
-    MediumLockList::Base::const_iterator mediumListEnd =
-        aMediumLockList->GetEnd();
-    for (MediumLockList::Base::const_iterator it = mediumListBegin;
-         it != mediumListEnd;
-         ++it)
-    {
-        const MediumLock &mediumLock = *it;
-        const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
-        AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
-
-        if (pMedium->m->state == MediumState_Deleting)
-        {
-            rc = pMedium->unmarkForDeletion();
-            AssertComRC(rc);
-        }
-    }
-
-    /* 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);
-    }
-}
-
-
-HRESULT Medium::exportFile(const char *aFilename,
-                           const ComObjPtr<MediumFormat> &aFormat,
-                           MediumVariant_T aVariant,
-                           void *aVDImageIOCallbacks, void *aVDImageIOUser,
-                           const ComObjPtr<Progress> &aProgress)
-{
-    AssertPtrReturn(aFilename, E_INVALIDARG);
-    AssertReturn(!aFormat.isNull(), E_INVALIDARG);
-    AssertReturn(!aProgress.isNull(), E_INVALIDARG);
-
-    AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
-    HRESULT rc = S_OK;
-    Medium::Task *pTask = NULL;
-
-    try
-    {
-        // locking: we need the tree lock first because we access parent pointers
-        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
-        // and we need to write-lock the media involved
-        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-        /* Build the source lock list. */
-        MediumLockList *pSourceMediumLockList(new MediumLockList());
-        rc = createMediumLockList(true /* fFailIfInaccessible */,
-                                  false /* fMediumLockWrite */,
-                                  NULL,
-                                  *pSourceMediumLockList);
-        if (FAILED(rc))
-        {
-            delete pSourceMediumLockList;
-            throw rc;
-        }
-
-        rc = pSourceMediumLockList->Lock();
-        if (FAILED(rc))
-        {
-            delete pSourceMediumLockList;
-            throw setError(rc,
-                           tr("Failed to lock source media '%s'"),
-                           getLocationFull().c_str());
-        }
-
-        /* setup task object to carry out the operation asynchronously */
-        pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
-                                       aVariant, aVDImageIOCallbacks,
-                                       aVDImageIOUser, pSourceMediumLockList);
-        rc = pTask->rc();
-        AssertComRC(rc);
-        if (FAILED(rc))
-            throw rc;
-    }
-    catch (HRESULT aRC) { rc = aRC; }
-
-    if (SUCCEEDED(rc))
-        rc = startThread(pTask);
-    else if (pTask != NULL)
-        delete pTask;
-
-    return rc;
-}
-
-HRESULT Medium::importFile(const char *aFilename,
-                           const ComObjPtr<MediumFormat> &aFormat,
-                           MediumVariant_T aVariant,
-                           void *aVDImageIOCallbacks, void *aVDImageIOUser,
-                           const ComObjPtr<Medium> &aParent,
-                           const ComObjPtr<Progress> &aProgress)
-{
-    AssertPtrReturn(aFilename, E_INVALIDARG);
-    AssertReturn(!aFormat.isNull(), E_INVALIDARG);
-    AssertReturn(!aProgress.isNull(), E_INVALIDARG);
-
-    AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
-    HRESULT rc = S_OK;
-    Medium::Task *pTask = NULL;
-
-    try
-    {
-        // locking: we need the tree lock first because we access parent pointers
-        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
-        // and we need to write-lock the media involved
-        AutoMultiWriteLock2 alock(this, aParent COMMA_LOCKVAL_SRC_POS);
-
-        if (   m->state != MediumState_NotCreated
-            && m->state != MediumState_Created)
-            throw setStateError();
-
-        /* Build the target lock list. */
-        MediumLockList *pTargetMediumLockList(new MediumLockList());
-        rc = createMediumLockList(true /* fFailIfInaccessible */,
-                                  true /* fMediumLockWrite */,
-                                  aParent,
-                                  *pTargetMediumLockList);
-        if (FAILED(rc))
-        {
-            delete pTargetMediumLockList;
-            throw rc;
-        }
-
-        rc = pTargetMediumLockList->Lock();
-        if (FAILED(rc))
-        {
-            delete pTargetMediumLockList;
-            throw setError(rc,
-                           tr("Failed to lock target media '%s'"),
-                           getLocationFull().c_str());
-        }
-
-        /* setup task object to carry out the operation asynchronously */
-        pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,
-                                       aVariant, aVDImageIOCallbacks,
-                                       aVDImageIOUser, aParent,
-                                       pTargetMediumLockList);
-        rc = pTask->rc();
-        AssertComRC(rc);
-        if (FAILED(rc))
-            throw rc;
-
-        if (m->state == MediumState_NotCreated)
-            m->state = MediumState_Creating;
-    }
-    catch (HRESULT aRC) { rc = aRC; }
-
-    if (SUCCEEDED(rc))
-        rc = startThread(pTask);
-    else if (pTask != NULL)
-        delete pTask;
-
-    return rc;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Private methods
-//
-////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Performs extra checks if the medium can be closed and returns S_OK in
- * this case. Otherwise, returns a respective error message. Called by
- * Close() under the medium tree lock and the medium lock.
- *
- * @note Also reused by Medium::Reset().
- *
- * @note Caller must hold the media tree write lock!
- */
-HRESULT Medium::canClose()
-{
-    Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
-
-    if (getChildren().size() != 0)
-        return setError(VBOX_E_OBJECT_IN_USE,
-                        tr("Cannot close medium '%s' because it has %d child media"),
-                        m->strLocationFull.c_str(), getChildren().size());
-
-    return S_OK;
-}
-
-/**
- * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
- *
- * This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending
- * on the device type of this medium.
- *
- * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
- *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
- *
- * @note Caller must have locked the media tree lock for writing!
- */
-HRESULT Medium::unregisterWithVirtualBox(bool *pfNeedsGlobalSaveSettings)
-{
-    /* Note that we need to de-associate ourselves from the parent to let
-     * unregisterHardDisk() properly save the registry */
-
-    /* we modify mParent and access children */
-    Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
-
-    Medium *pParentBackup = m->pParent;
-    AssertReturn(getChildren().size() == 0, E_FAIL);
-    if (m->pParent)
-        deparent();
-
-    HRESULT rc = E_FAIL;
-    switch (m->devType)
-    {
-        case DeviceType_DVD:
-            rc = m->pVirtualBox->unregisterImage(this, DeviceType_DVD, pfNeedsGlobalSaveSettings);
-        break;
-
-        case DeviceType_Floppy:
-            rc = m->pVirtualBox->unregisterImage(this, DeviceType_Floppy, pfNeedsGlobalSaveSettings);
-        break;
-
-        case DeviceType_HardDisk:
-            rc = m->pVirtualBox->unregisterHardDisk(this, pfNeedsGlobalSaveSettings);
-        break;
-
-        default:
-        break;
-    }
-
-    if (FAILED(rc))
-    {
-        if (pParentBackup)
-        {
-            // re-associate with the parent as we are still relatives in the registry
-            m->pParent = pParentBackup;
-            m->pParent->m->llChildren.push_back(this);
-        }
-    }
-
-    return rc;
-}
-
-/**
- * Checks that the format ID is valid and sets it on success.
- *
- * Note that this method will caller-reference the format object on success!
- * This reference must be released somewhere to let the MediumFormat object be
- * uninitialized.
- *
- * @note Must be called from under this object's write lock.
- */
-HRESULT Medium::setFormat(const Utf8Str &aFormat)
-{
-    /* get the format object first */
-    {
-        SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
-        AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
-
-        unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
-        if (m->formatObj.isNull())
-            return setError(E_INVALIDARG,
-                            tr("Invalid medium storage format '%s'"),
-                            aFormat.c_str());
-
-        /* reference the format permanently to prevent its unexpected
-         * uninitialization */
-        HRESULT rc = m->formatObj->addCaller();
-        AssertComRCReturnRC(rc);
-
-        /* get properties (preinsert them as keys in the map). Note that the
-         * map doesn't grow over the object life time since the set of
-         * properties is meant to be constant. */
-
-        Assert(m->mapProperties.empty());
-
-        for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
-             it != m->formatObj->getProperties().end();
-             ++it)
-        {
-            m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
-        }
-    }
-
-    unconst(m->strFormat) = aFormat;
-
-    return S_OK;
-}
-
-/**
  * Returns the last error message collected by the vdErrorCall callback and
  * resets it.
@@ -5744,5 +5855,4 @@
 }
 
-
 /**
  * Starts a new thread driven by the appropriate Medium::Task::handler() method.
@@ -5774,87 +5884,4 @@
 
     return S_OK;
-}
-
-/**
- * Fix the parent UUID of all children to point to this medium as their
- * parent.
- */
-HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
-{
-    MediumLockList mediumLockList;
-    HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
-                                      false /* fMediumLockWrite */,
-                                      this,
-                                      mediumLockList);
-    AssertComRCReturnRC(rc);
-
-    try
-    {
-        PVBOXHDD hdd;
-        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
-        ComAssertRCThrow(vrc, E_FAIL);
-
-        try
-        {
-            MediumLockList::Base::iterator lockListBegin =
-                mediumLockList.GetBegin();
-            MediumLockList::Base::iterator lockListEnd =
-                mediumLockList.GetEnd();
-            for (MediumLockList::Base::iterator it = lockListBegin;
-                 it != lockListEnd;
-                 ++it)
-            {
-                MediumLock &mediumLock = *it;
-                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
-                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
-
-                // open the medium
-                vrc = VDOpen(hdd,
-                             pMedium->m->strFormat.c_str(),
-                             pMedium->m->strLocationFull.c_str(),
-                             VD_OPEN_FLAGS_READONLY,
-                             pMedium->m->vdImageIfaces);
-                if (RT_FAILURE(vrc))
-                    throw vrc;
-            }
-
-            for (MediaList::const_iterator it = childrenToReparent.begin();
-                 it != childrenToReparent.end();
-                 ++it)
-            {
-                /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
-                vrc = VDOpen(hdd,
-                             (*it)->m->strFormat.c_str(),
-                             (*it)->m->strLocationFull.c_str(),
-                             VD_OPEN_FLAGS_INFO,
-                             (*it)->m->vdImageIfaces);
-                if (RT_FAILURE(vrc))
-                    throw vrc;
-
-                vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
-                if (RT_FAILURE(vrc))
-                    throw vrc;
-
-                vrc = VDClose(hdd, false /* fDelete */);
-                if (RT_FAILURE(vrc))
-                    throw vrc;
-
-                (*it)->UnlockWrite(NULL);
-            }
-        }
-        catch (HRESULT aRC) { rc = aRC; }
-        catch (int aVRC)
-        {
-            throw setError(E_FAIL,
-                            tr("Could not update medium UUID references to parent '%s' (%s)"),
-                            m->strLocationFull.c_str(),
-                            vdError(aVRC).c_str());
-        }
-
-        VDDestroy(hdd);
-    }
-    catch (HRESULT aRC) { rc = aRC; }
-
-    return rc;
 }
 
Index: /trunk/src/VBox/Main/include/MediumImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/MediumImpl.h	(revision 33907)
+++ /trunk/src/VBox/Main/include/MediumImpl.h	(revision 33908)
@@ -213,4 +213,5 @@
                               bool aWait,
                               bool *pfNeedsGlobalSaveSettings);
+    Utf8Str getPreferredDiffFormat();
 
     HRESULT close(bool *pfNeedsGlobalSaveSettings, AutoCaller &autoCaller);
@@ -242,18 +243,4 @@
     HRESULT fixParentUuidOfChildren(const MediaList &childrenToReparent);
 
-    /**
-     * Used by IAppliance to export disk images.
-     *
-     * @param aFilename             Filename to create (UTF8).
-     * @param aFormat               Medium format for creating @a aFilename.
-     * @param aVariant              Which exact image format variant to use
-     *                              for the destination image.
-     * @param aVDImageIOCallbacks   Pointer to the callback table for a
-     *                              VDINTERFACEIO interface. May be NULL.
-     * @param aVDImageIOUser        Opaque data for the callbacks.
-     * @param aProgress             Progress object to use.
-     * @return
-     * @note The source format is defined by the Medium instance.
-     */
     HRESULT exportFile(const char *aFilename,
                        const ComObjPtr<MediumFormat> &aFormat,
@@ -261,19 +248,4 @@
                        void *aVDImageIOCallbacks, void *aVDImageIOUser,
                        const ComObjPtr<Progress> &aProgress);
-    /**
-     * Used by IAppliance to import disk images.
-     *
-     * @param aFilename             Filename to read (UTF8).
-     * @param aFormat               Medium format for reading @a aFilename.
-     * @param aVariant              Which exact image format variant to use
-     *                              for the destination image.
-     * @param aVDImageIOCallbacks   Pointer to the callback table for a
-     *                              VDINTERFACEIO interface. May be NULL.
-     * @param aVDImageIOUser        Opaque data for the callbacks.
-     * @param aParent               Parent medium. May be NULL.
-     * @param aProgress             Progress object to use.
-     * @return
-     * @note The destination format is defined by the Medium instance.
-     */
     HRESULT importFile(const char *aFilename,
                        const ComObjPtr<MediumFormat> &aFormat,
@@ -283,7 +255,4 @@
                        const ComObjPtr<Progress> &aProgress);
 
-    /** Returns a preferred format for a differencing hard disk. */
-    Utf8Str getPreferredDiffFormat();
-
 private:
 
@@ -298,8 +267,8 @@
     HRESULT setFormat(const Utf8Str &aFormat);
 
-    Utf8Str vdError(int aVRC);
-
     VDTYPE convertDeviceType();
     DeviceType_T convertToDeviceType(VDTYPE enmType);
+
+    Utf8Str vdError(int aVRC);
 
     static DECLCALLBACK(void) vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
