VirtualBox

Changeset 37971 in vbox for trunk


Ignore:
Timestamp:
Jul 14, 2011 3:01:07 PM (14 years ago)
Author:
vboxsync
Message:

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

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/doc/manual/en_US/user_VBoxManage.xml

    r37929 r37971  
    10921092    <title>VBoxManage clonevm</title>
    10931093
    1094     <para>This command creates a full copy of an existing virtual
     1094    <para>This command creates a full or linked copy of an existing virtual
    10951095    machine.</para>
    10961096
     
    11181118       </listitem>
    11191119       <listitem>
    1120            <para><computeroutput>--options keepallmacs|keepnatmacs|keepdisknames</computeroutput>:
    1121            Allows additional fine tuning of the clone operation. The first two
     1120           <para><computeroutput>--options link|keepallmacs|keepnatmacs|keepdisknames</computeroutput>:
     1121           Allows additional fine tuning of the clone operation. The first
     1122           option defines that a linked clone should be created, which is
     1123           only possible for a machine clone from a snapshot. The next two
    11221124           options allow to define how the MAC addresses of every virtual
    11231125           network card should be handled. They can either be reinitialized
    1124            (the default), leaved unchanged
    1125            (<computeroutput>keepallmacs</computeroutput>) or leaved unchanged
     1126           (the default), left unchanged
     1127           (<computeroutput>keepallmacs</computeroutput>) or left unchanged
    11261128           when the network type is NAT
    11271129           (<computeroutput>keepnatmacs</computeroutput>). If you add
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp

    r37929 r37971  
    336336                     "                            [--snapshot <uuid>|<name>]\n"
    337337                     "                            [--mode machine|machineandchilds|all]\n"
    338                      "                            [--options keepallmacs|keepnatmacs|keepdisknames]\n"
     338                     "                            [--options link|keepallmacs|keepnatmacs|\n"
     339                     "                                       keepdisknames]\n"
    339340                     "                            [--name <name>]\n"
    340341                     "                            [--basefolder <basefolder>]\n"
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp

    r37900 r37971  
    311311            else if (!RTStrNICmp(psz, "KeepDiskNames", len))
    312312                options->push_back(CloneOptions_KeepDiskNames);
    313 //            else if (!RTStrNICmp(psz, "Link", len))
    314 //                options->push_back(CloneOptions_Link)
     313            else if (   !RTStrNICmp(psz, "Link", len)
     314                     || !RTStrNICmp(psz, "Linked", len))
     315                options->push_back(CloneOptions_Link);
    315316            else
    316317                rc = VERR_PARSE_ERROR;
  • 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.

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