VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MachineImplCloneVM.cpp@ 96407

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.5 KB
Line 
1/* $Id: MachineImplCloneVM.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * Implementation of MachineCloneVM
4 */
5
6/*
7 * Copyright (C) 2011-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <set>
29#include <map>
30#include "MachineImplCloneVM.h"
31
32#include "VirtualBoxImpl.h"
33#include "MediumImpl.h"
34#include "HostImpl.h"
35
36#include <iprt/path.h>
37#include <iprt/dir.h>
38#include <iprt/cpp/utils.h>
39#ifdef DEBUG_poetzsch
40# include <iprt/stream.h>
41#endif
42
43#include <VBox/com/list.h>
44#include <VBox/com/MultiResult.h>
45
46// typedefs
47/////////////////////////////////////////////////////////////////////////////
48
49typedef struct
50{
51 Utf8Str strBaseName;
52 ComPtr<IMedium> pMedium;
53 uint32_t uIdx;
54 ULONG uWeight;
55} MEDIUMTASK;
56
57typedef struct
58{
59 RTCList<MEDIUMTASK> chain;
60 DeviceType_T devType;
61 bool fCreateDiffs;
62 bool fAttachLinked;
63} MEDIUMTASKCHAIN;
64
65typedef struct
66{
67 Guid snapshotUuid;
68 Utf8Str strFile;
69 ULONG uWeight;
70} FILECOPYTASK;
71
72// The private class
73/////////////////////////////////////////////////////////////////////////////
74
75struct MachineCloneVMPrivate
76{
77 MachineCloneVMPrivate(MachineCloneVM *a_q, ComObjPtr<Machine> &a_pSrcMachine, ComObjPtr<Machine> &a_pTrgMachine,
78 CloneMode_T a_mode, const RTCList<CloneOptions_T> &opts)
79 : q_ptr(a_q)
80 , p(a_pSrcMachine)
81 , pSrcMachine(a_pSrcMachine)
82 , pTrgMachine(a_pTrgMachine)
83 , mode(a_mode)
84 , options(opts)
85 {}
86
87 DECLARE_TRANSLATE_METHODS(MachineCloneVMPrivate)
88
89 /* Thread management */
90 int startWorker()
91 {
92 return RTThreadCreate(NULL,
93 MachineCloneVMPrivate::workerThread,
94 static_cast<void*>(this),
95 0,
96 RTTHREADTYPE_MAIN_WORKER,
97 0,
98 "MachineClone");
99 }
100
101 static DECLCALLBACK(int) workerThread(RTTHREAD /* Thread */, void *pvUser)
102 {
103 MachineCloneVMPrivate *pTask = static_cast<MachineCloneVMPrivate*>(pvUser);
104 AssertReturn(pTask, VERR_INVALID_POINTER);
105
106 HRESULT rc = pTask->q_ptr->run();
107
108 pTask->pProgress->i_notifyComplete(rc);
109
110 pTask->q_ptr->destroy();
111
112 return VINF_SUCCESS;
113 }
114
115 /* Private helper methods */
116
117 /* MachineCloneVM::start helper: */
118 HRESULT createMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const;
119 inline void updateProgressStats(MEDIUMTASKCHAIN &mtc, bool fAttachLinked, ULONG &uCount, ULONG &uTotalWeight) const;
120 inline HRESULT addSaveState(const ComObjPtr<Machine> &machine, bool fAttachCurrent, ULONG &uCount, ULONG &uTotalWeight);
121 inline HRESULT addNVRAM(const ComObjPtr<Machine> &machine, bool fAttachCurrent, ULONG &uCount, ULONG &uTotalWeight);
122 inline HRESULT queryBaseName(const ComPtr<IMedium> &pMedium, Utf8Str &strBaseName) const;
123 HRESULT queryMediasForMachineState(const RTCList<ComObjPtr<Machine> > &machineList,
124 bool fAttachLinked, ULONG &uCount, ULONG &uTotalWeight);
125 HRESULT queryMediasForMachineAndChildStates(const RTCList<ComObjPtr<Machine> > &machineList,
126 bool fAttachLinked, ULONG &uCount, ULONG &uTotalWeight);
127 HRESULT queryMediasForAllStates(const RTCList<ComObjPtr<Machine> > &machineList, bool fAttachLinked, ULONG &uCount,
128 ULONG &uTotalWeight);
129
130 /* MachineCloneVM::run helper: */
131 bool findSnapshot(const settings::SnapshotsList &snl, const Guid &id, settings::Snapshot &sn) const;
132 void updateMACAddresses(settings::NetworkAdaptersList &nwl) const;
133 void updateMACAddresses(settings::SnapshotsList &sl) const;
134 void updateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const;
135 void updateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const;
136 void updateSaveStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const;
137 void updateNVRAMFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const;
138 HRESULT createDifferencingMedium(const ComObjPtr<Machine> &pMachine, const ComObjPtr<Medium> &pParent,
139 const Utf8Str &strSnapshotFolder, RTCList<ComObjPtr<Medium> > &newMedia,
140 ComObjPtr<Medium> *ppDiff) const;
141 static DECLCALLBACK(int) copyFileProgress(unsigned uPercentage, void *pvUser);
142 static void updateSnapshotHardwareUUIDs(settings::SnapshotsList &snapshot_list, const Guid &id);
143
144 /* Private q and parent pointer */
145 MachineCloneVM *q_ptr;
146 ComObjPtr<Machine> p;
147
148 /* Private helper members */
149 ComObjPtr<Machine> pSrcMachine;
150 ComObjPtr<Machine> pTrgMachine;
151 ComPtr<IMachine> pOldMachineState;
152 ComObjPtr<Progress> pProgress;
153 Guid snapshotId;
154 CloneMode_T mode;
155 RTCList<CloneOptions_T> options;
156 RTCList<MEDIUMTASKCHAIN> llMedias;
157 RTCList<FILECOPYTASK> llSaveStateFiles; /* Snapshot UUID -> File path */
158 RTCList<FILECOPYTASK> llNVRAMFiles; /* Snapshot UUID -> File path */
159};
160
161HRESULT MachineCloneVMPrivate::createMachineList(const ComPtr<ISnapshot> &pSnapshot,
162 RTCList< ComObjPtr<Machine> > &machineList) const
163{
164 HRESULT rc = S_OK;
165 Bstr name;
166 rc = pSnapshot->COMGETTER(Name)(name.asOutParam());
167 if (FAILED(rc)) return rc;
168
169 ComPtr<IMachine> pMachine;
170 rc = pSnapshot->COMGETTER(Machine)(pMachine.asOutParam());
171 if (FAILED(rc)) return rc;
172 machineList.append((Machine*)(IMachine*)pMachine);
173
174 SafeIfaceArray<ISnapshot> sfaChilds;
175 rc = pSnapshot->COMGETTER(Children)(ComSafeArrayAsOutParam(sfaChilds));
176 if (FAILED(rc)) return rc;
177 for (size_t i = 0; i < sfaChilds.size(); ++i)
178 {
179 rc = createMachineList(sfaChilds[i], machineList);
180 if (FAILED(rc)) return rc;
181 }
182
183 return rc;
184}
185
186void MachineCloneVMPrivate::updateProgressStats(MEDIUMTASKCHAIN &mtc, bool fAttachLinked,
187 ULONG &uCount, ULONG &uTotalWeight) const
188{
189 if (fAttachLinked)
190 {
191 /* Implicit diff creation as part of attach is a pretty cheap
192 * operation, and does only need one operation per attachment. */
193 ++uCount;
194 uTotalWeight += 1; /* 1MB per attachment */
195 }
196 else
197 {
198 /* Currently the copying of diff images involves reading at least
199 * the biggest parent in the previous chain. So even if the new
200 * diff image is small in size, it could need some time to create
201 * it. Adding the biggest size in the chain should balance this a
202 * little bit more, i.e. the weight is the sum of the data which
203 * needs to be read and written. */
204 ULONG uMaxWeight = 0;
205 for (size_t e = mtc.chain.size(); e > 0; --e)
206 {
207 MEDIUMTASK &mt = mtc.chain.at(e - 1);
208 mt.uWeight += uMaxWeight;
209
210 /* Calculate progress data */
211 ++uCount;
212 uTotalWeight += mt.uWeight;
213
214 /* Save the max size for better weighting of diff image
215 * creation. */
216 uMaxWeight = RT_MAX(uMaxWeight, mt.uWeight);
217 }
218 }
219}
220
221HRESULT MachineCloneVMPrivate::addSaveState(const ComObjPtr<Machine> &machine, bool fAttachCurrent, ULONG &uCount, ULONG &uTotalWeight)
222{
223 Bstr bstrSrcSaveStatePath;
224 HRESULT rc = machine->COMGETTER(StateFilePath)(bstrSrcSaveStatePath.asOutParam());
225 if (FAILED(rc)) return rc;
226 if (!bstrSrcSaveStatePath.isEmpty())
227 {
228 FILECOPYTASK fct;
229 if (fAttachCurrent)
230 {
231 /* Make this saved state part of "current state" of the target
232 * machine, whether it is part of a snapshot or not. */
233 fct.snapshotUuid.clear();
234 }
235 else
236 fct.snapshotUuid = machine->i_getSnapshotId();
237 fct.strFile = bstrSrcSaveStatePath;
238 uint64_t cbSize;
239 int vrc = RTFileQuerySizeByPath(fct.strFile.c_str(), &cbSize);
240 if (RT_FAILURE(vrc))
241 return p->setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not query file size of '%s' (%Rrc)"),
242 fct.strFile.c_str(), vrc);
243 /* same rule as above: count both the data which needs to
244 * be read and written */
245 fct.uWeight = (ULONG)(2 * (cbSize + _1M - 1) / _1M);
246 llSaveStateFiles.append(fct);
247 ++uCount;
248 uTotalWeight += fct.uWeight;
249 }
250 return S_OK;
251}
252
253HRESULT MachineCloneVMPrivate::addNVRAM(const ComObjPtr<Machine> &machine, bool fAttachCurrent, ULONG &uCount, ULONG &uTotalWeight)
254{
255 Bstr bstrSrcNVRAMPath;
256 ComPtr<INvramStore> pNvramStore;
257 HRESULT rc = machine->COMGETTER(NonVolatileStore)(pNvramStore.asOutParam());
258 if (FAILED(rc)) return rc;
259 rc = pNvramStore->COMGETTER(NonVolatileStorageFile)(bstrSrcNVRAMPath.asOutParam());
260 if (FAILED(rc)) return rc;
261 if (!bstrSrcNVRAMPath.isEmpty())
262 {
263 FILECOPYTASK fct;
264 if (fAttachCurrent)
265 {
266 /* Make this saved state part of "current state" of the target
267 * machine, whether it is part of a snapshot or not. */
268 fct.snapshotUuid.clear();
269 }
270 else
271 fct.snapshotUuid = machine->i_getSnapshotId();
272 fct.strFile = bstrSrcNVRAMPath;
273 if (!RTFileExists(fct.strFile.c_str()))
274 return S_OK;
275 uint64_t cbSize;
276 int vrc = RTFileQuerySizeByPath(fct.strFile.c_str(), &cbSize);
277 if (RT_FAILURE(vrc))
278 return p->setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not query file size of '%s' (%Rrc)"),
279 fct.strFile.c_str(), vrc);
280 /* same rule as above: count both the data which needs to
281 * be read and written */
282 fct.uWeight = (ULONG)(2 * (cbSize + _1M - 1) / _1M);
283 llNVRAMFiles.append(fct);
284 ++uCount;
285 uTotalWeight += fct.uWeight;
286 }
287 return S_OK;
288}
289
290HRESULT MachineCloneVMPrivate::queryBaseName(const ComPtr<IMedium> &pMedium, Utf8Str &strBaseName) const
291{
292 ComPtr<IMedium> pBaseMedium;
293 HRESULT rc = pMedium->COMGETTER(Base)(pBaseMedium.asOutParam());
294 if (FAILED(rc)) return rc;
295 Bstr bstrBaseName;
296 rc = pBaseMedium->COMGETTER(Name)(bstrBaseName.asOutParam());
297 if (FAILED(rc)) return rc;
298 strBaseName = bstrBaseName;
299 return rc;
300}
301
302HRESULT MachineCloneVMPrivate::queryMediasForMachineState(const RTCList<ComObjPtr<Machine> > &machineList,
303 bool fAttachLinked, ULONG &uCount, ULONG &uTotalWeight)
304{
305 /* This mode is pretty straightforward. We didn't need to know about any
306 * parent/children relationship and therefore simply adding all directly
307 * attached images of the source VM as cloning targets. The IMedium code
308 * take than care to merge any (possibly) existing parents into the new
309 * image. */
310 HRESULT rc = S_OK;
311 for (size_t i = 0; i < machineList.size(); ++i)
312 {
313 const ComObjPtr<Machine> &machine = machineList.at(i);
314 /* If this is the Snapshot Machine we want to clone, we need to
315 * create a new diff file for the new "current state". */
316 const bool fCreateDiffs = (machine == pOldMachineState);
317 /* Add all attachments of the different machines to a worker list. */
318 SafeIfaceArray<IMediumAttachment> sfaAttachments;
319 rc = machine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
320 if (FAILED(rc)) return rc;
321 for (size_t a = 0; a < sfaAttachments.size(); ++a)
322 {
323 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a];
324 DeviceType_T type;
325 rc = pAtt->COMGETTER(Type)(&type);
326 if (FAILED(rc)) return rc;
327
328 /* Only harddisks and floppies are of interest. */
329 if ( type != DeviceType_HardDisk
330 && type != DeviceType_Floppy)
331 continue;
332
333 /* Valid medium attached? */
334 ComPtr<IMedium> pSrcMedium;
335 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam());
336 if (FAILED(rc)) return rc;
337
338 if (pSrcMedium.isNull())
339 continue;
340
341 /* Create the medium task chain. In this case it will always
342 * contain one image only. */
343 MEDIUMTASKCHAIN mtc;
344 mtc.devType = type;
345 mtc.fCreateDiffs = fCreateDiffs;
346 mtc.fAttachLinked = fAttachLinked;
347
348 /* Refresh the state so that the file size get read. */
349 MediumState_T e;
350 rc = pSrcMedium->RefreshState(&e);
351 if (FAILED(rc)) return rc;
352 LONG64 lSize;
353 rc = pSrcMedium->COMGETTER(Size)(&lSize);
354 if (FAILED(rc)) return rc;
355
356 MEDIUMTASK mt;
357 mt.uIdx = UINT32_MAX; /* No read/write optimization possible. */
358
359 /* Save the base name. */
360 rc = queryBaseName(pSrcMedium, mt.strBaseName);
361 if (FAILED(rc)) return rc;
362
363 /* Save the current medium, for later cloning. */
364 mt.pMedium = pSrcMedium;
365 if (fAttachLinked)
366 mt.uWeight = 0; /* dummy */
367 else
368 mt.uWeight = (ULONG)((lSize + _1M - 1) / _1M);
369 mtc.chain.append(mt);
370
371 /* Update the progress info. */
372 updateProgressStats(mtc, fAttachLinked, uCount, uTotalWeight);
373 /* Append the list of images which have to be cloned. */
374 llMedias.append(mtc);
375 }
376 /* Add the save state file of this machine if there is one. */
377 rc = addSaveState(machine, true /*fAttachCurrent*/, uCount, uTotalWeight);
378 if (FAILED(rc)) return rc;
379 /* Add the NVRAM file of this machine if there is one. */
380 rc = addNVRAM(machine, true /*fAttachCurrent*/, uCount, uTotalWeight);
381 if (FAILED(rc)) return rc;
382 }
383
384 return rc;
385}
386
387HRESULT MachineCloneVMPrivate::queryMediasForMachineAndChildStates(const RTCList<ComObjPtr<Machine> > &machineList,
388 bool fAttachLinked, ULONG &uCount, ULONG &uTotalWeight)
389{
390 /* This is basically a three step approach. First select all medias
391 * directly or indirectly involved in the clone. Second create a histogram
392 * of the usage of all that medias. Third select the medias which are
393 * directly attached or have more than one directly/indirectly used child
394 * in the new clone. Step one and two are done in the first loop.
395 *
396 * Example of the histogram counts after going through 3 attachments from
397 * bottom to top:
398 *
399 * 3
400 * |
401 * -> 3
402 * / \
403 * 2 1 <-
404 * /
405 * -> 2
406 * / \
407 * -> 1 1
408 * \
409 * 1 <-
410 *
411 * Whenever the histogram count is changing compared to the previous one we
412 * need to include that image in the cloning step (Marked with <-). If we
413 * start at zero even the directly attached images are automatically
414 * included.
415 *
416 * Note: This still leads to media chains which can have the same medium
417 * included. This case is handled in "run" and therefore not critical, but
418 * it leads to wrong progress infos which isn't nice. */
419
420 Assert(!fAttachLinked);
421 HRESULT rc = S_OK;
422 std::map<ComPtr<IMedium>, uint32_t> mediaHist; /* Our usage histogram for the medias */
423 for (size_t i = 0; i < machineList.size(); ++i)
424 {
425 const ComObjPtr<Machine> &machine = machineList.at(i);
426 /* If this is the Snapshot Machine we want to clone, we need to
427 * create a new diff file for the new "current state". */
428 const bool fCreateDiffs = (machine == pOldMachineState);
429 /* Add all attachments (and their parents) of the different
430 * machines to a worker list. */
431 SafeIfaceArray<IMediumAttachment> sfaAttachments;
432 rc = machine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
433 if (FAILED(rc)) return rc;
434 for (size_t a = 0; a < sfaAttachments.size(); ++a)
435 {
436 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a];
437 DeviceType_T type;
438 rc = pAtt->COMGETTER(Type)(&type);
439 if (FAILED(rc)) return rc;
440
441 /* Only harddisks and floppies are of interest. */
442 if ( type != DeviceType_HardDisk
443 && type != DeviceType_Floppy)
444 continue;
445
446 /* Valid medium attached? */
447 ComPtr<IMedium> pSrcMedium;
448 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam());
449 if (FAILED(rc)) return rc;
450
451 if (pSrcMedium.isNull())
452 continue;
453
454 MEDIUMTASKCHAIN mtc;
455 mtc.devType = type;
456 mtc.fCreateDiffs = fCreateDiffs;
457 mtc.fAttachLinked = fAttachLinked;
458
459 while (!pSrcMedium.isNull())
460 {
461 /* Build a histogram of used medias and the parent chain. */
462 ++mediaHist[pSrcMedium];
463
464 /* Refresh the state so that the file size get read. */
465 MediumState_T e;
466 rc = pSrcMedium->RefreshState(&e);
467 if (FAILED(rc)) return rc;
468 LONG64 lSize;
469 rc = pSrcMedium->COMGETTER(Size)(&lSize);
470 if (FAILED(rc)) return rc;
471
472 MEDIUMTASK mt;
473 mt.uIdx = UINT32_MAX;
474 mt.pMedium = pSrcMedium;
475 mt.uWeight = (ULONG)((lSize + _1M - 1) / _1M);
476 mtc.chain.append(mt);
477
478 /* Query next parent. */
479 rc = pSrcMedium->COMGETTER(Parent)(pSrcMedium.asOutParam());
480 if (FAILED(rc)) return rc;
481 }
482
483 llMedias.append(mtc);
484 }
485 /* Add the save state file of this machine if there is one. */
486 rc = addSaveState(machine, false /*fAttachCurrent*/, uCount, uTotalWeight);
487 if (FAILED(rc)) return rc;
488 /* Add the NVRAM file of this machine if there is one. */
489 rc = addNVRAM(machine, false /*fAttachCurrent*/, uCount, uTotalWeight);
490 if (FAILED(rc)) return rc;
491 /* If this is the newly created current state, make sure that the
492 * saved state and NVRAM is also attached to it. */
493 if (fCreateDiffs)
494 {
495 rc = addSaveState(machine, true /*fAttachCurrent*/, uCount, uTotalWeight);
496 if (FAILED(rc)) return rc;
497 rc = addNVRAM(machine, true /*fAttachCurrent*/, uCount, uTotalWeight);
498 if (FAILED(rc)) return rc;
499 }
500 }
501 /* Build up the index list of the image chain. Unfortunately we can't do
502 * that in the previous loop, cause there we go from child -> parent and
503 * didn't know how many are between. */
504 for (size_t i = 0; i < llMedias.size(); ++i)
505 {
506 uint32_t uIdx = 0;
507 MEDIUMTASKCHAIN &mtc = llMedias.at(i);
508 for (size_t a = mtc.chain.size(); a > 0; --a)
509 mtc.chain[a - 1].uIdx = uIdx++;
510 }
511#ifdef DEBUG_poetzsch
512 /* Print the histogram */
513 std::map<ComPtr<IMedium>, uint32_t>::iterator it;
514 for (it = mediaHist.begin(); it != mediaHist.end(); ++it)
515 {
516 Bstr bstrSrcName;
517 rc = (*it).first->COMGETTER(Name)(bstrSrcName.asOutParam());
518 if (FAILED(rc)) return rc;
519 RTPrintf("%ls: %d\n", bstrSrcName.raw(), (*it).second);
520 }
521#endif
522 /* Go over every medium in the list and check if it either a directly
523 * attached disk or has more than one children. If so it needs to be
524 * replicated. Also we have to make sure that any direct or indirect
525 * children knows of the new parent (which doesn't necessarily mean it
526 * is a direct children in the source chain). */
527 for (size_t i = 0; i < llMedias.size(); ++i)
528 {
529 MEDIUMTASKCHAIN &mtc = llMedias.at(i);
530 RTCList<MEDIUMTASK> newChain;
531 uint32_t used = 0;
532 for (size_t a = 0; a < mtc.chain.size(); ++a)
533 {
534 const MEDIUMTASK &mt = mtc.chain.at(a);
535 uint32_t hist = mediaHist[mt.pMedium];
536#ifdef DEBUG_poetzsch
537 Bstr bstrSrcName;
538 rc = mt.pMedium->COMGETTER(Name)(bstrSrcName.asOutParam());
539 if (FAILED(rc)) return rc;
540 RTPrintf("%ls: %d (%d)\n", bstrSrcName.raw(), hist, used);
541#endif
542 /* Check if there is a "step" in the histogram when going the chain
543 * upwards. If so, we need this image, cause there is another branch
544 * from here in the cloned VM. */
545 if (hist > used)
546 {
547 newChain.append(mt);
548 used = hist;
549 }
550 }
551 /* Make sure we always using the old base name as new base name, even
552 * if the base is a differencing image in the source VM (with the UUID
553 * as name). */
554 rc = queryBaseName(newChain.last().pMedium, newChain.last().strBaseName);
555 if (FAILED(rc)) return rc;
556 /* Update the old medium chain with the updated one. */
557 mtc.chain = newChain;
558 /* Update the progress info. */
559 updateProgressStats(mtc, fAttachLinked, uCount, uTotalWeight);
560 }
561
562 return rc;
563}
564
565HRESULT MachineCloneVMPrivate::queryMediasForAllStates(const RTCList<ComObjPtr<Machine> > &machineList,
566 bool fAttachLinked, ULONG &uCount, ULONG &uTotalWeight)
567{
568 /* In this case we create a exact copy of the original VM. This means just
569 * adding all directly and indirectly attached disk images to the worker
570 * list. */
571 Assert(!fAttachLinked);
572 HRESULT rc = S_OK;
573 for (size_t i = 0; i < machineList.size(); ++i)
574 {
575 const ComObjPtr<Machine> &machine = machineList.at(i);
576 /* If this is the Snapshot Machine we want to clone, we need to
577 * create a new diff file for the new "current state". */
578 const bool fCreateDiffs = (machine == pOldMachineState);
579 /* Add all attachments (and their parents) of the different
580 * machines to a worker list. */
581 SafeIfaceArray<IMediumAttachment> sfaAttachments;
582 rc = machine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
583 if (FAILED(rc)) return rc;
584 for (size_t a = 0; a < sfaAttachments.size(); ++a)
585 {
586 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a];
587 DeviceType_T type;
588 rc = pAtt->COMGETTER(Type)(&type);
589 if (FAILED(rc)) return rc;
590
591 /* Only harddisks and floppies are of interest. */
592 if ( type != DeviceType_HardDisk
593 && type != DeviceType_Floppy)
594 continue;
595
596 /* Valid medium attached? */
597 ComPtr<IMedium> pSrcMedium;
598 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam());
599 if (FAILED(rc)) return rc;
600
601 if (pSrcMedium.isNull())
602 continue;
603
604 /* Build up a child->parent list of this attachment. (Note: we are
605 * not interested of any child's not attached to this VM. So this
606 * will not create a full copy of the base/child relationship.) */
607 MEDIUMTASKCHAIN mtc;
608 mtc.devType = type;
609 mtc.fCreateDiffs = fCreateDiffs;
610 mtc.fAttachLinked = fAttachLinked;
611
612 while (!pSrcMedium.isNull())
613 {
614 /* Refresh the state so that the file size get read. */
615 MediumState_T e;
616 rc = pSrcMedium->RefreshState(&e);
617 if (FAILED(rc)) return rc;
618 LONG64 lSize;
619 rc = pSrcMedium->COMGETTER(Size)(&lSize);
620 if (FAILED(rc)) return rc;
621
622 /* Save the current medium, for later cloning. */
623 MEDIUMTASK mt;
624 mt.uIdx = UINT32_MAX;
625 mt.pMedium = pSrcMedium;
626 mt.uWeight = (ULONG)((lSize + _1M - 1) / _1M);
627 mtc.chain.append(mt);
628
629 /* Query next parent. */
630 rc = pSrcMedium->COMGETTER(Parent)(pSrcMedium.asOutParam());
631 if (FAILED(rc)) return rc;
632 }
633 /* Update the progress info. */
634 updateProgressStats(mtc, fAttachLinked, uCount, uTotalWeight);
635 /* Append the list of images which have to be cloned. */
636 llMedias.append(mtc);
637 }
638 /* Add the save state file of this machine if there is one. */
639 rc = addSaveState(machine, false /*fAttachCurrent*/, uCount, uTotalWeight);
640 if (FAILED(rc)) return rc;
641 /* Add the NVRAM file of this machine if there is one. */
642 rc = addNVRAM(machine, false /*fAttachCurrent*/, uCount, uTotalWeight);
643 if (FAILED(rc)) return rc;
644 /* If this is the newly created current state, make sure that the
645 * saved state is also attached to it. */
646 if (fCreateDiffs)
647 {
648 rc = addSaveState(machine, true /*fAttachCurrent*/, uCount, uTotalWeight);
649 if (FAILED(rc)) return rc;
650 rc = addNVRAM(machine, true /*fAttachCurrent*/, uCount, uTotalWeight);
651 if (FAILED(rc)) return rc;
652 }
653 }
654 /* Build up the index list of the image chain. Unfortunately we can't do
655 * that in the previous loop, cause there we go from child -> parent and
656 * didn't know how many are between. */
657 for (size_t i = 0; i < llMedias.size(); ++i)
658 {
659 uint32_t uIdx = 0;
660 MEDIUMTASKCHAIN &mtc = llMedias.at(i);
661 for (size_t a = mtc.chain.size(); a > 0; --a)
662 mtc.chain[a - 1].uIdx = uIdx++;
663 }
664
665 return rc;
666}
667
668bool MachineCloneVMPrivate::findSnapshot(const settings::SnapshotsList &snl, const Guid &id, settings::Snapshot &sn) const
669{
670 settings::SnapshotsList::const_iterator it;
671 for (it = snl.begin(); it != snl.end(); ++it)
672 {
673 if (it->uuid == id)
674 {
675 sn = (*it);
676 return true;
677 }
678 else if (!it->llChildSnapshots.empty())
679 {
680 if (findSnapshot(it->llChildSnapshots, id, sn))
681 return true;
682 }
683 }
684 return false;
685}
686
687void MachineCloneVMPrivate::updateMACAddresses(settings::NetworkAdaptersList &nwl) const
688{
689 const bool fNotNAT = options.contains(CloneOptions_KeepNATMACs);
690 settings::NetworkAdaptersList::iterator it;
691 for (it = nwl.begin(); it != nwl.end(); ++it)
692 {
693 if ( fNotNAT
694 && it->mode == NetworkAttachmentType_NAT)
695 continue;
696 Host::i_generateMACAddress(it->strMACAddress);
697 }
698}
699
700void MachineCloneVMPrivate::updateMACAddresses(settings::SnapshotsList &sl) const
701{
702 settings::SnapshotsList::iterator it;
703 for (it = sl.begin(); it != sl.end(); ++it)
704 {
705 updateMACAddresses(it->hardware.llNetworkAdapters);
706 if (!it->llChildSnapshots.empty())
707 updateMACAddresses(it->llChildSnapshots);
708 }
709}
710
711void MachineCloneVMPrivate::updateStorageLists(settings::StorageControllersList &sc,
712 const Bstr &bstrOldId, const Bstr &bstrNewId) const
713{
714 settings::StorageControllersList::iterator it3;
715 for (it3 = sc.begin();
716 it3 != sc.end();
717 ++it3)
718 {
719 settings::AttachedDevicesList &llAttachments = it3->llAttachedDevices;
720 settings::AttachedDevicesList::iterator it4;
721 for (it4 = llAttachments.begin();
722 it4 != llAttachments.end();
723 ++it4)
724 {
725 if ( ( it4->deviceType == DeviceType_HardDisk
726 || it4->deviceType == DeviceType_Floppy)
727 && it4->uuid == bstrOldId)
728 {
729 it4->uuid = bstrNewId;
730 }
731 }
732 }
733}
734
735void MachineCloneVMPrivate::updateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId,
736 const Bstr &bstrNewId) const
737{
738 settings::SnapshotsList::iterator it;
739 for ( it = sl.begin();
740 it != sl.end();
741 ++it)
742 {
743 updateStorageLists(it->hardware.storage.llStorageControllers, bstrOldId, bstrNewId);
744 if (!it->llChildSnapshots.empty())
745 updateSnapshotStorageLists(it->llChildSnapshots, bstrOldId, bstrNewId);
746 }
747}
748
749void MachineCloneVMPrivate::updateSaveStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const
750{
751 settings::SnapshotsList::iterator it;
752 for (it = snl.begin(); it != snl.end(); ++it)
753 {
754 if (it->uuid == id)
755 it->strStateFile = strFile;
756 else if (!it->llChildSnapshots.empty())
757 updateSaveStateFile(it->llChildSnapshots, id, strFile);
758 }
759}
760
761void MachineCloneVMPrivate::updateNVRAMFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const
762{
763 settings::SnapshotsList::iterator it;
764 for (it = snl.begin(); it != snl.end(); ++it)
765 {
766 if (it->uuid == id)
767 it->hardware.nvramSettings.strNvramPath = strFile;
768 else if (!it->llChildSnapshots.empty())
769 updateNVRAMFile(it->llChildSnapshots, id, strFile);
770 }
771}
772
773HRESULT MachineCloneVMPrivate::createDifferencingMedium(const ComObjPtr<Machine> &pMachine, const ComObjPtr<Medium> &pParent,
774 const Utf8Str &strSnapshotFolder, RTCList<ComObjPtr<Medium> > &newMedia,
775 ComObjPtr<Medium> *ppDiff) const
776{
777 HRESULT rc = S_OK;
778 try
779 {
780 // check validity of parent object
781 {
782 AutoReadLock alock(pParent COMMA_LOCKVAL_SRC_POS);
783 Bstr bstrSrcId;
784 rc = pParent->COMGETTER(Id)(bstrSrcId.asOutParam());
785 if (FAILED(rc)) throw rc;
786 }
787 ComObjPtr<Medium> diff;
788 diff.createObject();
789 rc = diff->init(p->i_getVirtualBox(),
790 pParent->i_getPreferredDiffFormat(),
791 Utf8StrFmt("%s%c", strSnapshotFolder.c_str(), RTPATH_DELIMITER),
792 Guid::Empty /* empty media registry */,
793 DeviceType_HardDisk);
794 if (FAILED(rc)) throw rc;
795
796 MediumLockList *pMediumLockList(new MediumLockList());
797 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
798 diff /* pToLockWrite */,
799 false /* fMediumLockWriteAll */,
800 pParent,
801 *pMediumLockList);
802 if (FAILED(rc)) throw rc;
803 rc = pMediumLockList->Lock();
804 if (FAILED(rc)) throw rc;
805
806 /* this already registers the new diff image */
807 rc = pParent->i_createDiffStorage(diff,
808 pParent->i_getPreferredDiffVariant(),
809 pMediumLockList,
810 NULL /* aProgress */,
811 true /* aWait */,
812 false /* aNotify */);
813 delete pMediumLockList;
814 if (FAILED(rc)) throw rc;
815 /* Remember created medium. */
816 newMedia.append(diff);
817 *ppDiff = diff;
818 }
819 catch (HRESULT rc2)
820 {
821 rc = rc2;
822 }
823 catch (...)
824 {
825 rc = VirtualBoxBase::handleUnexpectedExceptions(pMachine, RT_SRC_POS);
826 }
827
828 return rc;
829}
830
831/* static */
832DECLCALLBACK(int) MachineCloneVMPrivate::copyFileProgress(unsigned uPercentage, void *pvUser)
833{
834 ComObjPtr<Progress> pProgress = *static_cast< ComObjPtr<Progress>* >(pvUser);
835
836 BOOL fCanceled = false;
837 HRESULT rc = pProgress->COMGETTER(Canceled)(&fCanceled);
838 if (FAILED(rc)) return VERR_GENERAL_FAILURE;
839 /* If canceled by the user tell it to the copy operation. */
840 if (fCanceled) return VERR_CANCELLED;
841 /* Set the new process. */
842 rc = pProgress->SetCurrentOperationProgress(uPercentage);
843 if (FAILED(rc)) return VERR_GENERAL_FAILURE;
844
845 return VINF_SUCCESS;
846}
847
848void MachineCloneVMPrivate::updateSnapshotHardwareUUIDs(settings::SnapshotsList &snapshot_list, const Guid &id)
849{
850 for (settings::SnapshotsList::iterator snapshot_it = snapshot_list.begin();
851 snapshot_it != snapshot_list.end();
852 ++snapshot_it)
853 {
854 if (!snapshot_it->hardware.uuid.isValid() || snapshot_it->hardware.uuid.isZero())
855 snapshot_it->hardware.uuid = id;
856 updateSnapshotHardwareUUIDs(snapshot_it->llChildSnapshots, id);
857 }
858}
859
860// The public class
861/////////////////////////////////////////////////////////////////////////////
862
863MachineCloneVM::MachineCloneVM(ComObjPtr<Machine> pSrcMachine, ComObjPtr<Machine> pTrgMachine, CloneMode_T mode,
864 const RTCList<CloneOptions_T> &opts) :
865 d_ptr(new MachineCloneVMPrivate(this, pSrcMachine, pTrgMachine, mode, opts))
866{
867}
868
869MachineCloneVM::~MachineCloneVM()
870{
871 delete d_ptr;
872}
873
874HRESULT MachineCloneVM::start(IProgress **pProgress)
875{
876 DPTR(MachineCloneVM);
877 ComObjPtr<Machine> &p = d->p;
878
879 HRESULT rc;
880 try
881 {
882 /** @todo r=klaus this code cannot deal with someone crazy specifying
883 * IMachine corresponding to a mutable machine as d->pSrcMachine */
884 if (d->pSrcMachine->i_isSessionMachine())
885 throw p->setError(E_INVALIDARG, tr("The source machine is mutable"));
886
887 /* Handle the special case that someone is requesting a _full_ clone
888 * with all snapshots (and the current state), but uses a snapshot
889 * machine (and not the current one) as source machine. In this case we
890 * just replace the source (snapshot) machine with the current machine. */
891 if ( d->mode == CloneMode_AllStates
892 && d->pSrcMachine->i_isSnapshotMachine())
893 {
894 Bstr bstrSrcMachineId;
895 rc = d->pSrcMachine->COMGETTER(Id)(bstrSrcMachineId.asOutParam());
896 if (FAILED(rc)) throw rc;
897 ComPtr<IMachine> newSrcMachine;
898 rc = d->pSrcMachine->i_getVirtualBox()->FindMachine(bstrSrcMachineId.raw(), newSrcMachine.asOutParam());
899 if (FAILED(rc)) throw rc;
900 d->pSrcMachine = (Machine*)(IMachine*)newSrcMachine;
901 }
902 bool fSubtreeIncludesCurrent = false;
903 ComObjPtr<Machine> pCurrState;
904 if (d->mode == CloneMode_MachineAndChildStates)
905 {
906 if (d->pSrcMachine->i_isSnapshotMachine())
907 {
908 /* find machine object for current snapshot of current state */
909 Bstr bstrSrcMachineId;
910 rc = d->pSrcMachine->COMGETTER(Id)(bstrSrcMachineId.asOutParam());
911 if (FAILED(rc)) throw rc;
912 ComPtr<IMachine> pCurr;
913 rc = d->pSrcMachine->i_getVirtualBox()->FindMachine(bstrSrcMachineId.raw(), pCurr.asOutParam());
914 if (FAILED(rc)) throw rc;
915 if (pCurr.isNull())
916 throw p->setError(VBOX_E_OBJECT_NOT_FOUND);
917 pCurrState = (Machine *)(IMachine *)pCurr;
918 ComPtr<ISnapshot> pSnapshot;
919 rc = pCurrState->COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam());
920 if (FAILED(rc)) throw rc;
921 if (pSnapshot.isNull())
922 throw p->setError(VBOX_E_OBJECT_NOT_FOUND);
923 ComPtr<IMachine> pCurrSnapMachine;
924 rc = pSnapshot->COMGETTER(Machine)(pCurrSnapMachine.asOutParam());
925 if (FAILED(rc)) throw rc;
926 if (pCurrSnapMachine.isNull())
927 throw p->setError(VBOX_E_OBJECT_NOT_FOUND);
928
929 /* now check if there is a parent chain which leads to the
930 * snapshot machine defining the subtree. */
931 while (!pSnapshot.isNull())
932 {
933 ComPtr<IMachine> pSnapMachine;
934 rc = pSnapshot->COMGETTER(Machine)(pSnapMachine.asOutParam());
935 if (FAILED(rc)) throw rc;
936 if (pSnapMachine.isNull())
937 throw p->setError(VBOX_E_OBJECT_NOT_FOUND);
938 if (pSnapMachine == d->pSrcMachine)
939 {
940 fSubtreeIncludesCurrent = true;
941 break;
942 }
943 rc = pSnapshot->COMGETTER(Parent)(pSnapshot.asOutParam());
944 if (FAILED(rc)) throw rc;
945 }
946 }
947 else
948 {
949 /* If the subtree is only the Current State simply use the
950 * 'machine' case for cloning. It is easier to understand. */
951 d->mode = CloneMode_MachineState;
952 }
953 }
954
955 /* Lock the target machine early (so nobody mess around with it in the meantime). */
956 AutoWriteLock trgLock(d->pTrgMachine COMMA_LOCKVAL_SRC_POS);
957
958 if (d->pSrcMachine->i_isSnapshotMachine())
959 d->snapshotId = d->pSrcMachine->i_getSnapshotId();
960
961 /* Add the current machine and all snapshot machines below this machine
962 * in a list for further processing. */
963 RTCList< ComObjPtr<Machine> > machineList;
964
965 /* Include current state? */
966 if ( d->mode == CloneMode_MachineState
967 || d->mode == CloneMode_AllStates)
968 machineList.append(d->pSrcMachine);
969 /* Should be done a depth copy with all child snapshots? */
970 if ( d->mode == CloneMode_MachineAndChildStates
971 || d->mode == CloneMode_AllStates)
972 {
973 ULONG cSnapshots = 0;
974 rc = d->pSrcMachine->COMGETTER(SnapshotCount)(&cSnapshots);
975 if (FAILED(rc)) throw rc;
976 if (cSnapshots > 0)
977 {
978 Utf8Str id;
979 if (d->mode == CloneMode_MachineAndChildStates)
980 id = d->snapshotId.toString();
981 ComPtr<ISnapshot> pSnapshot;
982 rc = d->pSrcMachine->FindSnapshot(Bstr(id).raw(), pSnapshot.asOutParam());
983 if (FAILED(rc)) throw rc;
984 rc = d->createMachineList(pSnapshot, machineList);
985 if (FAILED(rc)) throw rc;
986 if (d->mode == CloneMode_MachineAndChildStates)
987 {
988 if (fSubtreeIncludesCurrent)
989 {
990 if (pCurrState.isNull())
991 throw p->setError(VBOX_E_OBJECT_NOT_FOUND);
992 machineList.append(pCurrState);
993 }
994 else
995 {
996 rc = pSnapshot->COMGETTER(Machine)(d->pOldMachineState.asOutParam());
997 if (FAILED(rc)) throw rc;
998 }
999 }
1000 }
1001 }
1002
1003 /* We have different approaches for getting the medias which needs to
1004 * be replicated based on the clone mode the user requested (this is
1005 * mostly about the full clone mode).
1006 * MachineState:
1007 * - Only the images which are directly attached to an source VM will
1008 * be cloned. Any parent disks in the original chain will be merged
1009 * into the final cloned disk.
1010 * MachineAndChildStates:
1011 * - In this case we search for images which have more than one
1012 * children in the cloned VM or are directly attached to the new VM.
1013 * All others will be merged into the remaining images which are
1014 * cloned.
1015 * This case is the most complicated one and needs several iterations
1016 * to make sure we are only cloning images which are really
1017 * necessary.
1018 * AllStates:
1019 * - All disks which are directly or indirectly attached to the
1020 * original VM are cloned.
1021 *
1022 * Note: If you change something generic in one of the methods its
1023 * likely that it need to be changed in the others as well! */
1024 ULONG uCount = 2; /* One init task and the machine creation. */
1025 ULONG uTotalWeight = 2; /* The init task and the machine creation is worth one. */
1026 bool fAttachLinked = d->options.contains(CloneOptions_Link); /* Linked clones requested? */
1027 switch (d->mode)
1028 {
1029 case CloneMode_MachineState:
1030 d->queryMediasForMachineState(machineList, fAttachLinked, uCount, uTotalWeight);
1031 break;
1032 case CloneMode_MachineAndChildStates:
1033 d->queryMediasForMachineAndChildStates(machineList, fAttachLinked, uCount, uTotalWeight);
1034 break;
1035 case CloneMode_AllStates:
1036 d->queryMediasForAllStates(machineList, fAttachLinked, uCount, uTotalWeight);
1037 break;
1038#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
1039 case CloneMode_32BitHack: /* (compiler warnings) */
1040 AssertFailedBreak();
1041#endif
1042 }
1043
1044 /* Now create the progress project, so the user knows whats going on. */
1045 rc = d->pProgress.createObject();
1046 if (FAILED(rc)) throw rc;
1047 rc = d->pProgress->init(p->i_getVirtualBox(),
1048 static_cast<IMachine*>(d->pSrcMachine) /* aInitiator */,
1049 Bstr(tr("Cloning Machine")).raw(),
1050 true /* fCancellable */,
1051 uCount,
1052 uTotalWeight,
1053 Bstr(tr("Initialize Cloning")).raw(),
1054 1);
1055 if (FAILED(rc)) throw rc;
1056
1057 int vrc = d->startWorker();
1058
1059 if (RT_FAILURE(vrc))
1060 p->setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not create machine clone thread (%Rrc)"), vrc);
1061 }
1062 catch (HRESULT rc2)
1063 {
1064 rc = rc2;
1065 }
1066
1067 if (SUCCEEDED(rc))
1068 d->pProgress.queryInterfaceTo(pProgress);
1069
1070 return rc;
1071}
1072
1073HRESULT MachineCloneVM::run()
1074{
1075 DPTR(MachineCloneVM);
1076 ComObjPtr<Machine> &p = d->p;
1077
1078 AutoCaller autoCaller(p);
1079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1080
1081 AutoReadLock srcLock(p COMMA_LOCKVAL_SRC_POS);
1082 AutoWriteLock trgLock(d->pTrgMachine COMMA_LOCKVAL_SRC_POS);
1083
1084 HRESULT rc = S_OK;
1085
1086 /*
1087 * Todo:
1088 * - What about log files?
1089 */
1090
1091 /* Where should all the media go? */
1092 Utf8Str strTrgSnapshotFolder;
1093 Utf8Str strTrgMachineFolder = d->pTrgMachine->i_getSettingsFileFull();
1094 strTrgMachineFolder.stripFilename();
1095
1096 RTCList<ComObjPtr<Medium> > newMedia; /* All created images */
1097 RTCList<Utf8Str> newFiles; /* All extra created files (save states, ...) */
1098 std::set<ComObjPtr<Medium> > pMediumsForNotify;
1099 std::map<Guid, DeviceType_T> uIdsForNotify;
1100 try
1101 {
1102 /* Copy all the configuration from this machine to an empty
1103 * configuration dataset. */
1104 settings::MachineConfigFile trgMCF = *d->pSrcMachine->mData->pMachineConfigFile;
1105
1106 /* keep source machine hardware UUID if enabled*/
1107 if (d->options.contains(CloneOptions_KeepHwUUIDs))
1108 {
1109 /* because HW UUIDs must be preserved including snapshots by the option,
1110 * just fill zero UUIDs with corresponding machine UUID before any snapshot
1111 * processing will take place, while all uuids are from source machine */
1112 if (!trgMCF.hardwareMachine.uuid.isValid() || trgMCF.hardwareMachine.uuid.isZero())
1113 trgMCF.hardwareMachine.uuid = trgMCF.uuid;
1114
1115 MachineCloneVMPrivate::updateSnapshotHardwareUUIDs(trgMCF.llFirstSnapshot, trgMCF.uuid);
1116 }
1117
1118
1119 /* Reset media registry. */
1120 trgMCF.mediaRegistry.llHardDisks.clear();
1121 trgMCF.mediaRegistry.llDvdImages.clear();
1122 trgMCF.mediaRegistry.llFloppyImages.clear();
1123 /* If we got a valid snapshot id, replace the hardware/storage section
1124 * with the stuff from the snapshot. */
1125 settings::Snapshot sn;
1126
1127 if (d->snapshotId.isValid() && !d->snapshotId.isZero())
1128 if (!d->findSnapshot(trgMCF.llFirstSnapshot, d->snapshotId, sn))
1129 throw p->setError(E_FAIL,
1130 tr("Could not find data to snapshots '%s'"), d->snapshotId.toString().c_str());
1131
1132 if (d->mode == CloneMode_MachineState)
1133 {
1134 if (sn.uuid.isValid() && !sn.uuid.isZero())
1135 trgMCF.hardwareMachine = sn.hardware;
1136
1137 /* Remove any hint on snapshots. */
1138 trgMCF.llFirstSnapshot.clear();
1139 trgMCF.uuidCurrentSnapshot.clear();
1140 }
1141 else if ( d->mode == CloneMode_MachineAndChildStates
1142 && sn.uuid.isValid()
1143 && !sn.uuid.isZero())
1144 {
1145 if (!d->pOldMachineState.isNull())
1146 {
1147 /* Copy the snapshot data to the current machine. */
1148 trgMCF.hardwareMachine = sn.hardware;
1149
1150 /* Current state is under root snapshot. */
1151 trgMCF.uuidCurrentSnapshot = sn.uuid;
1152 }
1153 /* The snapshot will be the root one. */
1154 trgMCF.llFirstSnapshot.clear();
1155 trgMCF.llFirstSnapshot.push_back(sn);
1156 }
1157
1158 /* Generate new MAC addresses for all machines when not forbidden. */
1159 if (!d->options.contains(CloneOptions_KeepAllMACs))
1160 {
1161 d->updateMACAddresses(trgMCF.hardwareMachine.llNetworkAdapters);
1162 d->updateMACAddresses(trgMCF.llFirstSnapshot);
1163 }
1164
1165 /* When the current snapshot folder is absolute we reset it to the
1166 * default relative folder. */
1167 if (RTPathStartsWithRoot(trgMCF.machineUserData.strSnapshotFolder.c_str()))
1168 trgMCF.machineUserData.strSnapshotFolder = "Snapshots";
1169 trgMCF.strStateFile = "";
1170 /* Set the new name. */
1171 const Utf8Str strOldVMName = trgMCF.machineUserData.strName;
1172 trgMCF.machineUserData.strName = d->pTrgMachine->mUserData->s.strName;
1173 trgMCF.uuid = d->pTrgMachine->mData->mUuid;
1174
1175 Bstr bstrSrcSnapshotFolder;
1176 rc = d->pSrcMachine->COMGETTER(SnapshotFolder)(bstrSrcSnapshotFolder.asOutParam());
1177 if (FAILED(rc)) throw rc;
1178 /* The absolute name of the snapshot folder. */
1179 strTrgSnapshotFolder = Utf8StrFmt("%s%c%s", strTrgMachineFolder.c_str(), RTPATH_DELIMITER,
1180 trgMCF.machineUserData.strSnapshotFolder.c_str());
1181
1182 /* Should we rename the disk names. */
1183 bool fKeepDiskNames = d->options.contains(CloneOptions_KeepDiskNames);
1184
1185 /* We need to create a map with the already created medias. This is
1186 * necessary, cause different snapshots could have the same
1187 * parents/parent chain. If a medium is in this map already, it isn't
1188 * cloned a second time, but simply used. */
1189 typedef std::map<Utf8Str, ComObjPtr<Medium> > TStrMediumMap;
1190 typedef std::pair<Utf8Str, ComObjPtr<Medium> > TStrMediumPair;
1191 TStrMediumMap map;
1192 size_t cDisks = 0;
1193 for (size_t i = 0; i < d->llMedias.size(); ++i)
1194 {
1195 const MEDIUMTASKCHAIN &mtc = d->llMedias.at(i);
1196 ComObjPtr<Medium> pNewParent;
1197 uint32_t uSrcParentIdx = UINT32_MAX;
1198 uint32_t uTrgParentIdx = UINT32_MAX;
1199 for (size_t a = mtc.chain.size(); a > 0; --a)
1200 {
1201 const MEDIUMTASK &mt = mtc.chain.at(a - 1);
1202 ComPtr<IMedium> pMedium = mt.pMedium;
1203
1204 Bstr bstrSrcName;
1205 rc = pMedium->COMGETTER(Name)(bstrSrcName.asOutParam());
1206 if (FAILED(rc)) throw rc;
1207
1208 rc = d->pProgress->SetNextOperation(BstrFmt(tr("Cloning Disk '%ls' ..."), bstrSrcName.raw()).raw(),
1209 mt.uWeight);
1210 if (FAILED(rc)) throw rc;
1211
1212 Bstr bstrSrcId;
1213 rc = pMedium->COMGETTER(Id)(bstrSrcId.asOutParam());
1214 if (FAILED(rc)) throw rc;
1215
1216 if (mtc.fAttachLinked)
1217 {
1218 IMedium *pTmp = pMedium;
1219 ComObjPtr<Medium> pLMedium = static_cast<Medium*>(pTmp);
1220 if (pLMedium.isNull())
1221 throw p->setError(VBOX_E_OBJECT_NOT_FOUND);
1222 ComObjPtr<Medium> pBase = pLMedium->i_getBase();
1223 if (pBase->i_isReadOnly())
1224 {
1225 ComObjPtr<Medium> pDiff;
1226 /* create the diff under the snapshot medium */
1227 trgLock.release();
1228 srcLock.release();
1229 rc = d->createDifferencingMedium(p, pLMedium, strTrgSnapshotFolder,
1230 newMedia, &pDiff);
1231 srcLock.acquire();
1232 trgLock.acquire();
1233 if (FAILED(rc)) throw rc;
1234 map.insert(TStrMediumPair(Utf8Str(bstrSrcId), pDiff));
1235 /* diff image has to be used... */
1236 pNewParent = pDiff;
1237 pMediumsForNotify.insert(pDiff->i_getParent());
1238 uIdsForNotify[pDiff->i_getId()] = pDiff->i_getDeviceType();
1239 }
1240 else
1241 {
1242 /* Attach the medium directly, as its type is not
1243 * subject to diff creation. */
1244 newMedia.append(pLMedium);
1245 map.insert(TStrMediumPair(Utf8Str(bstrSrcId), pLMedium));
1246 pNewParent = pLMedium;
1247 }
1248 }
1249 else
1250 {
1251 /* Is a clone already there? */
1252 TStrMediumMap::iterator it = map.find(Utf8Str(bstrSrcId));
1253 if (it != map.end())
1254 pNewParent = it->second;
1255 else
1256 {
1257 ComPtr<IMediumFormat> pSrcFormat;
1258 rc = pMedium->COMGETTER(MediumFormat)(pSrcFormat.asOutParam());
1259 ULONG uSrcCaps = 0;
1260 com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap;
1261 rc = pSrcFormat->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap));
1262
1263 if (FAILED(rc)) throw rc;
1264 else
1265 {
1266 for (ULONG j = 0; j < mediumFormatCap.size(); j++)
1267 uSrcCaps |= mediumFormatCap[j];
1268 }
1269
1270 /* Default format? */
1271 Utf8Str strDefaultFormat;
1272 if (mtc.devType == DeviceType_HardDisk)
1273 p->mParent->i_getDefaultHardDiskFormat(strDefaultFormat);
1274 else
1275 strDefaultFormat = "RAW";
1276
1277 Bstr bstrSrcFormat(strDefaultFormat);
1278
1279 ULONG srcVar = MediumVariant_Standard;
1280 com::SafeArray <MediumVariant_T> mediumVariant;
1281
1282 /* Is the source file based? */
1283 if ((uSrcCaps & MediumFormatCapabilities_File) == MediumFormatCapabilities_File)
1284 {
1285 /* Yes, just use the source format. Otherwise the defaults
1286 * will be used. */
1287 rc = pMedium->COMGETTER(Format)(bstrSrcFormat.asOutParam());
1288 if (FAILED(rc)) throw rc;
1289
1290 rc = pMedium->COMGETTER(Variant)(ComSafeArrayAsOutParam(mediumVariant));
1291 if (FAILED(rc)) throw rc;
1292 else
1293 {
1294 for (size_t j = 0; j < mediumVariant.size(); j++)
1295 srcVar |= mediumVariant[j];
1296 }
1297 }
1298
1299 Guid newId;
1300 newId.create();
1301 Utf8Str strNewName(bstrSrcName);
1302 if (!fKeepDiskNames)
1303 {
1304 Utf8Str strSrcTest = bstrSrcName;
1305 /* Check if we have to use another name. */
1306 if (!mt.strBaseName.isEmpty())
1307 strSrcTest = mt.strBaseName;
1308 strSrcTest.stripSuffix();
1309 /* If the old disk name was in {uuid} format we also
1310 * want the new name in this format, but with the
1311 * updated id of course. If the old disk was called
1312 * like the VM name, we change it to the new VM name.
1313 * For all other disks we rename them with this
1314 * template: "new name-disk1.vdi". */
1315 if (strSrcTest == strOldVMName)
1316 strNewName = Utf8StrFmt("%s%s", trgMCF.machineUserData.strName.c_str(),
1317 RTPathSuffix(Utf8Str(bstrSrcName).c_str()));
1318 else if ( strSrcTest.startsWith("{")
1319 && strSrcTest.endsWith("}"))
1320 {
1321 strSrcTest = strSrcTest.substr(1, strSrcTest.length() - 2);
1322
1323 Guid temp_guid(strSrcTest);
1324 if (temp_guid.isValid() && !temp_guid.isZero())
1325 strNewName = Utf8StrFmt("%s%s", newId.toStringCurly().c_str(),
1326 RTPathSuffix(strNewName.c_str()));
1327 }
1328 else
1329 strNewName = Utf8StrFmt("%s-disk%d%s", trgMCF.machineUserData.strName.c_str(), ++cDisks,
1330 RTPathSuffix(Utf8Str(bstrSrcName).c_str()));
1331 }
1332
1333 /* Check if this medium comes from the snapshot folder, if
1334 * so, put it there in the cloned machine as well.
1335 * Otherwise it goes to the machine folder. */
1336 Bstr bstrSrcPath;
1337 Utf8Str strFile = Utf8StrFmt("%s%c%s", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, strNewName.c_str());
1338 rc = pMedium->COMGETTER(Location)(bstrSrcPath.asOutParam());
1339 if (FAILED(rc)) throw rc;
1340 if ( !bstrSrcPath.isEmpty()
1341 && RTPathStartsWith(Utf8Str(bstrSrcPath).c_str(), Utf8Str(bstrSrcSnapshotFolder).c_str())
1342 && (fKeepDiskNames || mt.strBaseName.isEmpty()))
1343 strFile = Utf8StrFmt("%s%c%s", strTrgSnapshotFolder.c_str(), RTPATH_DELIMITER, strNewName.c_str());
1344
1345 /* Start creating the clone. */
1346 ComObjPtr<Medium> pTarget;
1347 rc = pTarget.createObject();
1348 if (FAILED(rc)) throw rc;
1349
1350 rc = pTarget->init(p->mParent,
1351 Utf8Str(bstrSrcFormat),
1352 strFile,
1353 Guid::Empty /* empty media registry */,
1354 mtc.devType);
1355 if (FAILED(rc)) throw rc;
1356
1357 /* Update the new uuid. */
1358 pTarget->i_updateId(newId);
1359
1360 /* Do the disk cloning. */
1361 ComPtr<IProgress> progress2;
1362
1363 ComObjPtr<Medium> pLMedium = static_cast<Medium*>((IMedium*)pMedium);
1364 srcLock.release();
1365 rc = pLMedium->i_cloneToEx(pTarget,
1366 (MediumVariant_T)srcVar,
1367 pNewParent,
1368 progress2.asOutParam(),
1369 uSrcParentIdx,
1370 uTrgParentIdx,
1371 false /* aNotify */);
1372 srcLock.acquire();
1373 if (FAILED(rc)) throw rc;
1374
1375 /* Wait until the async process has finished. */
1376 srcLock.release();
1377 rc = d->pProgress->WaitForOtherProgressCompletion(progress2, 0 /* indefinite wait */);
1378 srcLock.acquire();
1379 if (FAILED(rc)) throw rc;
1380
1381 /* Remember created medium. */
1382 newMedia.append(pTarget);
1383 /* Get the medium type from the source and set it to the
1384 * new medium. */
1385 MediumType_T type;
1386 rc = pMedium->COMGETTER(Type)(&type);
1387 if (FAILED(rc)) throw rc;
1388 trgLock.release();
1389 srcLock.release();
1390 rc = pTarget->COMSETTER(Type)(type);
1391 srcLock.acquire();
1392 trgLock.acquire();
1393 if (FAILED(rc)) throw rc;
1394 map.insert(TStrMediumPair(Utf8Str(bstrSrcId), pTarget));
1395 /* register the new medium */
1396 {
1397 AutoWriteLock tlock(p->mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1398 rc = p->mParent->i_registerMedium(pTarget, &pTarget,
1399 tlock);
1400 if (FAILED(rc)) throw rc;
1401 }
1402 /* This medium becomes the parent of the next medium in the
1403 * chain. */
1404 pNewParent = pTarget;
1405 uIdsForNotify[pTarget->i_getId()] = pTarget->i_getDeviceType();
1406 }
1407 }
1408 /* Save the current source medium index as the new parent
1409 * medium index. */
1410 uSrcParentIdx = mt.uIdx;
1411 /* Simply increase the target index. */
1412 ++uTrgParentIdx;
1413 }
1414
1415 Bstr bstrSrcId;
1416 rc = mtc.chain.first().pMedium->COMGETTER(Id)(bstrSrcId.asOutParam());
1417 if (FAILED(rc)) throw rc;
1418 Bstr bstrTrgId;
1419 rc = pNewParent->COMGETTER(Id)(bstrTrgId.asOutParam());
1420 if (FAILED(rc)) throw rc;
1421 /* update snapshot configuration */
1422 d->updateSnapshotStorageLists(trgMCF.llFirstSnapshot, bstrSrcId, bstrTrgId);
1423
1424 /* create new 'Current State' diff for caller defined place */
1425 if (mtc.fCreateDiffs)
1426 {
1427 const MEDIUMTASK &mt = mtc.chain.first();
1428 ComObjPtr<Medium> pLMedium = static_cast<Medium*>((IMedium*)mt.pMedium);
1429 if (pLMedium.isNull())
1430 throw p->setError(VBOX_E_OBJECT_NOT_FOUND);
1431 ComObjPtr<Medium> pBase = pLMedium->i_getBase();
1432 if (pBase->i_isReadOnly())
1433 {
1434 ComObjPtr<Medium> pDiff;
1435 trgLock.release();
1436 srcLock.release();
1437 rc = d->createDifferencingMedium(p, pNewParent, strTrgSnapshotFolder,
1438 newMedia, &pDiff);
1439 srcLock.acquire();
1440 trgLock.acquire();
1441 if (FAILED(rc)) throw rc;
1442 /* diff image has to be used... */
1443 pNewParent = pDiff;
1444 pMediumsForNotify.insert(pDiff->i_getParent());
1445 uIdsForNotify[pDiff->i_getId()] = pDiff->i_getDeviceType();
1446 }
1447 else
1448 {
1449 /* Attach the medium directly, as its type is not
1450 * subject to diff creation. */
1451 newMedia.append(pNewParent);
1452 }
1453
1454 rc = pNewParent->COMGETTER(Id)(bstrTrgId.asOutParam());
1455 if (FAILED(rc)) throw rc;
1456 }
1457 /* update 'Current State' configuration */
1458 d->updateStorageLists(trgMCF.hardwareMachine.storage.llStorageControllers, bstrSrcId, bstrTrgId);
1459 }
1460 /* Make sure all disks know of the new machine uuid. We do this last to
1461 * be able to change the medium type above. */
1462 for (size_t i = newMedia.size(); i > 0; --i)
1463 {
1464 const ComObjPtr<Medium> &pMedium = newMedia.at(i - 1);
1465 AutoCaller mac(pMedium);
1466 if (FAILED(mac.rc())) throw mac.rc();
1467 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
1468 Guid uuid = d->pTrgMachine->mData->mUuid;
1469 if (d->options.contains(CloneOptions_Link))
1470 {
1471 ComObjPtr<Medium> pParent = pMedium->i_getParent();
1472 mlock.release();
1473 if (!pParent.isNull())
1474 {
1475 AutoCaller mac2(pParent);
1476 if (FAILED(mac2.rc())) throw mac2.rc();
1477 AutoReadLock mlock2(pParent COMMA_LOCKVAL_SRC_POS);
1478 if (pParent->i_getFirstRegistryMachineId(uuid))
1479 {
1480 mlock2.release();
1481 trgLock.release();
1482 srcLock.release();
1483 p->mParent->i_markRegistryModified(uuid);
1484 srcLock.acquire();
1485 trgLock.acquire();
1486 mlock2.acquire();
1487 }
1488 }
1489 mlock.acquire();
1490 }
1491 pMedium->i_removeRegistry(p->i_getVirtualBox()->i_getGlobalRegistryId());
1492 pMedium->i_addRegistry(uuid);
1493 }
1494 /* Check if a snapshot folder is necessary and if so doesn't already
1495 * exists. */
1496 if ( !d->llSaveStateFiles.isEmpty()
1497 && !RTDirExists(strTrgSnapshotFolder.c_str()))
1498 {
1499 int vrc = RTDirCreateFullPath(strTrgSnapshotFolder.c_str(), 0700);
1500 if (RT_FAILURE(vrc))
1501 throw p->setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
1502 tr("Could not create snapshots folder '%s' (%Rrc)"),
1503 strTrgSnapshotFolder.c_str(), vrc);
1504 }
1505 /* Clone all save state files. */
1506 for (size_t i = 0; i < d->llSaveStateFiles.size(); ++i)
1507 {
1508 FILECOPYTASK fct = d->llSaveStateFiles.at(i);
1509 const Utf8Str &strTrgSaveState = Utf8StrFmt("%s%c%s", strTrgSnapshotFolder.c_str(), RTPATH_DELIMITER,
1510 RTPathFilename(fct.strFile.c_str()));
1511
1512 /* Move to next sub-operation. */
1513 rc = d->pProgress->SetNextOperation(BstrFmt(tr("Copy save state file '%s' ..."),
1514 RTPathFilename(fct.strFile.c_str())).raw(), fct.uWeight);
1515 if (FAILED(rc)) throw rc;
1516 /* Copy the file only if it was not copied already. */
1517 if (!newFiles.contains(strTrgSaveState.c_str()))
1518 {
1519 int vrc = RTFileCopyEx(fct.strFile.c_str(), strTrgSaveState.c_str(), 0,
1520 MachineCloneVMPrivate::copyFileProgress, &d->pProgress);
1521 if (RT_FAILURE(vrc))
1522 throw p->setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
1523 tr("Could not copy state file '%s' to '%s' (%Rrc)"),
1524 fct.strFile.c_str(), strTrgSaveState.c_str(), vrc);
1525 newFiles.append(strTrgSaveState);
1526 }
1527 /* Update the path in the configuration either for the current
1528 * machine state or the snapshots. */
1529 if (!fct.snapshotUuid.isValid() || fct.snapshotUuid.isZero())
1530 trgMCF.strStateFile = strTrgSaveState;
1531 else
1532 d->updateSaveStateFile(trgMCF.llFirstSnapshot, fct.snapshotUuid, strTrgSaveState);
1533 }
1534
1535 /* Clone all NVRAM files. */
1536 for (size_t i = 0; i < d->llNVRAMFiles.size(); ++i)
1537 {
1538 FILECOPYTASK fct = d->llNVRAMFiles.at(i);
1539 Utf8Str strTrgNVRAM;
1540 if (!fct.snapshotUuid.isValid() || fct.snapshotUuid.isZero())
1541 strTrgNVRAM = Utf8StrFmt("%s%c%s.nvram", strTrgMachineFolder.c_str(), RTPATH_DELIMITER,
1542 trgMCF.machineUserData.strName.c_str());
1543 else
1544 strTrgNVRAM = Utf8StrFmt("%s%c%s", strTrgSnapshotFolder.c_str(), RTPATH_DELIMITER,
1545 RTPathFilename(fct.strFile.c_str()));
1546
1547 /* Move to next sub-operation. */
1548 rc = d->pProgress->SetNextOperation(BstrFmt(tr("Copy NVRAM file '%s' ..."),
1549 RTPathFilename(fct.strFile.c_str())).raw(), fct.uWeight);
1550 if (FAILED(rc)) throw rc;
1551 /* Copy the file only if it was not copied already. */
1552 if (!newFiles.contains(strTrgNVRAM.c_str()))
1553 {
1554 rc = p->i_getVirtualBox()->i_ensureFilePathExists(strTrgNVRAM.c_str(), true);
1555 if (FAILED(rc)) throw rc;
1556 int vrc = RTFileCopyEx(fct.strFile.c_str(), strTrgNVRAM.c_str(), 0,
1557 MachineCloneVMPrivate::copyFileProgress, &d->pProgress);
1558 if (RT_FAILURE(vrc))
1559 throw p->setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
1560 tr("Could not copy NVRAM file '%s' to '%s' (%Rrc)"),
1561 fct.strFile.c_str(), strTrgNVRAM.c_str(), vrc);
1562 newFiles.append(strTrgNVRAM);
1563 }
1564 /* Update the path in the configuration either for the current
1565 * machine state or the snapshots. */
1566 if (!fct.snapshotUuid.isValid() || fct.snapshotUuid.isZero())
1567 trgMCF.hardwareMachine.nvramSettings.strNvramPath = strTrgNVRAM;
1568 else
1569 d->updateNVRAMFile(trgMCF.llFirstSnapshot, fct.snapshotUuid, strTrgNVRAM);
1570 }
1571
1572 {
1573 rc = d->pProgress->SetNextOperation(BstrFmt(tr("Create Machine Clone '%s' ..."),
1574 trgMCF.machineUserData.strName.c_str()).raw(), 1);
1575 if (FAILED(rc)) throw rc;
1576 /* After modifying the new machine config, we can copy the stuff
1577 * over to the new machine. The machine have to be mutable for
1578 * this. */
1579 rc = d->pTrgMachine->i_checkStateDependency(p->MutableStateDep);
1580 if (FAILED(rc)) throw rc;
1581 rc = d->pTrgMachine->i_loadMachineDataFromSettings(trgMCF, &d->pTrgMachine->mData->mUuid);
1582 if (FAILED(rc)) throw rc;
1583
1584 /* Fix up the "current state modified" flag to what it should be,
1585 * as the value guessed in i_loadMachineDataFromSettings can be
1586 * quite far off the logical value for the cloned VM. */
1587 if (d->mode == CloneMode_MachineState)
1588 d->pTrgMachine->mData->mCurrentStateModified = FALSE;
1589 else if ( d->mode == CloneMode_MachineAndChildStates
1590 && sn.uuid.isValid()
1591 && !sn.uuid.isZero())
1592 {
1593 if (!d->pOldMachineState.isNull())
1594 {
1595 /* There will be created a new differencing image based on
1596 * this snapshot. So reset the modified state. */
1597 d->pTrgMachine->mData->mCurrentStateModified = FALSE;
1598 }
1599 else
1600 d->pTrgMachine->mData->mCurrentStateModified = p->mData->mCurrentStateModified;
1601 }
1602 else if (d->mode == CloneMode_AllStates)
1603 d->pTrgMachine->mData->mCurrentStateModified = p->mData->mCurrentStateModified;
1604
1605 /* If the target machine has saved state we MUST adjust the machine
1606 * state, otherwise saving settings will drop the information. */
1607 if (trgMCF.strStateFile.isNotEmpty())
1608 d->pTrgMachine->i_setMachineState(MachineState_Saved);
1609
1610 /* save all VM data */
1611 bool fNeedsGlobalSaveSettings = false;
1612 rc = d->pTrgMachine->i_saveSettings(&fNeedsGlobalSaveSettings, trgLock, Machine::SaveS_Force);
1613 if (FAILED(rc)) throw rc;
1614 /* Release all locks */
1615 trgLock.release();
1616 srcLock.release();
1617 if (fNeedsGlobalSaveSettings)
1618 {
1619 /* save the global settings; for that we should hold only the
1620 * VirtualBox lock */
1621 AutoWriteLock vlock(p->mParent COMMA_LOCKVAL_SRC_POS);
1622 rc = p->mParent->i_saveSettings();
1623 if (FAILED(rc)) throw rc;
1624 }
1625 }
1626
1627 /* Any additional machines need saving? */
1628 p->mParent->i_saveModifiedRegistries();
1629 }
1630 catch (HRESULT rc2)
1631 {
1632 /* Error handling code only works correctly without locks held. */
1633 trgLock.release();
1634 srcLock.release();
1635 rc = rc2;
1636 }
1637 catch (...)
1638 {
1639 rc = VirtualBoxBase::handleUnexpectedExceptions(p, RT_SRC_POS);
1640 }
1641
1642 MultiResult mrc(rc);
1643 /* Cleanup on failure (CANCEL also) */
1644 if (FAILED(rc))
1645 {
1646 int vrc = VINF_SUCCESS;
1647 /* Delete all created files. */
1648 for (size_t i = 0; i < newFiles.size(); ++i)
1649 {
1650 vrc = RTFileDelete(newFiles.at(i).c_str());
1651 if (RT_FAILURE(vrc))
1652 mrc = p->setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
1653 tr("Could not delete file '%s' (%Rrc)"), newFiles.at(i).c_str(), vrc);
1654 }
1655 /* Delete all already created medias. (Reverse, cause there could be
1656 * parent->child relations.) */
1657 for (size_t i = newMedia.size(); i > 0; --i)
1658 {
1659 const ComObjPtr<Medium> &pMedium = newMedia.at(i - 1);
1660 mrc = pMedium->i_deleteStorage(NULL /* aProgress */,
1661 true /* aWait */,
1662 false /* aNotify */);
1663 pMedium->Close();
1664 }
1665 /* Delete the snapshot folder when not empty. */
1666 if (!strTrgSnapshotFolder.isEmpty())
1667 RTDirRemove(strTrgSnapshotFolder.c_str());
1668 /* Delete the machine folder when not empty. */
1669 RTDirRemove(strTrgMachineFolder.c_str());
1670
1671 /* Must save the modified registries */
1672 p->mParent->i_saveModifiedRegistries();
1673 }
1674 else
1675 {
1676 for (std::map<Guid, DeviceType_T>::const_iterator it = uIdsForNotify.begin();
1677 it != uIdsForNotify.end();
1678 ++it)
1679 {
1680 p->mParent->i_onMediumRegistered(it->first, it->second, TRUE);
1681 }
1682 for (std::set<ComObjPtr<Medium> >::const_iterator it = pMediumsForNotify.begin();
1683 it != pMediumsForNotify.end();
1684 ++it)
1685 {
1686 if (it->isNotNull())
1687 p->mParent->i_onMediumConfigChanged(*it);
1688 }
1689 }
1690
1691 return mrc;
1692}
1693
1694void MachineCloneVM::destroy()
1695{
1696 delete this;
1697}
1698
Note: See TracBrowser for help on using the repository browser.

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