VirtualBox

Changeset 37971 in vbox for trunk/src/VBox/Main


Ignore:
Timestamp:
Jul 14, 2011 3:01:07 PM (14 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
72884
Message:

Main/Machine: implement creating linked clones.
Frontends/VBoxManage: enable creating linked clones

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

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/include/MachineImplCloneVM.h

    r37533 r37971  
    3434protected:
    3535    HRESULT run();
     36    HRESULT createDiffHelper(const ComObjPtr<Medium> &pParent,
     37                             const Utf8Str &strSnapshotFolder,
     38                             RTCList<ComObjPtr<Medium> > *pNewMedia,
     39                             ComObjPtr<Medium> *ppDiff);
    3640    void destroy();
    3741
  • trunk/src/VBox/Main/src-server/MachineImpl.cpp

    r37932 r37971  
    62606260        optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
    62616261
    6262     AssertReturn(!optList.contains(CloneOptions_Link), E_NOTIMPL);
     6262    AssertReturn(!optList.contains(CloneOptions_Link) || (isSnapshotMachine() && mode == CloneMode_MachineState), E_INVALIDARG);
    62636263    AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
    62646264
    62656265    AutoCaller autoCaller(this);
    62666266    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     6267
    62676268
    62686269    MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
     
    94559456/**
    94569457 * Deletes implicit differencing hard disks created either by
    9457  * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
    9458  *
    9459  * Note that to delete hard disks created by #AttachMedium() this method is
     9458 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
     9459 *
     9460 * Note that to delete hard disks created by #AttachDevice() this method is
    94609461 * called from #fixupMedia() when the changes are rolled back.
    94619462 *
  • trunk/src/VBox/Main/src-server/MachineImplCloneVM.cpp

    r37905 r37971  
    3636    ComPtr<IMedium>         pMedium;
    3737    ULONG                   uWeight;
    38 }MEDIUMTASK;
     38} MEDIUMTASK;
    3939
    4040typedef struct
     
    4242    RTCList<MEDIUMTASK>     chain;
    4343    bool                    fCreateDiffs;
    44 }MEDIUMTASKCHAIN;
     44    bool                    fAttachLinked;
     45} MEDIUMTASKCHAIN;
    4546
    4647typedef struct
     
    4950    Utf8Str                 strSaveStateFile;
    5051    ULONG                   uWeight;
    51 }SAVESTATETASK;
     52} SAVESTATETASK;
    5253
    5354// The private class
     
    330331            if (machine == d->pOldMachineState)
    331332                fCreateDiffs = true;
     333            /* If we want to create a linked clone just attach the medium
     334             * associated with the snapshot. The rest is taken care of by
     335             * attach already, so no need to duplicate this. */
     336            bool fAttachLinked = false;
     337            if (d->options.contains(CloneOptions_Link))
     338                fAttachLinked = true;
    332339            SafeIfaceArray<IMediumAttachment> sfaAttachments;
    333340            rc = machine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
     
    358365                MEDIUMTASKCHAIN mtc;
    359366                mtc.fCreateDiffs = fCreateDiffs;
    360                 while(!pSrcMedium.isNull())
     367                mtc.fAttachLinked = fAttachLinked;
     368
     369                if (d->mode == CloneMode_MachineState)
    361370                {
    362371                    /* Refresh the state so that the file size get read. */
     
    371380                    MEDIUMTASK mt;
    372381                    mt.pMedium = pSrcMedium;
    373                     mt.uWeight = (lSize + _1M - 1) / _1M;
     382                    if (fAttachLinked)
     383                        mt.uWeight = 0; /* dummy */
     384                    else
     385                        mt.uWeight = (lSize + _1M - 1) / _1M;
    374386                    mtc.chain.append(mt);
    375 
    376                     /* Query next parent. */
    377                     rc = pSrcMedium->COMGETTER(Parent)(pSrcMedium.asOutParam());
    378                     if (FAILED(rc)) throw rc;
    379                 };
    380                 /* Currently the creation of diff images involves reading at least
    381                  * the biggest parent in the previous chain. So even if the new
    382                  * diff image is small in size, it could need some time to create
    383                  * it. Adding the biggest size in the chain should balance this a
    384                  * little bit more, i.e. the weight is the sum of the data which
    385                  * needs to be read and written. */
    386                 uint64_t uMaxSize = 0;
    387                 for (size_t e = mtc.chain.size(); e > 0; --e)
     387                }
     388                else
    388389                {
    389                     MEDIUMTASK &mt = mtc.chain.at(e - 1);
    390                     mt.uWeight += uMaxSize;
    391 
    392                     /* Calculate progress data */
     390                    /** @todo r=klaus this puts way too many images in the list
     391                     * when cloning a snapshot (sub)tree, which means that more
     392                     * images are cloned than necessary. It is just the easiest
     393                     * way to get a working VM, as getting the image
     394                     * parent/child relationships right for only the bare
     395                     * minimum cloning is rather tricky. */
     396                    while (!pSrcMedium.isNull())
     397                    {
     398                        /* Refresh the state so that the file size get read. */
     399                        MediumState_T e;
     400                        rc = pSrcMedium->RefreshState(&e);
     401                        if (FAILED(rc)) throw rc;
     402                        LONG64 lSize;
     403                        rc = pSrcMedium->COMGETTER(Size)(&lSize);
     404                        if (FAILED(rc)) throw rc;
     405
     406                        /* Save the current medium, for later cloning. */
     407                        MEDIUMTASK mt;
     408                        mt.pMedium = pSrcMedium;
     409                        mt.uWeight = (lSize + _1M - 1) / _1M;
     410                        mtc.chain.append(mt);
     411
     412                        /* Query next parent. */
     413                        rc = pSrcMedium->COMGETTER(Parent)(pSrcMedium.asOutParam());
     414                        if (FAILED(rc)) throw rc;
     415                    }
     416                }
     417
     418                if (fAttachLinked)
     419                {
     420                    /* Implicit diff creation as part of attach is a pretty cheap
     421                     * operation, and does only need one operation per attachment. */
    393422                    ++uCount;
    394                     uTotalWeight += mt.uWeight;
    395 
    396                     /* Save the max size for better weighting of diff image
    397                      * creation. */
    398                     uMaxSize = RT_MAX(uMaxSize, mt.uWeight);
     423                    uTotalWeight += 1;  /* 1MB per attachment */
     424                }
     425                else
     426                {
     427                    /* Currently the copying of diff images involves reading at least
     428                     * the biggest parent in the previous chain. So even if the new
     429                     * diff image is small in size, it could need some time to create
     430                     * it. Adding the biggest size in the chain should balance this a
     431                     * little bit more, i.e. the weight is the sum of the data which
     432                     * needs to be read and written. */
     433                    uint64_t uMaxSize = 0;
     434                    for (size_t e = mtc.chain.size(); e > 0; --e)
     435                    {
     436                        MEDIUMTASK &mt = mtc.chain.at(e - 1);
     437                        mt.uWeight += uMaxSize;
     438
     439                        /* Calculate progress data */
     440                        ++uCount;
     441                        uTotalWeight += mt.uWeight;
     442
     443                        /* Save the max size for better weighting of diff image
     444                         * creation. */
     445                        uMaxSize = RT_MAX(uMaxSize, mt.uWeight);
     446                    }
    399447                }
    400448                d->llMedias.append(mtc);
     
    472520    strTrgMachineFolder.stripFilename();
    473521
    474     RTCList< ComObjPtr<Medium> > newMedias; /* All created images */
     522    RTCList<ComObjPtr<Medium> > newMedia;  /* All created images */
    475523    RTCList<Utf8Str> newFiles;              /* All extra created files (save states, ...) */
    476524    try
     
    499547            trgMCF.llFirstSnapshot.clear();
    500548            trgMCF.uuidCurrentSnapshot.clear();
    501         }else
    502         if (   d->mode == CloneMode_MachineAndChildStates
    503             && !sn.uuid.isEmpty())
     549        }
     550        else if (   d->mode == CloneMode_MachineAndChildStates
     551                 && !sn.uuid.isEmpty())
    504552        {
    505553            /* Copy the snapshot data to the current machine. */
     
    569617                if (FAILED(rc)) throw rc;
    570618
    571                 /* Is a clone already there? */
    572                 TStrMediumMap::iterator it = map.find(Utf8Str(bstrSrcId));
    573                 if (it != map.end())
    574                     pNewParent = it->second;
     619                if (mtc.fAttachLinked)
     620                {
     621                    IMedium *pTmp = pMedium;
     622                    ComObjPtr<Medium> pLMedium = static_cast<Medium*>(pTmp);
     623                    if (pLMedium.isNull())
     624                        throw E_POINTER;
     625                    if (pLMedium->isReadOnly())
     626                    {
     627                        ComObjPtr<Medium> pDiff;
     628                        /* create the diff under the snapshot medium */
     629                        rc = createDiffHelper(pLMedium, strTrgSnapshotFolder,
     630                                              &newMedia, &pDiff);
     631                        if (FAILED(rc)) throw rc;
     632                        map.insert(TStrMediumPair(Utf8Str(bstrSrcId), pDiff));
     633                        /* diff image has to be used... */
     634                        pNewParent = pDiff;
     635                    }
     636                    else
     637                    {
     638                        /* Attach the medium directly, as its type is not
     639                         * subject to diff creation. */
     640                        newMedia.append(pLMedium);
     641                        map.insert(TStrMediumPair(Utf8Str(bstrSrcId), pLMedium));
     642                        pNewParent = pLMedium;
     643                    }
     644                }
    575645                else
    576646                {
    577                     ComPtr<IMediumFormat> pSrcFormat;
    578                     rc = pMedium->COMGETTER(MediumFormat)(pSrcFormat.asOutParam());
    579                     ULONG uSrcCaps = 0;
    580                     rc = pSrcFormat->COMGETTER(Capabilities)(&uSrcCaps);
    581                     if (FAILED(rc)) throw rc;
    582 
    583                     /* Default format? */
    584                     Utf8Str strDefaultFormat;
    585                     p->mParent->getDefaultHardDiskFormat(strDefaultFormat);
    586                     Bstr bstrSrcFormat(strDefaultFormat);
    587                     ULONG srcVar = MediumVariant_Standard;
    588                     /* Is the source file based? */
    589                     if ((uSrcCaps & MediumFormatCapabilities_File) == MediumFormatCapabilities_File)
     647                    /* Is a clone already there? */
     648                    TStrMediumMap::iterator it = map.find(Utf8Str(bstrSrcId));
     649                    if (it != map.end())
     650                        pNewParent = it->second;
     651                    else
    590652                    {
    591                         /* Yes, just use the source format. Otherwise the defaults
    592                          * will be used. */
    593                         rc = pMedium->COMGETTER(Format)(bstrSrcFormat.asOutParam());
    594                         if (FAILED(rc)) throw rc;
    595                         rc = pMedium->COMGETTER(Variant)(&srcVar);
    596                         if (FAILED(rc)) throw rc;
     653                        ComPtr<IMediumFormat> pSrcFormat;
     654                        rc = pMedium->COMGETTER(MediumFormat)(pSrcFormat.asOutParam());
     655                        ULONG uSrcCaps = 0;
     656                        rc = pSrcFormat->COMGETTER(Capabilities)(&uSrcCaps);
     657                        if (FAILED(rc)) throw rc;
     658
     659                        /* Default format? */
     660                        Utf8Str strDefaultFormat;
     661                        p->mParent->getDefaultHardDiskFormat(strDefaultFormat);
     662                        Bstr bstrSrcFormat(strDefaultFormat);
     663                        ULONG srcVar = MediumVariant_Standard;
     664                        /* Is the source file based? */
     665                        if ((uSrcCaps & MediumFormatCapabilities_File) == MediumFormatCapabilities_File)
     666                        {
     667                            /* Yes, just use the source format. Otherwise the defaults
     668                             * will be used. */
     669                            rc = pMedium->COMGETTER(Format)(bstrSrcFormat.asOutParam());
     670                            if (FAILED(rc)) throw rc;
     671                            rc = pMedium->COMGETTER(Variant)(&srcVar);
     672                            if (FAILED(rc)) throw rc;
     673                        }
     674
     675                        Guid newId;
     676                        newId.create();
     677                        Utf8Str strNewName(bstrSrcName);
     678                        if (!fKeepDiskNames)
     679                        {
     680                            /* If the old disk name was in {uuid} format we also
     681                             * want the new name in this format, but with the
     682                             * updated id of course. If the old disk was called
     683                             * like the VM name, we change it to the new VM name.
     684                             * For all other disks we rename them with this
     685                             * template: "new name-disk1.vdi". */
     686                            Utf8Str strSrcTest = Utf8Str(bstrSrcName).stripExt();
     687                            if (strSrcTest == strOldVMName)
     688                                strNewName = Utf8StrFmt("%s%s", trgMCF.machineUserData.strName.c_str(), RTPathExt(Utf8Str(bstrSrcName).c_str()));
     689                            else if (   strSrcTest.startsWith("{")
     690                                     && strSrcTest.endsWith("}"))
     691                            {
     692                                strSrcTest = strSrcTest.substr(1, strSrcTest.length() - 2);
     693                                if (isValidGuid(strSrcTest))
     694                                    strNewName = Utf8StrFmt("%s%s", newId.toStringCurly().c_str(), RTPathExt(strNewName.c_str()));
     695                            }
     696                            else
     697                                strNewName = Utf8StrFmt("%s-disk%d%s", trgMCF.machineUserData.strName.c_str(), ++cDisks, RTPathExt(Utf8Str(bstrSrcName).c_str()));
     698                        }
     699
     700                        /* Check if this medium comes from the snapshot folder, if
     701                         * so, put it there in the cloned machine as well.
     702                         * Otherwise it goes to the machine folder. */
     703                        Bstr bstrSrcPath;
     704                        Utf8Str strFile = Utf8StrFmt("%s%c%s", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, strNewName.c_str());
     705                        rc = pMedium->COMGETTER(Location)(bstrSrcPath.asOutParam());
     706                        if (FAILED(rc)) throw rc;
     707                        if (   !bstrSrcPath.isEmpty()
     708                            &&  RTPathStartsWith(Utf8Str(bstrSrcPath).c_str(), Utf8Str(bstrSrcSnapshotFolder).c_str()))
     709                            strFile = Utf8StrFmt("%s%c%s", strTrgSnapshotFolder.c_str(), RTPATH_DELIMITER, strNewName.c_str());
     710                        else
     711                            strFile = Utf8StrFmt("%s%c%s", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, strNewName.c_str());
     712
     713                        /* Start creating the clone. */
     714                        ComObjPtr<Medium> pTarget;
     715                        rc = pTarget.createObject();
     716                        if (FAILED(rc)) throw rc;
     717
     718                        rc = pTarget->init(p->mParent,
     719                                           Utf8Str(bstrSrcFormat),
     720                                           strFile,
     721                                           Guid::Empty,  /* empty media registry */
     722                                           NULL          /* llRegistriesThatNeedSaving */);
     723                        if (FAILED(rc)) throw rc;
     724
     725                        /* Update the new uuid. */
     726                        pTarget->updateId(newId);
     727
     728                        srcLock.release();
     729                        /* Do the disk cloning. */
     730                        ComPtr<IProgress> progress2;
     731                        rc = pMedium->CloneTo(pTarget,
     732                                              srcVar,
     733                                              pNewParent,
     734                                              progress2.asOutParam());
     735                        if (FAILED(rc)) throw rc;
     736
     737                        /* Wait until the async process has finished. */
     738                        rc = d->pProgress->WaitForAsyncProgressCompletion(progress2);
     739                        srcLock.acquire();
     740                        if (FAILED(rc)) throw rc;
     741
     742                        /* Check the result of the async process. */
     743                        LONG iRc;
     744                        rc = progress2->COMGETTER(ResultCode)(&iRc);
     745                        if (FAILED(rc)) throw rc;
     746                        if (FAILED(iRc))
     747                        {
     748                            /* If the thread of the progress object has an error, then
     749                             * retrieve the error info from there, or it'll be lost. */
     750                            ProgressErrorInfo info(progress2);
     751                            throw p->setError(iRc, Utf8Str(info.getText()).c_str());
     752                        }
     753                        /* Remember created medium. */
     754                        newMedia.append(pTarget);
     755                        /* Get the medium type from the source and set it to the
     756                         * new medium. */
     757                        MediumType_T type;
     758                        rc = pMedium->COMGETTER(Type)(&type);
     759                        if (FAILED(rc)) throw rc;
     760                        rc = pTarget->COMSETTER(Type)(type);
     761                        if (FAILED(rc)) throw rc;
     762                        map.insert(TStrMediumPair(Utf8Str(bstrSrcId), pTarget));
     763                        /* register the new harddisk */
     764                        {
     765                            AutoWriteLock tlock(p->mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     766                            rc = p->mParent->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */);
     767                            if (FAILED(rc)) throw rc;
     768                        }
     769                        /* This medium becomes the parent of the next medium in the
     770                         * chain. */
     771                        pNewParent = pTarget;
    597772                    }
    598 
    599                     Guid newId;
    600                     newId.create();
    601                     Utf8Str strNewName(bstrSrcName);
    602                     if (!fKeepDiskNames)
    603                     {
    604                         /* If the old disk name was in {uuid} format we also
    605                          * want the new name in this format, but with the
    606                          * updated id of course. If the old disk was called
    607                          * like the VM name, we change it to the new VM name.
    608                          * For all other disks we rename them with this
    609                          * template: "new name-disk1.vdi". */
    610                         Utf8Str strSrcTest = Utf8Str(bstrSrcName).stripExt();
    611                         if (strSrcTest == strOldVMName)
    612                             strNewName = Utf8StrFmt("%s%s", trgMCF.machineUserData.strName.c_str(), RTPathExt(Utf8Str(bstrSrcName).c_str()));
    613                         else
    614                         if (strSrcTest.startsWith("{") &&
    615                             strSrcTest.endsWith("}"))
    616                         {
    617                             strSrcTest = strSrcTest.substr(1, strSrcTest.length() - 2);
    618                             if (isValidGuid(strSrcTest))
    619                                 strNewName = Utf8StrFmt("%s%s", newId.toStringCurly().c_str(), RTPathExt(strNewName.c_str()));
    620                         }
    621                         else
    622                             strNewName = Utf8StrFmt("%s-disk%d%s", trgMCF.machineUserData.strName.c_str(), ++cDisks, RTPathExt(Utf8Str(bstrSrcName).c_str()));
    623                     }
    624 
    625                     /* Check if this medium comes from the snapshot folder, if
    626                      * so, put it there in the cloned machine as well.
    627                      * Otherwise it goes to the machine folder. */
    628                     Bstr bstrSrcPath;
    629                     Utf8Str strFile = Utf8StrFmt("%s%c%s", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, strNewName.c_str());
    630                     rc = pMedium->COMGETTER(Location)(bstrSrcPath.asOutParam());
    631                     if (FAILED(rc)) throw rc;
    632                     if (   !bstrSrcPath.isEmpty()
    633                         &&  RTPathStartsWith(Utf8Str(bstrSrcPath).c_str(), Utf8Str(bstrSrcSnapshotFolder).c_str()))
    634                         strFile = Utf8StrFmt("%s%c%s", strTrgSnapshotFolder.c_str(), RTPATH_DELIMITER, strNewName.c_str());
    635                     else
    636                         strFile = Utf8StrFmt("%s%c%s", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, strNewName.c_str());
    637 
    638                     /* Start creating the clone. */
    639                     ComObjPtr<Medium> pTarget;
    640                     rc = pTarget.createObject();
    641                     if (FAILED(rc)) throw rc;
    642 
    643                     rc = pTarget->init(p->mParent,
    644                                        Utf8Str(bstrSrcFormat),
    645                                        strFile,
    646                                        Guid::Empty,  /* empty media registry */
    647                                        NULL          /* llRegistriesThatNeedSaving */);
    648                     if (FAILED(rc)) throw rc;
    649 
    650                     /* Update the new uuid. */
    651                     pTarget->updateId(newId);
    652 
    653                     srcLock.release();
    654                     /* Do the disk cloning. */
    655                     ComPtr<IProgress> progress2;
    656                     rc = pMedium->CloneTo(pTarget,
    657                                           srcVar,
    658                                           pNewParent,
    659                                           progress2.asOutParam());
    660                     if (FAILED(rc)) throw rc;
    661 
    662                     /* Wait until the asynchrony process has finished. */
    663                     rc = d->pProgress->WaitForAsyncProgressCompletion(progress2);
    664                     srcLock.acquire();
    665                     if (FAILED(rc)) throw rc;
    666 
    667                     /* Check the result of the asynchrony process. */
    668                     LONG iRc;
    669                     rc = progress2->COMGETTER(ResultCode)(&iRc);
    670                     if (FAILED(rc)) throw rc;
    671                     if (FAILED(iRc))
    672                     {
    673                         /* If the thread of the progress object has an error, then
    674                          * retrieve the error info from there, or it'll be lost. */
    675                         ProgressErrorInfo info(progress2);
    676                         throw p->setError(iRc, Utf8Str(info.getText()).c_str());
    677                     }
    678                     /* Remember created medias. */
    679                     newMedias.append(pTarget);
    680                     /* Get the medium type from the source and set it to the
    681                      * new medium. */
    682                     MediumType_T type;
    683                     rc = pMedium->COMGETTER(Type)(&type);
    684                     if (FAILED(rc)) throw rc;
    685                     rc = pTarget->COMSETTER(Type)(type);
    686                     if (FAILED(rc)) throw rc;
    687                     map.insert(TStrMediumPair(Utf8Str(bstrSrcId), pTarget));
    688                     /* Global register the new harddisk */
    689                     {
    690                         AutoWriteLock tlock(p->mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    691                         rc = p->mParent->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */);
    692                         if (FAILED(rc)) return rc;
    693                     }
    694                     /* This medium becomes the parent of the next medium in the
    695                      * chain. */
    696                     pNewParent = pTarget;
    697773                }
    698774            }
     
    701777            if (mtc.fCreateDiffs)
    702778            {
    703                 Bstr bstrSrcId;
    704                 rc = pNewParent->COMGETTER(Id)(bstrSrcId.asOutParam());
    705                 if (FAILED(rc)) throw rc;
    706                 ComObjPtr<Medium> diff;
    707                 diff.createObject();
    708                 rc = diff->init(p->mParent,
    709                                 pNewParent->getPreferredDiffFormat(),
    710                                 Utf8StrFmt("%s%c", strTrgSnapshotFolder.c_str(), RTPATH_DELIMITER),
    711                                 Guid::Empty, /* empty media registry */
    712                                 NULL);       /* pllRegistriesThatNeedSaving */
    713                 if (FAILED(rc)) throw rc;
    714                 MediumLockList *pMediumLockList(new MediumLockList());
    715                 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
    716                                                 true /* fMediumLockWrite */,
    717                                                 pNewParent,
    718                                                 *pMediumLockList);
    719                 if (FAILED(rc)) throw rc;
    720                 rc = pMediumLockList->Lock();
    721                 if (FAILED(rc)) throw rc;
    722                 rc = pNewParent->createDiffStorage(diff, MediumVariant_Standard,
    723                                                    pMediumLockList,
    724                                                    NULL /* aProgress */,
    725                                                    true /* aWait */,
    726                                                    NULL); // pllRegistriesThatNeedSaving
    727                 delete pMediumLockList;
    728                 if (FAILED(rc)) throw rc;
    729                 /* Remember created medias. */
    730                 newMedias.append(diff);
    731                 /* Global register the new harddisk */
     779                if (pNewParent->isReadOnly())
    732780                {
    733                     AutoWriteLock tlock(p->mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    734                     rc = p->mParent->registerHardDisk(diff, NULL /* pllRegistriesThatNeedSaving */);
    735                     if (FAILED(rc)) return rc;
     781                    ComObjPtr<Medium> pDiff;
     782                    rc = createDiffHelper(pNewParent, strTrgSnapshotFolder,
     783                                          &newMedia, &pDiff);
     784                    if (FAILED(rc)) throw rc;
     785                    /* diff image has to be used... */
     786                    pNewParent = pDiff;
    736787                }
    737                 /* This medium becomes the parent of the next medium in the
    738                  * chain. */
    739                 pNewParent = diff;
     788                else
     789                {
     790                    /* Attach the medium directly, as its type is not
     791                     * subject to diff creation. */
     792                    newMedia.append(pNewParent);
     793                }
    740794            }
    741795            Bstr bstrSrcId;
     
    752806        /* Make sure all disks know of the new machine uuid. We do this last to
    753807         * be able to change the medium type above. */
    754         for (size_t i = newMedias.size(); i > 0; --i)
    755         {
    756             ComObjPtr<Medium> &pMedium = newMedias.at(i - 1);
     808        for (size_t i = newMedia.size(); i > 0; --i)
     809        {
     810            const ComObjPtr<Medium> &pMedium = newMedia.at(i - 1);
    757811            AutoCaller mac(pMedium);
    758812            if (FAILED(mac.rc())) throw mac.rc();
    759813            AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
    760             pMedium->addRegistry(d->pTrgMachine->mData->mUuid, false /* fRecursive */);
     814            pMedium->addRegistry(d->options.contains(CloneOptions_Link) ? d->pSrcMachine->mData->mUuid : d->pTrgMachine->mData->mUuid, false /* fRecurse */);
    761815        }
    762816        /* Check if a snapshot folder is necessary and if so doesn't already
     
    812866        rc = d->pTrgMachine->SaveSettings();
    813867        if (FAILED(rc)) throw rc;
    814     }
    815     catch(HRESULT rc2)
     868        trgLock.release();
     869        if (d->options.contains(CloneOptions_Link))
     870        {
     871            srcLock.release();
     872            GuidList llRegistrySrc;
     873            llRegistrySrc.push_back(d->pSrcMachine->mData->mUuid);
     874            rc = p->mParent->saveRegistries(llRegistrySrc);
     875            if (FAILED(rc)) throw rc;
     876        }
     877    }
     878    catch (HRESULT rc2)
    816879    {
    817880        rc = rc2;
     
    836899        /* Delete all already created medias. (Reverse, cause there could be
    837900         * parent->child relations.) */
    838         for (size_t i = newMedias.size(); i > 0; --i)
    839         {
    840             bool fFile = false;
    841             Utf8Str strLoc;
    842             ComObjPtr<Medium> &pMedium = newMedias.at(i - 1);
    843             {
    844                 AutoCaller mac(pMedium);
    845                 if (FAILED(mac.rc())) { continue; mrc = mac.rc(); }
    846                 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
    847                 fFile = pMedium->isMediumFormatFile();
    848                 strLoc = pMedium->getLocationFull();
    849             }
    850             if (fFile)
    851             {
    852                 vrc = RTFileDelete(strLoc.c_str());
    853                 if (RT_FAILURE(vrc))
    854                     mrc = p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not delete file '%s' (%Rrc)"), strLoc.c_str(), vrc);
    855             }
     901        for (size_t i = newMedia.size(); i > 0; --i)
     902        {
     903            const ComObjPtr<Medium> &pMedium = newMedia.at(i - 1);
     904            mrc = pMedium->deleteStorage(NULL /* aProgress */,
     905                                         true /* aWait */,
     906                                         NULL /* llRegistriesThatNeedSaving */);
     907            pMedium->Close();
    856908        }
    857909        /* Delete the snapshot folder when not empty. */
     
    865917}
    866918
     919HRESULT MachineCloneVM::createDiffHelper(const ComObjPtr<Medium> &pParent,
     920                                         const Utf8Str &strSnapshotFolder,
     921                                         RTCList< ComObjPtr<Medium> > *pNewMedia,
     922                                         ComObjPtr<Medium> *ppDiff)
     923{
     924    DPTR(MachineCloneVM);
     925    ComObjPtr<Machine> &p = d->p;
     926    HRESULT rc = S_OK;
     927
     928    try
     929    {
     930        Bstr bstrSrcId;
     931        rc = pParent->COMGETTER(Id)(bstrSrcId.asOutParam());
     932        if (FAILED(rc)) throw rc;
     933        ComObjPtr<Medium> diff;
     934        diff.createObject();
     935        rc = diff->init(p->mParent,
     936                        pParent->getPreferredDiffFormat(),
     937                        Utf8StrFmt("%s%c", strSnapshotFolder.c_str(), RTPATH_DELIMITER),
     938                        Guid::Empty, /* empty media registry */
     939                        NULL);       /* pllRegistriesThatNeedSaving */
     940        if (FAILED(rc)) throw rc;
     941        MediumLockList *pMediumLockList(new MediumLockList());
     942        rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
     943                                        true /* fMediumLockWrite */,
     944                                        pParent,
     945                                        *pMediumLockList);
     946        if (FAILED(rc)) throw rc;
     947        rc = pMediumLockList->Lock();
     948        if (FAILED(rc)) throw rc;
     949        /* this already registers the new diff image */
     950        rc = pParent->createDiffStorage(diff, MediumVariant_Standard,
     951                                        pMediumLockList,
     952                                        NULL /* aProgress */,
     953                                        true /* aWait */,
     954                                        NULL); // pllRegistriesThatNeedSaving
     955        delete pMediumLockList;
     956        if (FAILED(rc)) throw rc;
     957        /* Remember created medium. */
     958        pNewMedia->append(diff);
     959        *ppDiff = diff;
     960    }
     961    catch (HRESULT rc2)
     962    {
     963        rc = rc2;
     964    }
     965    catch (...)
     966    {
     967        rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
     968    }
     969
     970    return rc;
     971}
     972
    867973void MachineCloneVM::destroy()
    868974{
Note: See TracChangeset for help on using the changeset viewer.

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