VirtualBox

Changeset 25887 in vbox


Ignore:
Timestamp:
Jan 18, 2010 11:58:58 AM (15 years ago)
Author:
vboxsync
Message:

Main: split up huge Medium task thread function, part 3

Location:
trunk/src/VBox/Main
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/MediumImpl.cpp

    r25886 r25887  
    51455145
    51465146/**
     5147 * Implementation code called from Medium::taskThread for the "merge" task.
     5148 * @param task
     5149 * @param pvdOperationIfaces
     5150 * @return
     5151 */
     5152HRESULT Medium::taskThreadMerge(Task &task, void *pvdOperationIfaces, bool fIsAsync)
     5153{
     5154    HRESULT rc = S_OK;
     5155
     5156    PVDINTERFACE vdOperationIfaces = (PVDINTERFACE)pvdOperationIfaces;
     5157
     5158    /* The lock is also used as a signal from the task initiator (which
     5159     * releases it only after RTThreadCreate()) that we can start the
     5160     * job. We don't actually need the lock for anything else since the
     5161     * object is protected by MediumState_Deleting and we don't modify
     5162     * its sensitive fields below */
     5163    {
     5164        AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
     5165    }
     5166
     5167    MergeChain *chain = task.d.chain.get();
     5168
     5169    try
     5170    {
     5171        PVBOXHDD hdd;
     5172        int vrc = VDCreate(m->vdDiskIfaces, &hdd);
     5173        ComAssertRCThrow(vrc, E_FAIL);
     5174
     5175        try
     5176        {
     5177            /* Open all hard disks in the chain (they are in the
     5178             * {parent,child} order in there. Note that we don't lock
     5179             * objects in this chain since they must be in states
     5180             * (Deleting and LockedWrite) that prevent from changing
     5181             * their format and location fields from outside. */
     5182
     5183            for (MergeChain::const_iterator it = chain->begin();
     5184                    it != chain->end(); ++ it)
     5185            {
     5186                /* complex sanity (sane complexity) */
     5187                Assert((chain->isForward() &&
     5188                        ((*it != chain->back() &&
     5189                            (*it)->m->state == MediumState_Deleting) ||
     5190                            (*it == chain->back() &&
     5191                            (*it)->m->state == MediumState_LockedWrite))) ||
     5192                        (!chain->isForward() &&
     5193                        ((*it != chain->front() &&
     5194                            (*it)->m->state == MediumState_Deleting) ||
     5195                            (*it == chain->front() &&
     5196                            (*it)->m->state == MediumState_LockedWrite))));
     5197
     5198                Assert(*it == chain->target() ||
     5199                        (*it)->m->backRefs.size() == 0);
     5200
     5201                /* open the first image with VDOPEN_FLAGS_INFO because
     5202                 * it's not necessarily the base one */
     5203                vrc = VDOpen(hdd, (*it)->m->strFormat.c_str(),
     5204                                (*it)->m->strLocationFull.c_str(),
     5205                                it == chain->begin() ?
     5206                                    VD_OPEN_FLAGS_INFO : 0,
     5207                                (*it)->m->vdDiskIfaces);
     5208                if (RT_FAILURE(vrc))
     5209                    throw vrc;
     5210            }
     5211
     5212            unsigned start = chain->isForward() ?
     5213                0 : (unsigned)chain->size() - 1;
     5214            unsigned end = chain->isForward() ?
     5215                (unsigned)chain->size() - 1 : 0;
     5216
     5217            vrc = VDMerge(hdd, start, end, vdOperationIfaces);
     5218            if (RT_FAILURE(vrc))
     5219                throw vrc;
     5220
     5221            /* update parent UUIDs */
     5222            /// @todo VDMerge should be taught to do so, including the
     5223            /// multiple children case
     5224            if (chain->isForward())
     5225            {
     5226                /* target's UUID needs to be updated (note that target
     5227                 * is the only image in the container on success) */
     5228                vrc = VDSetParentUuid(hdd, 0, chain->parent()->m->id);
     5229                if (RT_FAILURE(vrc))
     5230                    throw vrc;
     5231            }
     5232            else
     5233            {
     5234                /* we need to update UUIDs of all source's children
     5235                 * which cannot be part of the container at once so
     5236                 * add each one in there individually */
     5237                if (chain->children().size() > 0)
     5238                {
     5239                    for (MediaList::const_iterator it = chain->children().begin();
     5240                            it != chain->children().end();
     5241                            ++it)
     5242                    {
     5243                        /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
     5244                        vrc = VDOpen(hdd, (*it)->m->strFormat.c_str(),
     5245                                        (*it)->m->strLocationFull.c_str(),
     5246                                        VD_OPEN_FLAGS_INFO,
     5247                                        (*it)->m->vdDiskIfaces);
     5248                        if (RT_FAILURE(vrc))
     5249                            throw vrc;
     5250
     5251                        vrc = VDSetParentUuid(hdd, 1,
     5252                                                chain->target()->m->id);
     5253                        if (RT_FAILURE(vrc))
     5254                            throw vrc;
     5255
     5256                        vrc = VDClose(hdd, false /* fDelete */);
     5257                        if (RT_FAILURE(vrc))
     5258                            throw vrc;
     5259                    }
     5260                }
     5261            }
     5262        }
     5263        catch (HRESULT aRC) { rc = aRC; }
     5264        catch (int aVRC)
     5265        {
     5266            throw setError(E_FAIL,
     5267                            tr("Could not merge the hard disk '%s' to '%s'%s"),
     5268                            chain->source()->m->strLocationFull.raw(),
     5269                            chain->target()->m->strLocationFull.raw(),
     5270                            vdError(aVRC).raw());
     5271        }
     5272
     5273        VDDestroy(hdd);
     5274    }
     5275    catch (HRESULT aRC) { rc = aRC; }
     5276
     5277    HRESULT rc2;
     5278
     5279    bool saveSettingsFailed = false;
     5280
     5281    if (SUCCEEDED(rc))
     5282    {
     5283        /* all hard disks but the target were successfully deleted by
     5284         * VDMerge; reparent the last one and uninitialize deleted */
     5285
     5286        /* we set mParent & children() (note that thatLock is released
     5287         * here), but lock VirtualBox first to follow the rule */
     5288        AutoWriteLock alock1(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
     5289        AutoWriteLock alock2(m->pVirtualBox->hardDiskTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     5290
     5291        Medium *pSource = chain->source();
     5292        Medium *pTarget = chain->target();
     5293
     5294        if (chain->isForward())
     5295        {
     5296            /* first, unregister the target since it may become a base
     5297             * hard disk which needs re-registration */
     5298            rc2 = pTarget->m->pVirtualBox->unregisterHardDisk(pTarget, false /* aSaveSettings */);
     5299            AssertComRC(rc2);
     5300
     5301            /* then, reparent it and disconnect the deleted branch at
     5302             * both ends (chain->parent() is source's parent) */
     5303            pTarget->deparent();
     5304            pTarget->m->pParent = chain->parent();
     5305            if (pTarget->m->pParent)
     5306            {
     5307                pTarget->m->pParent->m->llChildren.push_back(pTarget);
     5308                pSource->deparent();
     5309            }
     5310
     5311            /* then, register again */
     5312            rc2 = pTarget->m->pVirtualBox->registerHardDisk(pTarget, false /* aSaveSettings */);
     5313            AssertComRC(rc2);
     5314        }
     5315        else
     5316        {
     5317            Assert(pTarget->getChildren().size() == 1);
     5318            Medium *targetChild = pTarget->getChildren().front();
     5319
     5320            /* disconnect the deleted branch at the elder end */
     5321            targetChild->deparent();
     5322
     5323            const MediaList &children = chain->children();
     5324
     5325            /* reparent source's chidren and disconnect the deleted
     5326             * branch at the younger end m*/
     5327            if (children.size() > 0)
     5328            {
     5329                /* obey {parent,child} lock order */
     5330                AutoWriteLock sourceLock(pSource COMMA_LOCKVAL_SRC_POS);
     5331
     5332                for (MediaList::const_iterator it = children.begin();
     5333                        it != children.end();
     5334                        ++it)
     5335                {
     5336                    AutoWriteLock childLock(*it COMMA_LOCKVAL_SRC_POS);
     5337
     5338                    Medium *p = *it;
     5339                    p->deparent();  // removes p from source
     5340                    pTarget->m->llChildren.push_back(p);
     5341                    p->m->pParent = pTarget;
     5342                }
     5343            }
     5344        }
     5345
     5346        /* try to save the hard disk registry */
     5347        rc = m->pVirtualBox->saveSettings();
     5348
     5349        if (SUCCEEDED(rc))
     5350        {
     5351            /* unregister and uninitialize all hard disks in the chain
     5352             * but the target */
     5353
     5354            for (MergeChain::iterator it = chain->begin();
     5355                    it != chain->end();)
     5356            {
     5357                if (*it == chain->target())
     5358                {
     5359                    ++ it;
     5360                    continue;
     5361                }
     5362
     5363                rc2 = (*it)->m->pVirtualBox->
     5364                    unregisterHardDisk(*it, false /* aSaveSettings */);
     5365                AssertComRC(rc2);
     5366
     5367                /* now, uninitialize the deleted hard disk (note that
     5368                 * due to the Deleting state, uninit() will not touch
     5369                 * the parent-child relationship so we need to
     5370                 * uninitialize each disk individually) */
     5371
     5372                /* note that the operation initiator hard disk (which is
     5373                 * normally also the source hard disk) is a special case
     5374                 * -- there is one more caller added by Task to it which
     5375                 * we must release. Also, if we are in sync mode, the
     5376                 * caller may still hold an AutoCaller instance for it
     5377                 * and therefore we cannot uninit() it (it's therefore
     5378                 * the caller's responsibility) */
     5379                if (*it == this)
     5380                    task.m_autoCaller.release();
     5381
     5382                /* release the caller added by MergeChain before
     5383                 * uninit() */
     5384                (*it)->releaseCaller();
     5385
     5386                if (fIsAsync || *it != this)
     5387                    (*it)->uninit();
     5388
     5389                /* delete (to prevent uninitialization in MergeChain
     5390                 * dtor) and advance to the next item */
     5391                it = chain->erase(it);
     5392            }
     5393
     5394            /* Note that states of all other hard disks (target, parent,
     5395             * children) will be restored by the MergeChain dtor */
     5396        }
     5397        else
     5398        {
     5399            /* too bad if we fail, but we'll need to rollback everything
     5400             * we did above to at least keep the HD tree in sync with
     5401             * the current registry on disk */
     5402
     5403            saveSettingsFailed = true;
     5404
     5405            /// @todo NEWMEDIA implement a proper undo
     5406
     5407            AssertFailed();
     5408        }
     5409    }
     5410
     5411    if (FAILED(rc))
     5412    {
     5413        /* Here we come if either VDMerge() failed (in which case we
     5414         * assume that it tried to do everything to make a further
     5415         * retry possible -- e.g. not deleted intermediate hard disks
     5416         * and so on) or VirtualBox::saveSettings() failed (where we
     5417         * should have the original tree but with intermediate storage
     5418         * units deleted by VDMerge()). We have to only restore states
     5419         * (through the MergeChain dtor) unless we are run synchronously
     5420         * in which case it's the responsibility of the caller as stated
     5421         * in the mergeTo() docs. The latter also implies that we
     5422         * don't own the merge chain, so release it in this case. */
     5423
     5424        if (!fIsAsync)
     5425            task.d.chain.release();
     5426
     5427        NOREF(saveSettingsFailed);
     5428    }
     5429
     5430    return rc;
     5431}
     5432
     5433/**
     5434 * Implementation code called from Medium::taskThread for the "clone" task.
     5435 * @param task
     5436 * @param pvdOperationIfaces
     5437 * @return
     5438 */
     5439HRESULT Medium::taskThreadClone(Task &task, void *pvdOperationIfaces)
     5440{
     5441    HRESULT rc = S_OK;
     5442
     5443    PVDINTERFACE vdOperationIfaces = (PVDINTERFACE)pvdOperationIfaces;
     5444
     5445    ComObjPtr<Medium> &pTarget = task.d.target;
     5446    ComObjPtr<Medium> &pParent = task.d.parentDisk;
     5447
     5448    /* Lock all in {parent,child} order. The lock is also used as a
     5449     * signal from the task initiator (which releases it only after
     5450     * RTThreadCreate()) that we can start the job. */
     5451    AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
     5452
     5453    ImageChain *srcChain = task.d.source.get();
     5454    ImageChain *parentChain = task.d.parent.get();
     5455
     5456    uint64_t size = 0, logicalSize = 0;
     5457
     5458    /* The object may request a specific UUID (through a special form of
     5459     * the setLocation() argument). Otherwise we have to generate it */
     5460    Guid targetId = pTarget->m->id;
     5461    bool generateUuid = targetId.isEmpty();
     5462    if (generateUuid)
     5463    {
     5464        targetId.create();
     5465        /* VirtualBox::registerHardDisk() will need UUID */
     5466        unconst(pTarget->m->id) = targetId;
     5467    }
     5468
     5469    try
     5470    {
     5471        PVBOXHDD hdd;
     5472        int vrc = VDCreate(m->vdDiskIfaces, &hdd);
     5473        ComAssertRCThrow(vrc, E_FAIL);
     5474
     5475        try
     5476        {
     5477            /* Open all hard disk images in the source chain. */
     5478            for (MediaList::const_iterator it = srcChain->begin();
     5479                 it != srcChain->end();
     5480                 ++it)
     5481            {
     5482                /* sanity check */
     5483                Assert((*it)->m->state == MediumState_LockedRead);
     5484
     5485                /** Open all images in read-only mode. */
     5486                vrc = VDOpen(hdd,
     5487                             (*it)->m->strFormat.c_str(),
     5488                             (*it)->m->strLocationFull.c_str(),
     5489                             VD_OPEN_FLAGS_READONLY,
     5490                             (*it)->m->vdDiskIfaces);
     5491                if (RT_FAILURE(vrc))
     5492                    throw setError(E_FAIL,
     5493                                    tr("Could not open the hard disk storage unit '%s'%s"),
     5494                                    (*it)->m->strLocationFull.raw(),
     5495                                    vdError(vrc).raw());
     5496            }
     5497
     5498            Utf8Str targetFormat(pTarget->m->strFormat);
     5499            Utf8Str targetLocation(pTarget->m->strLocationFull);
     5500
     5501            Assert(    pTarget->m->state == MediumState_Creating
     5502                    || pTarget->m->state == MediumState_LockedWrite);
     5503            Assert(m->state == MediumState_LockedRead);
     5504            Assert(pParent.isNull() || pParent->m->state == MediumState_LockedRead);
     5505
     5506            /* unlock before the potentially lengthy operation */
     5507            thisLock.leave();
     5508
     5509            /* ensure the target directory exists */
     5510            rc = VirtualBox::ensureFilePathExists(targetLocation);
     5511            if (FAILED(rc)) throw rc;
     5512
     5513            PVBOXHDD targetHdd;
     5514            vrc = VDCreate(m->vdDiskIfaces, &targetHdd);
     5515            ComAssertRCThrow(vrc, E_FAIL);
     5516
     5517            try
     5518            {
     5519                /* Open all hard disk images in the parent chain. */
     5520                for (MediaList::const_iterator it = parentChain->begin();
     5521                     it != parentChain->end();
     5522                     ++it)
     5523                {
     5524                    /** @todo r=klaus (*it) is not locked, lots of
     5525                        * race opportunities below */
     5526                    /* sanity check */
     5527                    Assert(    (*it)->m->state == MediumState_LockedRead
     5528                            || (*it)->m->state == MediumState_LockedWrite);
     5529
     5530                    /* Open all images in appropriate mode. */
     5531                    vrc = VDOpen(targetHdd,
     5532                                 (*it)->m->strFormat.c_str(),
     5533                                 (*it)->m->strLocationFull.c_str(),
     5534                                 ((*it)->m->state == MediumState_LockedWrite) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
     5535                                 (*it)->m->vdDiskIfaces);
     5536                    if (RT_FAILURE(vrc))
     5537                        throw setError(E_FAIL,
     5538                                       tr("Could not open the hard disk storage unit '%s'%s"),
     5539                                       (*it)->m->strLocationFull.raw(),
     5540                                       vdError(vrc).raw());
     5541                }
     5542
     5543                /** @todo r=klaus target isn't locked, race getting the state */
     5544                vrc = VDCopy(hdd,
     5545                             VD_LAST_IMAGE,
     5546                             targetHdd,
     5547                             targetFormat.c_str(),
     5548                             pTarget->m->state == MediumState_Creating ? targetLocation.raw() : (char *)NULL,
     5549                             false,
     5550                             0,
     5551                             task.d.variant,
     5552                             targetId.raw(),
     5553                             NULL,
     5554                             pTarget->m->vdDiskIfaces,
     5555                             vdOperationIfaces);
     5556                if (RT_FAILURE(vrc))
     5557                    throw setError(E_FAIL,
     5558                                    tr("Could not create the clone hard disk '%s'%s"),
     5559                                    targetLocation.raw(), vdError(vrc).raw());
     5560
     5561                size = VDGetFileSize(targetHdd, 0);
     5562                logicalSize = VDGetSize(targetHdd, 0) / _1M;
     5563            }
     5564            catch (HRESULT aRC) { rc = aRC; }
     5565
     5566            VDDestroy(targetHdd);
     5567        }
     5568        catch (HRESULT aRC) { rc = aRC; }
     5569
     5570        VDDestroy(hdd);
     5571    }
     5572    catch (HRESULT aRC) { rc = aRC; }
     5573
     5574    /* Only do the parent changes for newly created images. */
     5575    if (pTarget->m->state == MediumState_Creating)
     5576    {
     5577        if (SUCCEEDED(rc))
     5578        {
     5579            /* we set mParent & children() (note that thatLock is released
     5580                * here), but lock VirtualBox first to follow the rule */
     5581            AutoWriteLock alock1(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
     5582            AutoWriteLock alock2(m->pVirtualBox->hardDiskTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     5583
     5584            Assert(pTarget->m->pParent.isNull());
     5585
     5586            if (pParent)
     5587            {
     5588                /* associate the clone with the parent and deassociate
     5589                    * from VirtualBox */
     5590                pTarget->m->pParent = pParent;
     5591                pParent->m->llChildren.push_back(pTarget);
     5592
     5593                /* register with mVirtualBox as the last step and move to
     5594                    * Created state only on success (leaving an orphan file is
     5595                    * better than breaking media registry consistency) */
     5596                rc = pParent->m->pVirtualBox->registerHardDisk(pTarget);
     5597
     5598                if (FAILED(rc))
     5599                    /* break parent association on failure to register */
     5600                    pTarget->deparent();     // removes target from parent
     5601            }
     5602            else
     5603            {
     5604                /* just register  */
     5605                rc = m->pVirtualBox->registerHardDisk(pTarget);
     5606            }
     5607        }
     5608    }
     5609
     5610    thisLock.maybeEnter();
     5611
     5612    if (pTarget->m->state == MediumState_Creating)
     5613    {
     5614        if (SUCCEEDED(rc))
     5615        {
     5616            pTarget->m->state = MediumState_Created;
     5617
     5618            pTarget->m->size = size;
     5619            pTarget->m->logicalSize = logicalSize;
     5620        }
     5621        else
     5622        {
     5623            /* back to NotCreated on failure */
     5624            pTarget->m->state = MediumState_NotCreated;
     5625
     5626            /* reset UUID to prevent it from being reused next time */
     5627            if (generateUuid)
     5628                unconst(pTarget->m->id).clear();
     5629        }
     5630    }
     5631
     5632    /* Everything is explicitly unlocked when the task exits,
     5633     * as the task destruction also destroys the source chain. */
     5634
     5635    /* Make sure the source chain is released early. It could happen
     5636     * that we get a deadlock in Appliance::Import when Medium::Close
     5637     * is called & the source chain is released at the same time. */
     5638    task.d.source.reset();
     5639
     5640    return rc;
     5641}
     5642
     5643
     5644/**
    51475645 * Thread function for time-consuming tasks.
    51485646 *
     
    52005698        break;
    52015699
    5202         ////////////////////////////////////////////////////////////////////////
    5203 
    52045700        case Task::Merge:
    5205         {
    5206             /* The lock is also used as a signal from the task initiator (which
    5207              * releases it only after RTThreadCreate()) that we can start the
    5208              * job. We don't actually need the lock for anything else since the
    5209              * object is protected by MediumState_Deleting and we don't modify
    5210              * its sensitive fields below */
    5211             {
    5212                 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
    5213             }
    5214 
    5215             MergeChain *chain = task->d.chain.get();
    5216 
    5217 #if 0
    5218             LogFlow(("*** MERGE forward = %RTbool\n", chain->isForward()));
    5219 #endif
    5220 
    5221             try
    5222             {
    5223                 PVBOXHDD hdd;
    5224                 int vrc = VDCreate(that->m->vdDiskIfaces, &hdd);
    5225                 ComAssertRCThrow(vrc, E_FAIL);
    5226 
    5227                 try
    5228                 {
    5229                     /* Open all hard disks in the chain (they are in the
    5230                      * {parent,child} order in there. Note that we don't lock
    5231                      * objects in this chain since they must be in states
    5232                      * (Deleting and LockedWrite) that prevent from changing
    5233                      * their format and location fields from outside. */
    5234 
    5235                     for (MergeChain::const_iterator it = chain->begin();
    5236                          it != chain->end(); ++ it)
    5237                     {
    5238                         /* complex sanity (sane complexity) */
    5239                         Assert((chain->isForward() &&
    5240                                 ((*it != chain->back() &&
    5241                                   (*it)->m->state == MediumState_Deleting) ||
    5242                                  (*it == chain->back() &&
    5243                                   (*it)->m->state == MediumState_LockedWrite))) ||
    5244                                (!chain->isForward() &&
    5245                                 ((*it != chain->front() &&
    5246                                   (*it)->m->state == MediumState_Deleting) ||
    5247                                  (*it == chain->front() &&
    5248                                   (*it)->m->state == MediumState_LockedWrite))));
    5249 
    5250                         Assert(*it == chain->target() ||
    5251                                (*it)->m->backRefs.size() == 0);
    5252 
    5253                         /* open the first image with VDOPEN_FLAGS_INFO because
    5254                          * it's not necessarily the base one */
    5255                         vrc = VDOpen(hdd, (*it)->m->strFormat.c_str(),
    5256                                       (*it)->m->strLocationFull.c_str(),
    5257                                       it == chain->begin() ?
    5258                                           VD_OPEN_FLAGS_INFO : 0,
    5259                                       (*it)->m->vdDiskIfaces);
    5260                         if (RT_FAILURE(vrc))
    5261                             throw vrc;
    5262 #if 0
    5263                         LogFlow(("*** MERGE disk = %s\n", (*it)->m->strLocationFull.raw()));
    5264 #endif
    5265                     }
    5266 
    5267                     unsigned start = chain->isForward() ?
    5268                         0 : (unsigned)chain->size() - 1;
    5269                     unsigned end = chain->isForward() ?
    5270                         (unsigned)chain->size() - 1 : 0;
    5271 #if 0
    5272                     LogFlow(("*** MERGE from %d to %d\n", start, end));
    5273 #endif
    5274                     vrc = VDMerge(hdd, start, end, vdOperationIfaces);
    5275                     if (RT_FAILURE(vrc))
    5276                         throw vrc;
    5277 
    5278                     /* update parent UUIDs */
    5279                     /// @todo VDMerge should be taught to do so, including the
    5280                     /// multiple children case
    5281                     if (chain->isForward())
    5282                     {
    5283                         /* target's UUID needs to be updated (note that target
    5284                          * is the only image in the container on success) */
    5285                         vrc = VDSetParentUuid(hdd, 0, chain->parent()->m->id);
    5286                         if (RT_FAILURE(vrc))
    5287                             throw vrc;
    5288                     }
    5289                     else
    5290                     {
    5291                         /* we need to update UUIDs of all source's children
    5292                          * which cannot be part of the container at once so
    5293                          * add each one in there individually */
    5294                         if (chain->children().size() > 0)
    5295                         {
    5296                             for (MediaList::const_iterator it = chain->children().begin();
    5297                                  it != chain->children().end();
    5298                                  ++it)
    5299                             {
    5300                                 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
    5301                                 vrc = VDOpen(hdd, (*it)->m->strFormat.c_str(),
    5302                                              (*it)->m->strLocationFull.c_str(),
    5303                                              VD_OPEN_FLAGS_INFO,
    5304                                              (*it)->m->vdDiskIfaces);
    5305                                 if (RT_FAILURE(vrc))
    5306                                     throw vrc;
    5307 
    5308                                 vrc = VDSetParentUuid(hdd, 1,
    5309                                                       chain->target()->m->id);
    5310                                 if (RT_FAILURE(vrc))
    5311                                     throw vrc;
    5312 
    5313                                 vrc = VDClose(hdd, false /* fDelete */);
    5314                                 if (RT_FAILURE(vrc))
    5315                                     throw vrc;
    5316                             }
    5317                         }
    5318                     }
    5319                 }
    5320                 catch (HRESULT aRC) { rc = aRC; }
    5321                 catch (int aVRC)
    5322                 {
    5323                     throw setError(E_FAIL,
    5324                                    tr("Could not merge the hard disk '%s' to '%s'%s"),
    5325                                    chain->source()->m->strLocationFull.raw(),
    5326                                    chain->target()->m->strLocationFull.raw(),
    5327                                    that->vdError(aVRC).raw());
    5328                 }
    5329 
    5330                 VDDestroy(hdd);
    5331             }
    5332             catch (HRESULT aRC) { rc = aRC; }
    5333 
    5334             HRESULT rc2;
    5335 
    5336             bool saveSettingsFailed = false;
    5337 
    5338             if (SUCCEEDED(rc))
    5339             {
    5340                 /* all hard disks but the target were successfully deleted by
    5341                  * VDMerge; reparent the last one and uninitialize deleted */
    5342 
    5343                 /* we set mParent & children() (note that thatLock is released
    5344                  * here), but lock VirtualBox first to follow the rule */
    5345                 AutoWriteLock alock1(that->m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
    5346                 AutoWriteLock alock2(that->m->pVirtualBox->hardDiskTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    5347 
    5348                 Medium *pSource = chain->source();
    5349                 Medium *pTarget = chain->target();
    5350 
    5351                 if (chain->isForward())
    5352                 {
    5353                     /* first, unregister the target since it may become a base
    5354                      * hard disk which needs re-registration */
    5355                     rc2 = pTarget->m->pVirtualBox->unregisterHardDisk(pTarget, false /* aSaveSettings */);
    5356                     AssertComRC(rc2);
    5357 
    5358                     /* then, reparent it and disconnect the deleted branch at
    5359                      * both ends (chain->parent() is source's parent) */
    5360                     pTarget->deparent();
    5361                     pTarget->m->pParent = chain->parent();
    5362                     if (pTarget->m->pParent)
    5363                     {
    5364                         pTarget->m->pParent->m->llChildren.push_back(pTarget);
    5365                         pSource->deparent();
    5366                     }
    5367 
    5368                     /* then, register again */
    5369                     rc2 = pTarget->m->pVirtualBox->registerHardDisk(pTarget, false /* aSaveSettings */);
    5370                     AssertComRC(rc2);
    5371                 }
    5372                 else
    5373                 {
    5374                     Assert(pTarget->getChildren().size() == 1);
    5375                     Medium *targetChild = pTarget->getChildren().front();
    5376 
    5377                     /* disconnect the deleted branch at the elder end */
    5378                     targetChild->deparent();
    5379 
    5380                     const MediaList &children = chain->children();
    5381 
    5382                     /* reparent source's chidren and disconnect the deleted
    5383                      * branch at the younger end m*/
    5384                     if (children.size() > 0)
    5385                     {
    5386                         /* obey {parent,child} lock order */
    5387                         AutoWriteLock sourceLock(pSource COMMA_LOCKVAL_SRC_POS);
    5388 
    5389                         for (MediaList::const_iterator it = children.begin();
    5390                              it != children.end();
    5391                              ++it)
    5392                         {
    5393                             AutoWriteLock childLock(*it COMMA_LOCKVAL_SRC_POS);
    5394 
    5395                             Medium *p = *it;
    5396                             p->deparent();  // removes p from source
    5397                             pTarget->m->llChildren.push_back(p);
    5398                             p->m->pParent = pTarget;
    5399                         }
    5400                     }
    5401                 }
    5402 
    5403                 /* try to save the hard disk registry */
    5404                 rc = that->m->pVirtualBox->saveSettings();
    5405 
    5406                 if (SUCCEEDED(rc))
    5407                 {
    5408                     /* unregister and uninitialize all hard disks in the chain
    5409                      * but the target */
    5410 
    5411                     for (MergeChain::iterator it = chain->begin();
    5412                          it != chain->end();)
    5413                     {
    5414                         if (*it == chain->target())
    5415                         {
    5416                             ++ it;
    5417                             continue;
    5418                         }
    5419 
    5420                         rc2 = (*it)->m->pVirtualBox->
    5421                             unregisterHardDisk(*it, false /* aSaveSettings */);
    5422                         AssertComRC(rc2);
    5423 
    5424                         /* now, uninitialize the deleted hard disk (note that
    5425                          * due to the Deleting state, uninit() will not touch
    5426                          * the parent-child relationship so we need to
    5427                          * uninitialize each disk individually) */
    5428 
    5429                         /* note that the operation initiator hard disk (which is
    5430                          * normally also the source hard disk) is a special case
    5431                          * -- there is one more caller added by Task to it which
    5432                          * we must release. Also, if we are in sync mode, the
    5433                          * caller may still hold an AutoCaller instance for it
    5434                          * and therefore we cannot uninit() it (it's therefore
    5435                          * the caller's responsibility) */
    5436                         if (*it == that)
    5437                             task->m_autoCaller.release();
    5438 
    5439                         /* release the caller added by MergeChain before
    5440                          * uninit() */
    5441                         (*it)->releaseCaller();
    5442 
    5443                         if (fIsAsync || *it != that)
    5444                             (*it)->uninit();
    5445 
    5446                         /* delete (to prevent uninitialization in MergeChain
    5447                          * dtor) and advance to the next item */
    5448                         it = chain->erase(it);
    5449                     }
    5450 
    5451                     /* Note that states of all other hard disks (target, parent,
    5452                      * children) will be restored by the MergeChain dtor */
    5453                 }
    5454                 else
    5455                 {
    5456                     /* too bad if we fail, but we'll need to rollback everything
    5457                      * we did above to at least keep the HD tree in sync with
    5458                      * the current registry on disk */
    5459 
    5460                     saveSettingsFailed = true;
    5461 
    5462                     /// @todo NEWMEDIA implement a proper undo
    5463 
    5464                     AssertFailed();
    5465                 }
    5466             }
    5467 
    5468             if (FAILED(rc))
    5469             {
    5470                 /* Here we come if either VDMerge() failed (in which case we
    5471                  * assume that it tried to do everything to make a further
    5472                  * retry possible -- e.g. not deleted intermediate hard disks
    5473                  * and so on) or VirtualBox::saveSettings() failed (where we
    5474                  * should have the original tree but with intermediate storage
    5475                  * units deleted by VDMerge()). We have to only restore states
    5476                  * (through the MergeChain dtor) unless we are run synchronously
    5477                  * in which case it's the responsibility of the caller as stated
    5478                  * in the mergeTo() docs. The latter also implies that we
    5479                  * don't own the merge chain, so release it in this case. */
    5480 
    5481                 if (!fIsAsync)
    5482                     task->d.chain.release();
    5483 
    5484                 NOREF(saveSettingsFailed);
    5485             }
    5486 
    5487             break;
    5488         }
    5489 
    5490         ////////////////////////////////////////////////////////////////////////
     5701            rc = that->taskThreadMerge(*task, (void*)vdOperationIfaces, fIsAsync);
     5702        break;
    54915703
    54925704        case Task::Clone:
    5493         {
    5494             ComObjPtr<Medium> &pTarget = task->d.target;
    5495             ComObjPtr<Medium> &pParent = task->d.parentDisk;
    5496 
    5497             /* Lock all in {parent,child} order. The lock is also used as a
    5498              * signal from the task initiator (which releases it only after
    5499              * RTThreadCreate()) that we can start the job. */
    5500             AutoMultiWriteLock3 thatLock(that, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
    5501 
    5502             ImageChain *srcChain = task->d.source.get();
    5503             ImageChain *parentChain = task->d.parent.get();
    5504 
    5505             uint64_t size = 0, logicalSize = 0;
    5506 
    5507             /* The object may request a specific UUID (through a special form of
    5508              * the setLocation() argument). Otherwise we have to generate it */
    5509             Guid targetId = pTarget->m->id;
    5510             bool generateUuid = targetId.isEmpty();
    5511             if (generateUuid)
    5512             {
    5513                 targetId.create();
    5514                 /* VirtualBox::registerHardDisk() will need UUID */
    5515                 unconst(pTarget->m->id) = targetId;
    5516             }
    5517 
    5518             try
    5519             {
    5520                 PVBOXHDD hdd;
    5521                 int vrc = VDCreate(that->m->vdDiskIfaces, &hdd);
    5522                 ComAssertRCThrow(vrc, E_FAIL);
    5523 
    5524                 try
    5525                 {
    5526                     /* Open all hard disk images in the source chain. */
    5527                     for (MediaList::const_iterator it = srcChain->begin();
    5528                          it != srcChain->end();
    5529                          ++it)
    5530                     {
    5531                         /* sanity check */
    5532                         Assert((*it)->m->state == MediumState_LockedRead);
    5533 
    5534                         /** Open all images in read-only mode. */
    5535                         vrc = VDOpen(hdd, (*it)->m->strFormat.c_str(),
    5536                                      (*it)->m->strLocationFull.c_str(),
    5537                                      VD_OPEN_FLAGS_READONLY,
    5538                                      (*it)->m->vdDiskIfaces);
    5539                         if (RT_FAILURE(vrc))
    5540                         {
    5541                             throw setError(E_FAIL,
    5542                                            tr("Could not open the hard disk storage unit '%s'%s"),
    5543                                            (*it)->m->strLocationFull.raw(),
    5544                                            that->vdError(vrc).raw());
    5545                         }
    5546                     }
    5547 
    5548                     Utf8Str targetFormat(pTarget->m->strFormat);
    5549                     Utf8Str targetLocation(pTarget->m->strLocationFull);
    5550 
    5551                     Assert(    pTarget->m->state == MediumState_Creating
    5552                            ||  pTarget->m->state == MediumState_LockedWrite);
    5553                     Assert(that->m->state == MediumState_LockedRead);
    5554                     Assert(pParent.isNull() || pParent->m->state == MediumState_LockedRead);
    5555 
    5556                     /* unlock before the potentially lengthy operation */
    5557                     thatLock.leave();
    5558 
    5559                     /* ensure the target directory exists */
    5560                     rc = VirtualBox::ensureFilePathExists(targetLocation);
    5561                     if (FAILED(rc)) throw rc;
    5562 
    5563                     PVBOXHDD targetHdd;
    5564                     vrc = VDCreate(that->m->vdDiskIfaces, &targetHdd);
    5565                     ComAssertRCThrow(vrc, E_FAIL);
    5566 
    5567                     try
    5568                     {
    5569                         /* Open all hard disk images in the parent chain. */
    5570                         for (MediaList::const_iterator it = parentChain->begin();
    5571                              it != parentChain->end();
    5572                              ++it)
    5573                         {
    5574                             /** @todo r=klaus (*it) is not locked, lots of
    5575                              * race opportunities below */
    5576                             /* sanity check */
    5577                             Assert(    (*it)->m->state == MediumState_LockedRead
    5578                                    ||  (*it)->m->state == MediumState_LockedWrite);
    5579 
    5580                             /* Open all images in appropriate mode. */
    5581                             vrc = VDOpen(targetHdd, (*it)->m->strFormat.c_str(),
    5582                                          (*it)->m->strLocationFull.c_str(),
    5583                                          ((*it)->m->state == MediumState_LockedWrite) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
    5584                                          (*it)->m->vdDiskIfaces);
    5585                             if (RT_FAILURE(vrc))
    5586                             {
    5587                                 throw setError(E_FAIL,
    5588                                                tr("Could not open the hard disk storage unit '%s'%s"),
    5589                                                (*it)->m->strLocationFull.raw(),
    5590                                                that->vdError(vrc).raw());
    5591                             }
    5592                         }
    5593 
    5594                         /** @todo r=klaus target isn't locked, race getting the state */
    5595                         vrc = VDCopy(hdd, VD_LAST_IMAGE, targetHdd,
    5596                                      targetFormat.c_str(),
    5597                                      pTarget->m->state == MediumState_Creating ? targetLocation.raw() : (char *)NULL,
    5598                                      false, 0,
    5599                                      task->d.variant, targetId.raw(), NULL,
    5600                                      pTarget->m->vdDiskIfaces,
    5601                                      vdOperationIfaces);
    5602                         if (RT_FAILURE(vrc))
    5603                         {
    5604                             throw setError(E_FAIL,
    5605                                            tr("Could not create the clone hard disk '%s'%s"),
    5606                                            targetLocation.raw(), that->vdError(vrc).raw());
    5607                         }
    5608                         size = VDGetFileSize(targetHdd, 0);
    5609                         logicalSize = VDGetSize(targetHdd, 0) / _1M;
    5610                     }
    5611                     catch (HRESULT aRC) { rc = aRC; }
    5612 
    5613                     VDDestroy(targetHdd);
    5614                 }
    5615                 catch (HRESULT aRC) { rc = aRC; }
    5616 
    5617                 VDDestroy(hdd);
    5618             }
    5619             catch (HRESULT aRC) { rc = aRC; }
    5620 
    5621             /* Only do the parent changes for newly created images. */
    5622             if (pTarget->m->state == MediumState_Creating)
    5623             {
    5624                 if (SUCCEEDED(rc))
    5625                 {
    5626                     /* we set mParent & children() (note that thatLock is released
    5627                      * here), but lock VirtualBox first to follow the rule */
    5628                     AutoWriteLock alock1(that->m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
    5629                     AutoWriteLock alock2(that->m->pVirtualBox->hardDiskTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    5630 
    5631                     Assert(pTarget->m->pParent.isNull());
    5632 
    5633                     if (pParent)
    5634                     {
    5635                         /* associate the clone with the parent and deassociate
    5636                          * from VirtualBox */
    5637                         pTarget->m->pParent = pParent;
    5638                         pParent->m->llChildren.push_back(pTarget);
    5639 
    5640                         /* register with mVirtualBox as the last step and move to
    5641                          * Created state only on success (leaving an orphan file is
    5642                          * better than breaking media registry consistency) */
    5643                         rc = pParent->m->pVirtualBox->registerHardDisk(pTarget);
    5644 
    5645                         if (FAILED(rc))
    5646                             /* break parent association on failure to register */
    5647                             pTarget->deparent();     // removes target from parent
    5648                     }
    5649                     else
    5650                     {
    5651                         /* just register  */
    5652                         rc = that->m->pVirtualBox->registerHardDisk(pTarget);
    5653                     }
    5654                 }
    5655             }
    5656 
    5657             thatLock.maybeEnter();
    5658 
    5659             if (pTarget->m->state == MediumState_Creating)
    5660             {
    5661                 if (SUCCEEDED(rc))
    5662                 {
    5663                     pTarget->m->state = MediumState_Created;
    5664 
    5665                     pTarget->m->size = size;
    5666                     pTarget->m->logicalSize = logicalSize;
    5667                 }
    5668                 else
    5669                 {
    5670                     /* back to NotCreated on failure */
    5671                     pTarget->m->state = MediumState_NotCreated;
    5672 
    5673                     /* reset UUID to prevent it from being reused next time */
    5674                     if (generateUuid)
    5675                         unconst(pTarget->m->id).clear();
    5676                 }
    5677             }
    5678 
    5679             /* Everything is explicitly unlocked when the task exits,
    5680              * as the task destruction also destroys the source chain. */
    5681 
    5682             /* Make sure the source chain is released early. It could happen
    5683              * that we get a deadlock in Appliance::Import when Medium::Close
    5684              * is called & the source chain is released at the same time. */
    5685             task->d.source.reset();
    5686             break;
    5687         }
    5688 
    5689         ////////////////////////////////////////////////////////////////////////
     5705            rc = that->taskThreadClone(*task, (void*)vdOperationIfaces);
     5706        break;
    56905707
    56915708        case Task::Delete:
  • trunk/src/VBox/Main/include/MediumImpl.h

    r25886 r25887  
    311311    HRESULT taskThreadCreateBase(Task &task, void *pvdOperationIfaces);
    312312    HRESULT taskThreadCreateDiff(Task &task, void *pvdOperationIfaces, bool fIsAsync);
     313    HRESULT taskThreadMerge(Task &task, void *pvdOperationIfaces, bool fIsAsync);
     314    HRESULT taskThreadClone(Task &task, void *pvdOperationIfaces);
    313315
    314316    static DECLCALLBACK(int) taskThread(RTTHREAD thread, void *pvUser);
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette