VirtualBox

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

Last change on this file since 105659 was 98352, checked in by vboxsync, 22 months ago

Main: Fix identifiers containing an incorrect plural of "medium".

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