VirtualBox

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

Last change on this file since 85672 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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