VirtualBox

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

Last change on this file since 91404 was 91326, checked in by vboxsync, 3 years ago

Main/NvramStore,FE/VBoxManage: Allow multiple NVRAM states (UEFI,TPM,etc.) to exist for a VM and and manage them in a central place. This allows to collect them in a single tar archive and provide a single interface to get access to the individual states (work in progress), bugref:10098

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