VirtualBox

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

Last change on this file since 37521 was 37521, checked in by vboxsync, 13 years ago

Main-CloneVM: fix cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.5 KB
Line 
1/* $Id: MachineImplCloneVM.cpp 37521 2011-06-17 06:56:59Z vboxsync $ */
2/** @file
3 * Implementation of MachineCloneVM
4 */
5
6/*
7 * Copyright (C) 2011 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 "MachineImplCloneVM.h"
19
20#include "VirtualBoxImpl.h"
21#include "MediumImpl.h"
22
23#include <iprt/path.h>
24#include <iprt/dir.h>
25#include <iprt/cpp/utils.h>
26
27#include <VBox/com/list.h>
28#include <VBox/com/MultiResult.h>
29
30// typedefs
31/////////////////////////////////////////////////////////////////////////////
32
33typedef struct
34{
35 ComPtr<IMedium> pMedium;
36 uint64_t uSize;
37}MEDIUMTASK;
38
39typedef struct
40{
41 RTCList<MEDIUMTASK> chain;
42 bool fCreateDiffs;
43}MEDIUMTASKCHAIN;
44
45typedef struct
46{
47 Guid snapshotUuid;
48 Utf8Str strSaveStateFile;
49 uint64_t cbSize;
50}SAVESTATETASK;
51
52// The private class
53/////////////////////////////////////////////////////////////////////////////
54
55struct MachineCloneVMPrivate
56{
57 MachineCloneVMPrivate(MachineCloneVM *a_q, ComObjPtr<Machine> &a_pSrcMachine, ComObjPtr<Machine> &a_pTrgMachine, CloneMode_T a_mode, bool a_fFullClone)
58 : q_ptr(a_q)
59 , p(a_pSrcMachine)
60 , pSrcMachine(a_pSrcMachine)
61 , pTrgMachine(a_pTrgMachine)
62 , mode(a_mode)
63 , fLinkDisks(!a_fFullClone)
64 {}
65
66 /* Thread management */
67 int startWorker()
68 {
69 return RTThreadCreate(NULL,
70 MachineCloneVMPrivate::workerThread,
71 static_cast<void*>(this),
72 0,
73 RTTHREADTYPE_MAIN_WORKER,
74 0,
75 "MachineClone");
76 }
77
78 static int workerThread(RTTHREAD /* Thread */, void *pvUser)
79 {
80 MachineCloneVMPrivate *pTask = static_cast<MachineCloneVMPrivate*>(pvUser);
81 AssertReturn(pTask, VERR_INVALID_POINTER);
82
83 HRESULT rc = pTask->q_ptr->run();
84
85 pTask->pProgress->notifyComplete(rc);
86
87 pTask->q_ptr->destroy();
88
89 return VINF_SUCCESS;
90 }
91
92 /* Private helper methods */
93 HRESULT cloneCreateMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const;
94 settings::Snapshot cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const;
95 void cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const;
96 void cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const;
97 void cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const;
98 static int cloneCopyStateFileProgress(unsigned uPercentage, void *pvUser);
99
100 /* Private q and parent pointer */
101 MachineCloneVM *q_ptr;
102 ComObjPtr<Machine> p;
103
104 /* Private helper members */
105 ComObjPtr<Machine> pSrcMachine;
106 ComObjPtr<Machine> pTrgMachine;
107 ComPtr<IMachine> pOldMachineState;
108 ComObjPtr<Progress> pProgress;
109 Guid snapshotId;
110 CloneMode_T mode;
111 bool fLinkDisks;
112 RTCList<MEDIUMTASKCHAIN> llMedias;
113 RTCList<SAVESTATETASK> llSaveStateFiles; /* Snapshot UUID -> File path */
114};
115
116HRESULT MachineCloneVMPrivate::cloneCreateMachineList(const ComPtr<ISnapshot> &pSnapshot, RTCList< ComObjPtr<Machine> > &machineList) const
117{
118 HRESULT rc = S_OK;
119 Bstr name;
120 rc = pSnapshot->COMGETTER(Name)(name.asOutParam());
121 if (FAILED(rc)) return rc;
122
123 ComPtr<IMachine> pMachine;
124 rc = pSnapshot->COMGETTER(Machine)(pMachine.asOutParam());
125 if (FAILED(rc)) return rc;
126 machineList.append((Machine*)(IMachine*)pMachine);
127
128 SafeIfaceArray<ISnapshot> sfaChilds;
129 rc = pSnapshot->COMGETTER(Children)(ComSafeArrayAsOutParam(sfaChilds));
130 if (FAILED(rc)) return rc;
131 for (size_t i = 0; i < sfaChilds.size(); ++i)
132 {
133 rc = cloneCreateMachineList(sfaChilds[i], machineList);
134 if (FAILED(rc)) return rc;
135 }
136
137 return rc;
138}
139
140settings::Snapshot MachineCloneVMPrivate::cloneFindSnapshot(settings::MachineConfigFile *pMCF, const settings::SnapshotsList &snl, const Guid &id) const
141{
142 settings::SnapshotsList::const_iterator it;
143 for (it = snl.begin(); it != snl.end(); ++it)
144 {
145 if (it->uuid == id)
146 return *it;
147 else if (!it->llChildSnapshots.empty())
148 return cloneFindSnapshot(pMCF, it->llChildSnapshots, id);
149 }
150 return settings::Snapshot();
151}
152
153void MachineCloneVMPrivate::cloneUpdateStorageLists(settings::StorageControllersList &sc, const Bstr &bstrOldId, const Bstr &bstrNewId) const
154{
155 settings::StorageControllersList::iterator it3;
156 for (it3 = sc.begin();
157 it3 != sc.end();
158 ++it3)
159 {
160 settings::AttachedDevicesList &llAttachments = it3->llAttachedDevices;
161 settings::AttachedDevicesList::iterator it4;
162 for (it4 = llAttachments.begin();
163 it4 != llAttachments.end();
164 ++it4)
165 {
166 if ( it4->deviceType == DeviceType_HardDisk
167 && it4->uuid == bstrOldId)
168 {
169 it4->uuid = bstrNewId;
170 }
171 }
172 }
173}
174
175void MachineCloneVMPrivate::cloneUpdateSnapshotStorageLists(settings::SnapshotsList &sl, const Bstr &bstrOldId, const Bstr &bstrNewId) const
176{
177 settings::SnapshotsList::iterator it;
178 for ( it = sl.begin();
179 it != sl.end();
180 ++it)
181 {
182 cloneUpdateStorageLists(it->storage.llStorageControllers, bstrOldId, bstrNewId);
183 if (!it->llChildSnapshots.empty())
184 cloneUpdateSnapshotStorageLists(it->llChildSnapshots, bstrOldId, bstrNewId);
185 }
186}
187
188void MachineCloneVMPrivate::cloneUpdateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile) const
189{
190 settings::SnapshotsList::iterator it;
191 for (it = snl.begin(); it != snl.end(); ++it)
192 {
193 if (it->uuid == id)
194 it->strStateFile = strFile;
195 else if (!it->llChildSnapshots.empty())
196 cloneUpdateStateFile(it->llChildSnapshots, id, strFile);
197 }
198}
199
200/* static */
201int MachineCloneVMPrivate::cloneCopyStateFileProgress(unsigned uPercentage, void *pvUser)
202{
203 ComObjPtr<Progress> pProgress = *static_cast< ComObjPtr<Progress>* >(pvUser);
204
205 BOOL fCanceled = false;
206 HRESULT rc = pProgress->COMGETTER(Canceled)(&fCanceled);
207 if (FAILED(rc)) return VERR_GENERAL_FAILURE;
208 /* If canceled by the user tell it to the copy operation. */
209 if (fCanceled) return VERR_CANCELLED;
210 /* Set the new process. */
211 rc = pProgress->SetCurrentOperationProgress(uPercentage);
212 if (FAILED(rc)) return VERR_GENERAL_FAILURE;
213
214 return VINF_SUCCESS;
215}
216
217
218// The public class
219/////////////////////////////////////////////////////////////////////////////
220
221MachineCloneVM::MachineCloneVM(ComObjPtr<Machine> pSrcMachine, ComObjPtr<Machine> pTrgMachine, CloneMode_T mode, bool fFullClone)
222 : d_ptr(new MachineCloneVMPrivate(this, pSrcMachine, pTrgMachine, mode, fFullClone))
223{
224}
225
226MachineCloneVM::~MachineCloneVM()
227{
228 delete d_ptr;
229}
230
231HRESULT MachineCloneVM::start(IProgress **pProgress)
232{
233 DPTR(MachineCloneVM);
234 ComObjPtr<Machine> &p = d->p;
235
236 HRESULT rc;
237 try
238 {
239 /* Lock the target machine early (so nobody mess around with it in the meantime). */
240 AutoWriteLock trgLock(d->pTrgMachine COMMA_LOCKVAL_SRC_POS);
241
242 if (d->pSrcMachine->isSnapshotMachine())
243 d->snapshotId = d->pSrcMachine->getSnapshotId();
244
245 /* Add the current machine and all snapshot machines below this machine
246 * in a list for further processing. */
247 RTCList< ComObjPtr<Machine> > machineList;
248
249 /* Include current state? */
250 if ( d->mode == CloneMode_MachineState)
251// || d->mode == CloneMode_AllStates)
252 machineList.append(d->pSrcMachine);
253 /* Should be done a depth copy with all child snapshots? */
254 if ( d->mode == CloneMode_MachineAndChildStates
255 || d->mode == CloneMode_AllStates)
256 {
257 ULONG cSnapshots = 0;
258 rc = d->pSrcMachine->COMGETTER(SnapshotCount)(&cSnapshots);
259 if (FAILED(rc)) throw rc;
260 if (cSnapshots > 0)
261 {
262 Utf8Str id;
263 if ( d->mode == CloneMode_MachineAndChildStates
264 && !d->snapshotId.isEmpty())
265 id = d->snapshotId.toString();
266 ComPtr<ISnapshot> pSnapshot;
267 rc = d->pSrcMachine->FindSnapshot(Bstr(id).raw(), pSnapshot.asOutParam());
268 if (FAILED(rc)) throw rc;
269 rc = d->cloneCreateMachineList(pSnapshot, machineList);
270 if (FAILED(rc)) throw rc;
271 rc = pSnapshot->COMGETTER(Machine)(d->pOldMachineState.asOutParam());
272 if (FAILED(rc)) throw rc;
273 }
274 }
275
276 /* Go over every machine and walk over every attachment this machine has. */
277 ULONG uCount = 2; /* One init task and the machine creation. */
278 ULONG uTotalWeight = 2; /* The init task and the machine creation is worth one. */
279 for (size_t i = 0; i < machineList.size(); ++i)
280 {
281 ComObjPtr<Machine> machine = machineList.at(i);
282 /* If this is the Snapshot Machine we want to clone, we need to
283 * create a new diff file for the new "current state". */
284 bool fCreateDiffs = false;
285 if (machine == d->pOldMachineState)
286 fCreateDiffs = true;
287 SafeIfaceArray<IMediumAttachment> sfaAttachments;
288 rc = machine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
289 if (FAILED(rc)) throw rc;
290 /* Add all attachments (and there parents) of the different
291 * machines to a worker list. */
292 for (size_t a = 0; a < sfaAttachments.size(); ++a)
293 {
294 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a];
295 DeviceType_T type;
296 rc = pAtt->COMGETTER(Type)(&type);
297 if (FAILED(rc)) throw rc;
298
299 /* Only harddisk's are of interest. */
300 if (type != DeviceType_HardDisk)
301 continue;
302
303 /* Valid medium attached? */
304 ComPtr<IMedium> pSrcMedium;
305 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam());
306 if (FAILED(rc)) throw rc;
307 if (pSrcMedium.isNull())
308 continue;
309
310 /* Build up a child->parent list of this attachment. (Note: we are
311 * not interested of any child's not attached to this VM. So this
312 * will not create a full copy of the base/child relationship.) */
313 MEDIUMTASKCHAIN mtc;
314 mtc.fCreateDiffs = fCreateDiffs;
315 while(!pSrcMedium.isNull())
316 {
317 /* Refresh the state so that the file size get read. */
318 MediumState_T e;
319 rc = pSrcMedium->RefreshState(&e);
320 if (FAILED(rc)) throw rc;
321 LONG64 lSize;
322 rc = pSrcMedium->COMGETTER(Size)(&lSize);
323 if (FAILED(rc)) throw rc;
324
325 /* Save the current medium, for later cloning. */
326 MEDIUMTASK mt;
327 mt.pMedium = pSrcMedium;
328 mt.uSize = lSize;
329 mtc.chain.append(mt);
330
331 /* Calculate progress data */
332 ++uCount;
333 uTotalWeight += lSize;
334
335 /* Query next parent. */
336 rc = pSrcMedium->COMGETTER(Parent)(pSrcMedium.asOutParam());
337 if (FAILED(rc)) throw rc;
338 };
339 d->llMedias.append(mtc);
340 }
341 Bstr bstrSrcSaveStatePath;
342 rc = machine->COMGETTER(StateFilePath)(bstrSrcSaveStatePath.asOutParam());
343 if (FAILED(rc)) throw rc;
344 if (!bstrSrcSaveStatePath.isEmpty())
345 {
346 SAVESTATETASK sst;
347 sst.snapshotUuid = machine->getSnapshotId();
348 sst.strSaveStateFile = bstrSrcSaveStatePath;
349 int vrc = RTFileQuerySize(sst.strSaveStateFile.c_str(), &sst.cbSize);
350 if (RT_FAILURE(vrc))
351 throw p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not query file size of '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), vrc);
352 d->llSaveStateFiles.append(sst);
353 ++uCount;
354 uTotalWeight += sst.cbSize;
355 }
356 }
357
358 rc = d->pProgress.createObject();
359 if (FAILED(rc)) throw rc;
360 rc = d->pProgress->init(p->getVirtualBox(),
361 static_cast<IMachine*>(d->pSrcMachine) /* aInitiator */,
362 Bstr(p->tr("Cloning Machine")).raw(),
363 true /* fCancellable */,
364 uCount,
365 uTotalWeight,
366 Bstr(p->tr("Initialize Cloning")).raw(),
367 1);
368 if (FAILED(rc)) throw rc;
369
370 int vrc = d->startWorker();
371
372 if (RT_FAILURE(vrc))
373 p->setError(VBOX_E_IPRT_ERROR, "Could not create machine clone thread (%Rrc)", vrc);
374 }
375 catch (HRESULT rc2)
376 {
377 rc = rc2;
378 }
379
380 if (SUCCEEDED(rc))
381 d->pProgress.queryInterfaceTo(pProgress);
382
383 return rc;
384}
385
386HRESULT MachineCloneVM::run()
387{
388 DPTR(MachineCloneVM);
389 ComObjPtr<Machine> &p = d->p;
390
391 AutoCaller autoCaller(p);
392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
393
394 AutoReadLock srcLock(p COMMA_LOCKVAL_SRC_POS);
395 AutoWriteLock trgLock(d->pTrgMachine COMMA_LOCKVAL_SRC_POS);
396
397 MultiResult rc = S_OK;
398
399 /*
400 * Todo:
401 * - Regardless where the old media comes from (e.g. snapshots folder) it
402 * goes to the new main VM folder. Maybe we like to be a little bit
403 * smarter here.
404 * - Snapshot diffs (can) have the uuid as name. After cloning this isn't
405 * right anymore. Is it worth to change to the new uuid? Or should the
406 * cloned disks called exactly as the original one or should all new disks
407 * get a new name with the new VM name in it.
408 * - What about log files?
409 */
410
411 /* Where should all the media go? */
412 Utf8Str strTrgSnapshotFolder;
413 Utf8Str strTrgMachineFolder = d->pTrgMachine->getSettingsFileFull();
414 strTrgMachineFolder.stripFilename();
415
416 RTCList< ComObjPtr<Medium> > newMedias; /* All created images */
417 RTCList<Utf8Str> newFiles; /* All extra created files (save states, ...) */
418 try
419 {
420 /* Copy all the configuration from this machine to an empty
421 * configuration dataset. */
422 settings::MachineConfigFile trgMCF = *d->pSrcMachine->mData->pMachineConfigFile;
423
424 /* Reset media registry. */
425 trgMCF.mediaRegistry.llHardDisks.clear();
426 /* If we got a valid snapshot id, replace the hardware/storage section
427 * with the stuff from the snapshot. */
428 settings::Snapshot sn;
429 if (!d->snapshotId.isEmpty())
430 sn = d->cloneFindSnapshot(&trgMCF, trgMCF.llFirstSnapshot, d->snapshotId);
431
432 if (d->mode == CloneMode_MachineState)
433 {
434 if (!sn.uuid.isEmpty())
435 {
436 trgMCF.hardwareMachine = sn.hardware;
437 trgMCF.storageMachine = sn.storage;
438 }
439
440 /* Remove any hint on snapshots. */
441 trgMCF.llFirstSnapshot.clear();
442 trgMCF.uuidCurrentSnapshot.clear();
443 }else
444 if ( d->mode == CloneMode_MachineAndChildStates
445 && !sn.uuid.isEmpty())
446 {
447 /* Copy the snapshot data to the current machine. */
448 trgMCF.hardwareMachine = sn.hardware;
449 trgMCF.storageMachine = sn.storage;
450
451 /* The snapshot will be the root one. */
452 trgMCF.uuidCurrentSnapshot = sn.uuid;
453 trgMCF.llFirstSnapshot.clear();
454 trgMCF.llFirstSnapshot.push_back(sn);
455 }
456
457 /* When the current snapshot folder is absolute we reset it to the
458 * default relative folder. */
459 if (RTPathStartsWithRoot(trgMCF.machineUserData.strSnapshotFolder.c_str()))
460 trgMCF.machineUserData.strSnapshotFolder = "Snapshots";
461 trgMCF.strStateFile = "";
462 /* Force writing of setting file. */
463 trgMCF.fCurrentStateModified = true;
464 /* Set the new name. */
465 trgMCF.machineUserData.strName = d->pTrgMachine->mUserData->s.strName;
466 trgMCF.uuid = d->pTrgMachine->mData->mUuid;
467
468 Bstr bstrSrcSnapshotFolder;
469 rc = d->pSrcMachine->COMGETTER(SnapshotFolder)(bstrSrcSnapshotFolder.asOutParam());
470 if (FAILED(rc)) throw rc;
471 /* The absolute name of the snapshot folder. */
472 strTrgSnapshotFolder = Utf8StrFmt("%s%c%s%c", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, trgMCF.machineUserData.strSnapshotFolder.c_str(), RTPATH_DELIMITER);
473
474 /* We need to create a map with the already created medias. This is
475 * necessary, cause different snapshots could have the same
476 * parents/parent chain. If a medium is in this map already, it isn't
477 * cloned a second time, but simply used. */
478 typedef std::map<Utf8Str, ComObjPtr<Medium> > TStrMediumMap;
479 typedef std::pair<Utf8Str, ComObjPtr<Medium> > TStrMediumPair;
480 TStrMediumMap map;
481 for (size_t i = 0; i < d->llMedias.size(); ++i)
482 {
483 const MEDIUMTASKCHAIN &mtc = d->llMedias.at(i);
484 ComObjPtr<Medium> pNewParent;
485 for (size_t a = mtc.chain.size(); a > 0; --a)
486 {
487 const MEDIUMTASK &mt = mtc.chain.at(a - 1);
488 ComPtr<IMedium> pMedium = mt.pMedium;
489
490 Bstr bstrSrcName;
491 rc = pMedium->COMGETTER(Name)(bstrSrcName.asOutParam());
492 if (FAILED(rc)) throw rc;
493
494 rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Cloning Disk '%ls' ..."), bstrSrcName.raw()).raw(), mt.uSize);
495 if (FAILED(rc)) throw rc;
496
497 Bstr bstrSrcId;
498 rc = pMedium->COMGETTER(Id)(bstrSrcId.asOutParam());
499 if (FAILED(rc)) throw rc;
500
501 /* Is a clone already there? */
502 TStrMediumMap::iterator it = map.find(Utf8Str(bstrSrcId));
503 if (it != map.end())
504 pNewParent = it->second;
505 else
506 {
507 ComPtr<IMediumFormat> pSrcFormat;
508 rc = pMedium->COMGETTER(MediumFormat)(pSrcFormat.asOutParam());
509 ULONG uSrcCaps = 0;
510 rc = pSrcFormat->COMGETTER(Capabilities)(&uSrcCaps);
511 if (FAILED(rc)) throw rc;
512
513 Bstr bstrSrcFormat = "VDI";
514 ULONG srcVar = MediumVariant_Standard;
515 /* Is the source file based? */
516 if ((uSrcCaps & MediumFormatCapabilities_File) == MediumFormatCapabilities_File)
517 {
518 /* Yes, just use the source format. Otherwise the defaults
519 * will be used. */
520 rc = pMedium->COMGETTER(Format)(bstrSrcFormat.asOutParam());
521 if (FAILED(rc)) throw rc;
522 rc = pMedium->COMGETTER(Variant)(&srcVar);
523 if (FAILED(rc)) throw rc;
524 }
525
526 /* Start creating the clone. */
527 ComObjPtr<Medium> pTarget;
528 rc = pTarget.createObject();
529 if (FAILED(rc)) throw rc;
530
531 Utf8Str strFile = Utf8StrFmt("%s%c%lS", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, bstrSrcName.raw());
532 rc = pTarget->init(p->mParent,
533 Utf8Str(bstrSrcFormat),
534 strFile,
535 d->pTrgMachine->mData->mUuid, /* media registry */
536 NULL /* llRegistriesThatNeedSaving */);
537 if (FAILED(rc)) throw rc;
538
539 /* Do the disk cloning. */
540 ComPtr<IProgress> progress2;
541 rc = pMedium->CloneTo(pTarget,
542 srcVar,
543 pNewParent,
544 progress2.asOutParam());
545 if (FAILED(rc)) throw rc;
546
547 /* Wait until the asynchrony process has finished. */
548 srcLock.release();
549 rc = d->pProgress->WaitForAsyncProgressCompletion(progress2);
550 srcLock.acquire();
551 if (FAILED(rc)) throw rc;
552
553 /* Check the result of the asynchrony process. */
554 LONG iRc;
555 rc = progress2->COMGETTER(ResultCode)(&iRc);
556 if (FAILED(rc)) throw rc;
557 if (FAILED(iRc))
558 {
559 /* If the thread of the progress object has an error, then
560 * retrieve the error info from there, or it'll be lost. */
561 ProgressErrorInfo info(progress2);
562 throw p->setError(iRc, Utf8Str(info.getText()).c_str());
563 }
564
565 map.insert(TStrMediumPair(Utf8Str(bstrSrcId), pTarget));
566
567 /* Remember created medias. */
568 newMedias.append(pTarget);
569 /* This medium becomes the parent of the next medium in the
570 * chain. */
571 pNewParent = pTarget;
572 }
573 }
574
575 /* Create diffs for the last image chain. */
576 if (mtc.fCreateDiffs)
577 {
578 Bstr bstrSrcId;
579 rc = pNewParent->COMGETTER(Id)(bstrSrcId.asOutParam());
580 if (FAILED(rc)) throw rc;
581 GuidList *pllRegistriesThatNeedSaving;
582 ComObjPtr<Medium> diff;
583 diff.createObject();
584 rc = diff->init(p->mParent,
585 pNewParent->getPreferredDiffFormat(),
586 strTrgSnapshotFolder,
587 d->pTrgMachine->mData->mUuid,
588 NULL); // pllRegistriesThatNeedSaving
589 if (FAILED(rc)) throw rc;
590 MediumLockList *pMediumLockList(new MediumLockList()); /* todo: deleteeeeeeeee */
591 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
592 true /* fMediumLockWrite */,
593 pNewParent,
594 *pMediumLockList);
595 if (FAILED(rc)) throw rc;
596 rc = pMediumLockList->Lock();
597 if (FAILED(rc)) throw rc;
598 rc = pNewParent->createDiffStorage(diff, MediumVariant_Standard,
599 pMediumLockList,
600 NULL /* aProgress */,
601 true /* aWait */,
602 NULL); // pllRegistriesThatNeedSaving
603 delete pMediumLockList;
604 if (FAILED(rc)) throw rc;
605 pNewParent = diff;
606 newMedias.append(diff);
607 }
608 Bstr bstrSrcId;
609 rc = mtc.chain.first().pMedium->COMGETTER(Id)(bstrSrcId.asOutParam());
610 if (FAILED(rc)) throw rc;
611 Bstr bstrTrgId;
612 rc = pNewParent->COMGETTER(Id)(bstrTrgId.asOutParam());
613 if (FAILED(rc)) throw rc;
614 /* We have to patch the configuration, so it contains the new
615 * medium uuid instead of the old one. */
616 d->cloneUpdateStorageLists(trgMCF.storageMachine.llStorageControllers, bstrSrcId, bstrTrgId);
617 d->cloneUpdateSnapshotStorageLists(trgMCF.llFirstSnapshot, bstrSrcId, bstrTrgId);
618 }
619 /* Clone all save state files. */
620 for (size_t i = 0; i < d->llSaveStateFiles.size(); ++i)
621 {
622 SAVESTATETASK sst = d->llSaveStateFiles.at(i);
623 const Utf8Str &strTrgSaveState = Utf8StrFmt("%s%s", strTrgSnapshotFolder.c_str(), RTPathFilename(sst.strSaveStateFile.c_str()));
624
625 /* Move to next sub-operation. */
626 rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Copy save state file '%s' ..."), RTPathFilename(sst.strSaveStateFile.c_str())).raw(), sst.cbSize);
627 if (FAILED(rc)) throw rc;
628 /* Copy the file only if it was not copied already. */
629 if (!newFiles.contains(strTrgSaveState.c_str()))
630 {
631 int vrc = RTFileCopyEx(sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), 0, MachineCloneVMPrivate::cloneCopyStateFileProgress, &d->pProgress);
632 if (RT_FAILURE(vrc))
633 throw p->setError(VBOX_E_IPRT_ERROR,
634 p->tr("Could not copy state file '%s' to '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), vrc);
635 newFiles.append(strTrgSaveState);
636 }
637 /* Update the path in the configuration either for the current
638 * machine state or the snapshots. */
639 if (sst.snapshotUuid.isEmpty())
640 trgMCF.strStateFile = strTrgSaveState;
641 else
642 d->cloneUpdateStateFile(trgMCF.llFirstSnapshot, sst.snapshotUuid, strTrgSaveState);
643 }
644
645 if (false)
646// if (!d->pOldMachineState.isNull())
647 {
648 SafeIfaceArray<IMediumAttachment> sfaAttachments;
649 rc = d->pOldMachineState->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
650 if (FAILED(rc)) throw rc;
651 for (size_t a = 0; a < sfaAttachments.size(); ++a)
652 {
653 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a];
654 DeviceType_T type;
655 rc = pAtt->COMGETTER(Type)(&type);
656 if (FAILED(rc)) throw rc;
657
658 /* Only harddisk's are of interest. */
659 if (type != DeviceType_HardDisk)
660 continue;
661
662 /* Valid medium attached? */
663 ComPtr<IMedium> pSrcMedium;
664 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam());
665 if (FAILED(rc)) throw rc;
666 if (pSrcMedium.isNull())
667 continue;
668
669// ComObjPtr<Medium> pMedium = static_cast<Medium*>((IMedium*)pSrcMedium);
670// ComObjPtr<Medium> diff;
671// diff.createObject();
672 // store this diff in the same registry as the parent
673// Guid uuidRegistryParent;
674// if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
675// {
676 // parent image has no registry: this can happen if we're attaching a new immutable
677 // image that has not yet been attached (medium then points to the base and we're
678 // creating the diff image for the immutable, and the parent is not yet registered);
679 // put the parent in the machine registry then
680// addMediumToRegistry(medium, llRegistriesThatNeedSaving, &uuidRegistryParent);
681// }
682// rc = diff->init(mParent,
683// pMedium->getPreferredDiffFormat(),
684// strFullSnapshotFolder.append(RTPATH_SLASH_STR),
685// uuidRegistryParent,
686// pllRegistriesThatNeedSaving);
687// if (FAILED(rc)) throw rc;
688//
689// rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
690// pMediumLockList,
691// NULL /* aProgress */,
692// true /* aWait */,
693// pllRegistriesThatNeedSaving);
694 }
695 }
696
697 {
698 rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Create Machine Clone '%s' ..."), trgMCF.machineUserData.strName.c_str()).raw(), 1);
699 if (FAILED(rc)) throw rc;
700 /* After modifying the new machine config, we can copy the stuff
701 * over to the new machine. The machine have to be mutable for
702 * this. */
703 rc = d->pTrgMachine->checkStateDependency(p->MutableStateDep);
704 if (FAILED(rc)) throw rc;
705 rc = d->pTrgMachine->loadMachineDataFromSettings(trgMCF,
706 &d->pTrgMachine->mData->mUuid);
707 if (FAILED(rc)) throw rc;
708 }
709
710 /* The medias are created before the machine was there. We have to make
711 * sure the new medias know of there new parent or we get in trouble
712 * when the media registry is saved for this VM, especially in case of
713 * difference image chain's. See VirtualBox::saveMediaRegistry.*/
714// for (size_t i = 0; i < newBaseMedias.size(); ++i)
715// {
716// rc = newBaseMedias.at(i)->addRegistry(d->pTrgMachine->mData->mUuid, true /* fRecursive */);
717// if (FAILED(rc)) throw rc;
718// }
719
720 /* Now save the new configuration to disk. */
721 rc = d->pTrgMachine->SaveSettings();
722 if (FAILED(rc)) throw rc;
723 }
724 catch(HRESULT rc2)
725 {
726 rc = rc2;
727 }
728 catch (...)
729 {
730 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
731 }
732
733 /* Cleanup on failure (CANCEL also) */
734 if (FAILED(rc))
735 {
736 int vrc = VINF_SUCCESS;
737 /* Delete all created files. */
738 for (size_t i = 0; i < newFiles.size(); ++i)
739 {
740 vrc = RTFileDelete(newFiles.at(i).c_str());
741 if (RT_FAILURE(vrc))
742 rc = p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not delete file '%s' (%Rrc)"), newFiles.at(i).c_str(), vrc);
743 }
744 /* Delete all already created medias. (Reverse, cause there could be
745 * parent->child relations.) */
746 for (size_t i = newMedias.size(); i > 0; --i)
747 {
748 bool fFile = false;
749 Utf8Str strLoc;
750 ComObjPtr<Medium> &pMedium = newMedias.at(i - 1);
751 {
752 AutoCaller mac(pMedium);
753 if (FAILED(mac.rc())) { continue; rc = mac.rc(); }
754 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
755 fFile = pMedium->isMediumFormatFile();
756 strLoc = pMedium->getLocationFull();
757 }
758 if (fFile)
759 {
760 vrc = RTFileDelete(strLoc.c_str());
761 if (RT_FAILURE(vrc))
762 rc = p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not delete file '%s' (%Rrc)"), strLoc.c_str(), vrc);
763 }
764 }
765 /* Delete the snapshot folder when not empty. */
766 if (!strTrgSnapshotFolder.isEmpty())
767 RTDirRemove(strTrgSnapshotFolder.c_str());
768 /* Delete the machine folder when not empty. */
769 RTDirRemove(strTrgMachineFolder.c_str());
770 }
771
772 return rc;
773}
774
775void MachineCloneVM::destroy()
776{
777 delete this;
778}
779
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