VirtualBox

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

Last change on this file since 37502 was 37502, checked in by vboxsync, 14 years ago

Main;Settings: make the settings copyable

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.4 KB
Line 
1/* $Id: MachineImplCloneVM.cpp 37502 2011-06-16 15:20:46Z 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.215389.xyz. 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 */
409
410 /* Where should all the media go? */
411 Utf8Str strTrgMachineFolder = d->pTrgMachine->getSettingsFileFull();
412 strTrgMachineFolder.stripFilename();
413
414 RTCList< ComObjPtr<Medium> > newMedias; /* All created images */
415 RTCList<Utf8Str> newFiles; /* All extra created files (save states, ...) */
416 try
417 {
418 /* Copy all the configuration from this machine to an empty
419 * configuration dataset. */
420 settings::MachineConfigFile trgMCF = *d->pSrcMachine->mData->pMachineConfigFile;
421
422 /* Reset media registry. */
423 trgMCF.mediaRegistry.llHardDisks.clear();
424 /* If we got a valid snapshot id, replace the hardware/storage section
425 * with the stuff from the snapshot. */
426 settings::Snapshot sn;
427 if (!d->snapshotId.isEmpty())
428 sn = d->cloneFindSnapshot(&trgMCF, trgMCF.llFirstSnapshot, d->snapshotId);
429
430 if (d->mode == CloneMode_MachineState)
431 {
432 if (!sn.uuid.isEmpty())
433 {
434 trgMCF.hardwareMachine = sn.hardware;
435 trgMCF.storageMachine = sn.storage;
436 }
437
438 /* Remove any hint on snapshots. */
439 trgMCF.llFirstSnapshot.clear();
440 trgMCF.uuidCurrentSnapshot.clear();
441 }else
442 if ( d->mode == CloneMode_MachineAndChildStates
443 && !sn.uuid.isEmpty())
444 {
445 /* Copy the snapshot data to the current machine. */
446 trgMCF.hardwareMachine = sn.hardware;
447 trgMCF.storageMachine = sn.storage;
448
449 /* The snapshot will be the root one. */
450 trgMCF.uuidCurrentSnapshot = sn.uuid;
451 trgMCF.llFirstSnapshot.clear();
452 trgMCF.llFirstSnapshot.push_back(sn);
453 }
454
455 /* When the current snapshot folder is absolute we reset it to the
456 * default relative folder. */
457 if (RTPathStartsWithRoot(trgMCF.machineUserData.strSnapshotFolder.c_str()))
458 trgMCF.machineUserData.strSnapshotFolder = "Snapshots";
459 trgMCF.strStateFile = "";
460 /* Force writing of setting file. */
461 trgMCF.fCurrentStateModified = true;
462 /* Set the new name. */
463 trgMCF.machineUserData.strName = d->pTrgMachine->mUserData->s.strName;
464 trgMCF.uuid = d->pTrgMachine->mData->mUuid;
465
466 Bstr bstrSrcSnapshotFolder;
467 rc = d->pSrcMachine->COMGETTER(SnapshotFolder)(bstrSrcSnapshotFolder.asOutParam());
468 if (FAILED(rc)) throw rc;
469 /* The absolute name of the snapshot folder. */
470 Utf8Str strTrgSnapshotFolder = Utf8StrFmt("%s%c%s%c", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, trgMCF.machineUserData.strSnapshotFolder.c_str(), RTPATH_DELIMITER);
471
472 /* We need to create a map with the already created medias. This is
473 * necessary, cause different snapshots could have the same
474 * parents/parent chain. If a medium is in this map already, it isn't
475 * cloned a second time, but simply used. */
476 typedef std::map<Utf8Str, ComObjPtr<Medium> > TStrMediumMap;
477 typedef std::pair<Utf8Str, ComObjPtr<Medium> > TStrMediumPair;
478 TStrMediumMap map;
479 for (size_t i = 0; i < d->llMedias.size(); ++i)
480 {
481 const MEDIUMTASKCHAIN &mtc = d->llMedias.at(i);
482 ComObjPtr<Medium> pNewParent;
483 for (size_t a = mtc.chain.size(); a > 0; --a)
484 {
485 const MEDIUMTASK &mt = mtc.chain.at(a - 1);
486 ComPtr<IMedium> pMedium = mt.pMedium;
487
488 Bstr bstrSrcName;
489 rc = pMedium->COMGETTER(Name)(bstrSrcName.asOutParam());
490 if (FAILED(rc)) throw rc;
491
492 rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Cloning Disk '%ls' ..."), bstrSrcName.raw()).raw(), mt.uSize);
493 if (FAILED(rc)) throw rc;
494
495 Bstr bstrSrcId;
496 rc = pMedium->COMGETTER(Id)(bstrSrcId.asOutParam());
497 if (FAILED(rc)) throw rc;
498
499 /* Is a clone already there? */
500 TStrMediumMap::iterator it = map.find(Utf8Str(bstrSrcId));
501 if (it != map.end())
502 pNewParent = it->second;
503 else
504 {
505 ComPtr<IMediumFormat> pSrcFormat;
506 rc = pMedium->COMGETTER(MediumFormat)(pSrcFormat.asOutParam());
507 ULONG uSrcCaps = 0;
508 rc = pSrcFormat->COMGETTER(Capabilities)(&uSrcCaps);
509 if (FAILED(rc)) throw rc;
510
511 Bstr bstrSrcFormat = "VDI";
512 ULONG srcVar = MediumVariant_Standard;
513 /* Is the source file based? */
514 if ((uSrcCaps & MediumFormatCapabilities_File) == MediumFormatCapabilities_File)
515 {
516 /* Yes, just use the source format. Otherwise the defaults
517 * will be used. */
518 rc = pMedium->COMGETTER(Format)(bstrSrcFormat.asOutParam());
519 if (FAILED(rc)) throw rc;
520 rc = pMedium->COMGETTER(Variant)(&srcVar);
521 if (FAILED(rc)) throw rc;
522 }
523
524 /* Start creating the clone. */
525 ComObjPtr<Medium> pTarget;
526 rc = pTarget.createObject();
527 if (FAILED(rc)) throw rc;
528
529 Utf8Str strFile = Utf8StrFmt("%s%c%lS", strTrgMachineFolder.c_str(), RTPATH_DELIMITER, bstrSrcName.raw());
530 rc = pTarget->init(p->mParent,
531 Utf8Str(bstrSrcFormat),
532 strFile,
533 d->pTrgMachine->mData->mUuid, /* media registry */
534 NULL /* llRegistriesThatNeedSaving */);
535 if (FAILED(rc)) throw rc;
536
537 /* Do the disk cloning. */
538 ComPtr<IProgress> progress2;
539 rc = pMedium->CloneTo(pTarget,
540 srcVar,
541 pNewParent,
542 progress2.asOutParam());
543 if (FAILED(rc)) throw rc;
544
545 /* Wait until the asynchrony process has finished. */
546 srcLock.release();
547 rc = d->pProgress->WaitForAsyncProgressCompletion(progress2);
548 srcLock.acquire();
549 if (FAILED(rc)) throw rc;
550
551 /* Check the result of the asynchrony process. */
552 LONG iRc;
553 rc = progress2->COMGETTER(ResultCode)(&iRc);
554 if (FAILED(rc)) throw rc;
555 if (FAILED(iRc))
556 {
557 /* If the thread of the progress object has an error, then
558 * retrieve the error info from there, or it'll be lost. */
559 ProgressErrorInfo info(progress2);
560 throw p->setError(iRc, Utf8Str(info.getText()).c_str());
561 }
562
563 map.insert(TStrMediumPair(Utf8Str(bstrSrcId), pTarget));
564
565 /* Remember created medias. */
566 newMedias.append(pTarget);
567 /* This medium becomes the parent of the next medium in the
568 * chain. */
569 pNewParent = pTarget;
570 }
571 }
572
573 /* Create diffs for the last image chain. */
574 if (mtc.fCreateDiffs)
575 {
576 Bstr bstrSrcId;
577 rc = pNewParent->COMGETTER(Id)(bstrSrcId.asOutParam());
578 if (FAILED(rc)) throw rc;
579 GuidList *pllRegistriesThatNeedSaving;
580 ComObjPtr<Medium> diff;
581 diff.createObject();
582 rc = diff->init(p->mParent,
583 pNewParent->getPreferredDiffFormat(),
584 strTrgSnapshotFolder,
585 d->pTrgMachine->mData->mUuid,
586 NULL); // pllRegistriesThatNeedSaving
587 if (FAILED(rc)) throw rc;
588 MediumLockList *pMediumLockList(new MediumLockList()); /* todo: deleteeeeeeeee */
589 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
590 true /* fMediumLockWrite */,
591 pNewParent,
592 *pMediumLockList);
593 if (FAILED(rc)) throw rc;
594 rc = pMediumLockList->Lock();
595 if (FAILED(rc)) throw rc;
596 rc = pNewParent->createDiffStorage(diff, MediumVariant_Standard,
597 pMediumLockList,
598 NULL /* aProgress */,
599 true /* aWait */,
600 NULL); // pllRegistriesThatNeedSaving
601 delete pMediumLockList;
602 if (FAILED(rc)) throw rc;
603 pNewParent = diff;
604 }
605 Bstr bstrSrcId;
606 rc = mtc.chain.first().pMedium->COMGETTER(Id)(bstrSrcId.asOutParam());
607 if (FAILED(rc)) throw rc;
608 Bstr bstrTrgId;
609 rc = pNewParent->COMGETTER(Id)(bstrTrgId.asOutParam());
610 if (FAILED(rc)) throw rc;
611 /* We have to patch the configuration, so it contains the new
612 * medium uuid instead of the old one. */
613 d->cloneUpdateStorageLists(trgMCF.storageMachine.llStorageControllers, bstrSrcId, bstrTrgId);
614 d->cloneUpdateSnapshotStorageLists(trgMCF.llFirstSnapshot, bstrSrcId, bstrTrgId);
615 }
616 /* Clone all save state files. */
617 for (size_t i = 0; i < d->llSaveStateFiles.size(); ++i)
618 {
619 SAVESTATETASK sst = d->llSaveStateFiles.at(i);
620 const Utf8Str &strTrgSaveState = Utf8StrFmt("%s%s", strTrgSnapshotFolder.c_str(), RTPathFilename(sst.strSaveStateFile.c_str()));
621
622 /* Move to next sub-operation. */
623 rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Copy save state file '%s' ..."), RTPathFilename(sst.strSaveStateFile.c_str())).raw(), sst.cbSize);
624 if (FAILED(rc)) throw rc;
625 /* Copy the file only if it was not copied already. */
626 if (!newFiles.contains(strTrgSaveState.c_str()))
627 {
628 int vrc = RTFileCopyEx(sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), 0, MachineCloneVMPrivate::cloneCopyStateFileProgress, &d->pProgress);
629 if (RT_FAILURE(vrc))
630 throw p->setError(VBOX_E_IPRT_ERROR,
631 p->tr("Could not copy state file '%s' to '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), vrc);
632 newFiles.append(strTrgSaveState);
633 }
634 /* Update the path in the configuration either for the current
635 * machine state or the snapshots. */
636 if (sst.snapshotUuid.isEmpty())
637 trgMCF.strStateFile = strTrgSaveState;
638 else
639 d->cloneUpdateStateFile(trgMCF.llFirstSnapshot, sst.snapshotUuid, strTrgSaveState);
640 }
641
642 if (false)
643// if (!d->pOldMachineState.isNull())
644 {
645 SafeIfaceArray<IMediumAttachment> sfaAttachments;
646 rc = d->pOldMachineState->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
647 if (FAILED(rc)) throw rc;
648 for (size_t a = 0; a < sfaAttachments.size(); ++a)
649 {
650 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a];
651 DeviceType_T type;
652 rc = pAtt->COMGETTER(Type)(&type);
653 if (FAILED(rc)) throw rc;
654
655 /* Only harddisk's are of interest. */
656 if (type != DeviceType_HardDisk)
657 continue;
658
659 /* Valid medium attached? */
660 ComPtr<IMedium> pSrcMedium;
661 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam());
662 if (FAILED(rc)) throw rc;
663 if (pSrcMedium.isNull())
664 continue;
665
666// ComObjPtr<Medium> pMedium = static_cast<Medium*>((IMedium*)pSrcMedium);
667// ComObjPtr<Medium> diff;
668// diff.createObject();
669 // store this diff in the same registry as the parent
670// Guid uuidRegistryParent;
671// if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
672// {
673 // parent image has no registry: this can happen if we're attaching a new immutable
674 // image that has not yet been attached (medium then points to the base and we're
675 // creating the diff image for the immutable, and the parent is not yet registered);
676 // put the parent in the machine registry then
677// addMediumToRegistry(medium, llRegistriesThatNeedSaving, &uuidRegistryParent);
678// }
679// rc = diff->init(mParent,
680// pMedium->getPreferredDiffFormat(),
681// strFullSnapshotFolder.append(RTPATH_SLASH_STR),
682// uuidRegistryParent,
683// pllRegistriesThatNeedSaving);
684// if (FAILED(rc)) throw rc;
685//
686// rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
687// pMediumLockList,
688// NULL /* aProgress */,
689// true /* aWait */,
690// pllRegistriesThatNeedSaving);
691 }
692 }
693
694 {
695 rc = d->pProgress->SetNextOperation(BstrFmt(p->tr("Create Machine Clone '%s' ..."), trgMCF.machineUserData.strName.c_str()).raw(), 1);
696 if (FAILED(rc)) throw rc;
697 /* After modifying the new machine config, we can copy the stuff
698 * over to the new machine. The machine have to be mutable for
699 * this. */
700 rc = d->pTrgMachine->checkStateDependency(p->MutableStateDep);
701 if (FAILED(rc)) throw rc;
702 rc = d->pTrgMachine->loadMachineDataFromSettings(trgMCF,
703 &d->pTrgMachine->mData->mUuid);
704 if (FAILED(rc)) throw rc;
705 }
706
707 /* The medias are created before the machine was there. We have to make
708 * sure the new medias know of there new parent or we get in trouble
709 * when the media registry is saved for this VM, especially in case of
710 * difference image chain's. See VirtualBox::saveMediaRegistry.*/
711// for (size_t i = 0; i < newBaseMedias.size(); ++i)
712// {
713// rc = newBaseMedias.at(i)->addRegistry(d->pTrgMachine->mData->mUuid, true /* fRecursive */);
714// if (FAILED(rc)) throw rc;
715// }
716
717 /* Now save the new configuration to disk. */
718 rc = d->pTrgMachine->SaveSettings();
719 if (FAILED(rc)) throw rc;
720 }
721 catch(HRESULT rc2)
722 {
723 rc = rc2;
724 }
725 catch (...)
726 {
727 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
728 }
729
730 /* Cleanup on failure (CANCEL also) */
731 if (FAILED(rc))
732 {
733 int vrc = VINF_SUCCESS;
734 /* Delete all created files. */
735 for (size_t i = 0; i < newFiles.size(); ++i)
736 {
737 vrc = RTFileDelete(newFiles.at(i).c_str());
738 if (RT_FAILURE(vrc))
739 rc = p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not delete file '%s' (%Rrc)"), newFiles.at(i).c_str(), vrc);
740 }
741 /* Delete all already created medias. (Reverse, cause there could be
742 * parent->child relations.) */
743 for (size_t i = newMedias.size(); i > 0; --i)
744 {
745 ComObjPtr<Medium> &pMedium = newMedias.at(i - 1);
746 AutoCaller mac(pMedium);
747 if (FAILED(mac.rc())) { continue; rc = mac.rc(); }
748 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
749 bool fFile = pMedium->isMediumFormatFile();
750 Utf8Str strLoc = pMedium->getLocationFull();
751 mlock.release();
752 /* Close the medium. If this succeed, delete it finally from the
753 * disk. */
754 rc = pMedium->close(NULL, mac);
755 if (FAILED(rc)) continue;
756 if (fFile)
757 {
758 vrc = RTFileDelete(strLoc.c_str());
759 if (RT_FAILURE(vrc))
760 rc = p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not delete file '%s' (%Rrc)"), strLoc.c_str(), vrc);
761 }
762 }
763 /* Delete the machine folder when not empty. */
764 RTDirRemove(strTrgMachineFolder.c_str());
765 }
766
767 return rc;
768}
769
770void MachineCloneVM::destroy()
771{
772 delete this;
773}
774
Note: See TracBrowser for help on using the repository browser.

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