VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MachineImplMoveVM.cpp@ 70674

最後變更 在這個檔案從70674是 70674,由 vboxsync 提交於 7 年 前

bugref:8345. Added a Progress object for rollback action. is it correct or not? I am not sure.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 49.4 KB
 
1/* $Id: MachineImplMoveVM.cpp 70674 2018-01-22 12:00:30Z vboxsync $ */
2/** @file
3 * Implementation of MachineMoveVM
4 */
5
6/*
7 * Copyright (C) 2011-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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#include <iprt/fs.h>
18#include <iprt/dir.h>
19#include <iprt/file.h>
20#include <iprt/path.h>
21#include <iprt/cpp/utils.h>
22#include <iprt/stream.h>
23
24#include "MachineImplMoveVM.h"
25#include "VirtualBoxImpl.h"
26#include "Logging.h"
27
28typedef std::multimap<Utf8Str, Utf8Str> list_t;
29typedef std::multimap<Utf8Str, Utf8Str>::const_iterator cit_t;
30typedef std::multimap<Utf8Str, Utf8Str>::iterator it_t;
31typedef std::pair <std::multimap<Utf8Str, Utf8Str>::iterator, std::multimap<Utf8Str, Utf8Str>::iterator> rangeRes_t;
32
33struct fileList_t
34{
35 HRESULT add(const Utf8Str& folder, const Utf8Str& file)
36 {
37 HRESULT rc = S_OK;
38 m_list.insert(std::make_pair(folder, file));
39 return rc;
40 }
41
42 HRESULT add(const Utf8Str& fullPath)
43 {
44 HRESULT rc = S_OK;
45 Utf8Str folder = fullPath;
46 folder.stripFilename();
47 Utf8Str filename = fullPath;
48 filename.stripPath();
49 m_list.insert(std::make_pair(folder, filename));
50 return rc;
51 }
52
53 HRESULT removeFileFromList(const Utf8Str& fullPath)
54 {
55 HRESULT rc = S_OK;
56 Utf8Str folder = fullPath;
57 folder.stripFilename();
58 Utf8Str filename = fullPath;
59 filename.stripPath();
60 rangeRes_t res = m_list.equal_range(folder);
61 for (it_t it=res.first; it!=res.second; ++it)
62 {
63 if (it->second.equals(filename))
64 m_list.erase(it);
65 }
66 return rc;
67 }
68
69 HRESULT removeFileFromList(const Utf8Str& path, const Utf8Str& fileName)
70 {
71 HRESULT rc = S_OK;
72 rangeRes_t res = m_list.equal_range(path);
73 for (it_t it=res.first; it!=res.second; ++it)
74 {
75 if (it->second.equals(fileName))
76 m_list.erase(it);
77 }
78 return rc;
79 }
80
81 HRESULT removeFolderFromList(const Utf8Str& path)
82 {
83 HRESULT rc = S_OK;
84 m_list.erase(path);
85 return rc;
86 }
87
88 rangeRes_t getFilesInRange(const Utf8Str& path)
89 {
90 rangeRes_t res;
91 res = m_list.equal_range(path);
92 return res;
93 }
94
95 std::list<Utf8Str> getFilesInList(const Utf8Str& path)
96 {
97 std::list<Utf8Str> list_;
98 rangeRes_t res = m_list.equal_range(path);
99 for (it_t it=res.first; it!=res.second; ++it)
100 list_.push_back(it->second);
101 return list_;
102 }
103
104
105 list_t m_list;
106
107};
108
109HRESULT MachineMoveVM::init()
110{
111 HRESULT rc = S_OK;
112// ComObjPtr<Machine> &machine = m_pMachine;
113 Utf8Str strTargetFolder = m_targetPath;
114
115 /*
116 * We have a mode which user is able to request
117 * basic mode:
118 * - The images which are solely attached to the VM
119 * and located in the original VM folder will be moved.
120 *
121 * Comment: in the future some other modes can be added.
122 */
123
124 try
125 {
126 Utf8Str info;
127 int vrc = 0;
128
129 RTFOFF cbTotal = 0;
130 RTFOFF cbFree = 0;
131 uint32_t cbBlock = 0;
132 uint32_t cbSector = 0;
133
134 vrc = RTFsQuerySizes(strTargetFolder.c_str(), &cbTotal, &cbFree, &cbBlock, &cbSector);
135 if (FAILED(vrc)) throw vrc;
136 long long totalFreeSpace = cbFree;
137 long long totalSpace = cbTotal;
138 info = Utf8StrFmt("blocks: total %lld, free %u ", cbTotal, cbFree);
139 RTPrintf("%s \n", info.c_str());
140 RTPrintf("total space (Kb) %lld (Mb) %lld (Gb) %lld\n",
141 totalSpace/1024, totalSpace/(1024*1024), totalSpace/(1024*1024*1024));
142 RTPrintf("total free space (Kb) %lld (Mb) %lld (Gb) %lld\n",
143 totalFreeSpace/1024, totalFreeSpace/(1024*1024), totalFreeSpace/(1024*1024*1024));
144
145 RTFSPROPERTIES properties;
146 vrc = RTFsQueryProperties(strTargetFolder.c_str(), &properties);
147 if (FAILED(vrc)) throw vrc;
148 info = Utf8StrFmt("disk properties:\n"
149 "remote: %s \n"
150 "read only: %s \n"
151 "compressed: %s \n",
152 properties.fRemote == true ? "true":"false",
153 properties.fReadOnly == true ? "true":"false",
154 properties.fCompressed == true ? "true":"false");
155
156 RTPrintf("%s \n", info.c_str());
157
158 /* Get the original VM path */
159 Utf8Str strSettingsFilePath;
160 Bstr bstr_settingsFilePath;
161 m_pMachine->COMGETTER(SettingsFilePath)(bstr_settingsFilePath.asOutParam());
162 strSettingsFilePath = bstr_settingsFilePath;
163 strSettingsFilePath.stripFilename();
164
165 vmFolders.insert(std::make_pair(VBox_SettingFolder, strSettingsFilePath));
166
167 /* Collect all files from the VM's folder */
168 fileList_t fullFileList;
169 rc = getFilesList(strSettingsFilePath, fullFileList);
170 if (FAILED(rc)) throw rc;
171
172// {
173// cit_t it = fullFileList.m_list.begin();
174// while(it != fullFileList.m_list.end())
175// {
176// Utf8Str strFile = Utf8StrFmt("folder: %s, file %s", it->first.c_str(), it->second.c_str());
177// RTPrintf("%s\n", strFile.c_str());
178// ++it;
179// }
180// }
181
182 /*
183 * Collect all known folders used by the VM:
184 * - log folder;
185 * - state folder;
186 * - snapshot folder.
187 */
188 Utf8Str strLogFolder;
189 Bstr bstr_logFolder;
190 m_pMachine->COMGETTER(LogFolder)(bstr_logFolder.asOutParam());
191 strLogFolder = bstr_logFolder;
192 if ( m_type.equals("full")
193 && strLogFolder.contains(strSettingsFilePath))
194 {
195 vmFolders.insert(std::make_pair(VBox_LogFolder, strLogFolder));
196 }
197
198 Utf8Str strStateFilePath;
199 Bstr bstr_stateFilePath;
200 MachineState_T machineState;
201 rc = m_pMachine->COMGETTER(State)(&machineState);
202 if (FAILED(rc)) throw rc;
203 else if (machineState == MachineState_Saved)
204 {
205 m_pMachine->COMGETTER(StateFilePath)(bstr_stateFilePath.asOutParam());
206 strStateFilePath = bstr_stateFilePath;
207 strStateFilePath.stripFilename();
208 if ( m_type.equals("full")
209 && strStateFilePath.contains(strSettingsFilePath))
210 vmFolders.insert(std::make_pair(VBox_StateFolder, strStateFilePath));//Utf8Str(bstr_stateFilePath)));
211 }
212
213 Utf8Str strSnapshotFolder;
214 Bstr bstr_snapshotFolder;
215 m_pMachine->COMGETTER(SnapshotFolder)(bstr_snapshotFolder.asOutParam());
216 strSnapshotFolder = bstr_snapshotFolder;
217 if ( m_type.equals("full")
218 && strSnapshotFolder.contains(strSettingsFilePath))
219 vmFolders.insert(std::make_pair(VBox_SnapshotFolder, strSnapshotFolder));
220
221 if (m_pMachine->i_isSnapshotMachine())
222 {
223 Bstr bstrSrcMachineId;
224 rc = m_pMachine->COMGETTER(Id)(bstrSrcMachineId.asOutParam());
225 if (FAILED(rc)) throw rc;
226 ComPtr<IMachine> newSrcMachine;
227 rc = m_pMachine->i_getVirtualBox()->FindMachine(bstrSrcMachineId.raw(), newSrcMachine.asOutParam());
228 if (FAILED(rc)) throw rc;
229 }
230
231 /* Add the current machine and all snapshot machines below this machine
232 * in a list for further processing.
233 */
234
235 long long neededFreeSpace = 0;
236
237 /* Actual file list */
238 fileList_t actualFileList;
239 Utf8Str strTargetImageName;
240 std::vector< ComObjPtr<Machine> > machineList;
241
242// if (m_pMachine->i_isSnapshotMachine())
243// Guid snapshotId = m_pMachine->i_getSnapshotId();
244
245 /* Include current state? */
246 machineList.push_back(m_pMachine);
247
248 {
249 ULONG cSnapshots = 0;
250 rc = m_pMachine->COMGETTER(SnapshotCount)(&cSnapshots);
251 if (FAILED(rc)) throw rc;
252 if (cSnapshots > 0)
253 {
254 Utf8Str id;
255 if (m_pMachine->i_isSnapshotMachine())
256 id = m_pMachine->i_getSnapshotId().toString();
257 ComPtr<ISnapshot> pSnapshot;
258 rc = m_pMachine->FindSnapshot(Bstr(id).raw(), pSnapshot.asOutParam());
259 if (FAILED(rc)) throw rc;
260 rc = createMachineList(pSnapshot, machineList);
261 if (FAILED(rc)) throw rc;
262 }
263 }
264
265 ULONG uCount = 2; /* One init task and the machine creation. */
266 ULONG uTotalWeight = 2; /* The init task and the machine creation is worth one. */
267
268 /* here we fill the lists llMedias and llSaveStateFiles */
269 queryMediasForAllStates(machineList, uCount, uTotalWeight);
270
271 {
272 uint64_t totalMediumsSize = 0;
273
274 for (size_t i = 0; i < llMedias.size(); ++i)
275 {
276 LONG64 cbSize = 0;
277 MEDIUMTASKCHAIN &mtc = llMedias.at(i);
278 for (size_t a = mtc.chain.size(); a > 0; --a)
279 {
280 Bstr bstrLocation;
281 Utf8Str strLocation;
282 Utf8Str name = mtc.chain[a - 1].strBaseName;
283 ComPtr<IMedium> plMedium = mtc.chain[a - 1].pMedium;
284 rc = plMedium->COMGETTER(Location)(bstrLocation.asOutParam());
285 if (FAILED(rc)) throw rc;
286 strLocation = bstrLocation;
287
288 /*if an image is located in the actual VM folder it will be added to the actual list */
289 if (strLocation.contains(strSettingsFilePath))
290 {
291 rc = plMedium->COMGETTER(Size)(&cbSize);
292 if (FAILED(rc)) throw rc;
293
294 totalMediumsSize += cbSize;
295
296 std::pair<std::map<Utf8Str, MEDIUMTASK>::iterator,bool> ret;
297 ret = finalMediumsMap.insert(std::make_pair(name, mtc.chain[a - 1]));
298 if (ret.second == true)
299 RTPrintf("Image %s was added into the moved list\n", name.c_str());
300 }
301 }
302 }
303 neededFreeSpace += totalMediumsSize;
304 }
305
306 {
307 uint64_t totalStateSize = 0;
308
309 for (size_t i = 0; i < llSaveStateFiles.size(); ++i)
310 {
311 uint64_t cbFile = 0;
312 SAVESTATETASK &sst = llSaveStateFiles.at(i);
313
314 Utf8Str name = sst.strSaveStateFile;
315 /*if a state file is located in the actual VM folder it will be added to the actual list */
316 if (name.contains(strSettingsFilePath))
317 {
318 vrc = RTFileQuerySize(name.c_str(), &cbFile);
319 if (RT_SUCCESS(vrc))
320 {
321 totalStateSize += cbFile;
322 }
323
324 std::pair<std::map<Utf8Str, SAVESTATETASK>::iterator,bool> ret;
325 ret = finalSaveStateFilesMap.insert(std::make_pair(name, sst));
326 if (ret.second == true)
327 {
328 RTPrintf("State file %s was added into the moved list\n", name.c_str());
329 }
330 }
331 }
332 neededFreeSpace += totalStateSize;
333 }
334
335 /*
336 * Move log folder
337 */
338 {
339 Utf8Str strFolder = vmFolders[VBox_LogFolder];
340 if (strFolder.isNotEmpty())
341 {
342 uint64_t totalLogSize = 0;
343 rc = getFolderSize(strFolder, totalLogSize);
344 if (SUCCEEDED(rc))
345 {
346// RTPrintf("total files size in the log folder is %llu\n", totalLogSize);
347
348 neededFreeSpace += totalLogSize;
349 if (totalFreeSpace - neededFreeSpace <= 1024*1024)
350 {
351 throw VERR_OUT_OF_RESOURCES;//less than 1Mb free space on the target location
352 }
353
354 fileList_t filesList;
355 getFilesList(strFolder, filesList);
356 cit_t it = filesList.m_list.begin();
357 while(it != filesList.m_list.end())
358 {
359 Utf8Str strFile = it->first.c_str();
360 strFile.append(RTPATH_DELIMITER).append(it->second.c_str());
361 RTPrintf("%s\n", strFile.c_str());
362 actualFileList.add(strFile);
363
364 RTPrintf("The log file %s added into the moved list\n", strFile.c_str());
365 ++it;
366 }
367 }
368 }
369 }
370
371 /* Check a target location on enough room */
372 if (totalFreeSpace - neededFreeSpace <= 1024*1024)
373 {
374 throw VERR_OUT_OF_RESOURCES;//less than 1Mb free space on the target location
375 }
376
377 /* Init both Progress instances */
378 {
379 /** weak VirtualBox parent */
380 VirtualBox* const pVirtualBox = NULL;
381 unconst(pVirtualBox) = m_pMachine->i_getVirtualBox();
382 rc = m_pProgress->init(pVirtualBox,
383 static_cast<IMachine*>(m_pMachine) /* aInitiator */,
384 Bstr(m_pMachine->tr("Moving Machine")).raw(),
385 true /* fCancellable */,
386 uCount,
387 uTotalWeight,
388 Bstr(m_pMachine->tr("Initialize Moving")).raw(),
389 1);
390 if (FAILED(rc))
391 {
392 throw m_pMachine->setError(VBOX_E_IPRT_ERROR,
393 m_pMachine->tr("Couldn't correctly setup the progress object "
394 "for moving VM operation (%Rrc)"),
395 rc);
396 }
397
398 m_pRollBackProgress.createObject();
399 rc = m_pRollBackProgress->init(pVirtualBox,
400 static_cast<IMachine*>(m_pMachine) /* aInitiator */,
401 Bstr(m_pMachine->tr("Moving back Machine")).raw(),
402 true /* fCancellable */,
403 uCount,
404 uTotalWeight,
405 Bstr(m_pMachine->tr("Initialize Moving back")).raw(),
406 1);
407 if (FAILED(rc))
408 {
409 throw m_pMachine->setError(VBOX_E_IPRT_ERROR,
410 m_pMachine->tr("Couldn't correctly setup the progress object "
411 "for possible rollback operation during moving VM (%Rrc)"),
412 rc);
413 }
414 }
415
416 /* save all VM data */
417 m_pMachine->i_setModified(Machine::IsModified_MachineData);
418 rc = m_pMachine->SaveSettings();
419 }
420 catch(HRESULT hrc)
421 {
422 rc = hrc;
423 }
424
425 LogFlowFuncLeave();
426
427 return rc;
428}
429
430void MachineMoveVM::updateStateFile(settings::MachineConfigFile *snl, const Guid &id, const Utf8Str &strFile)
431{
432 settings::SnapshotsList::iterator it;
433 for (it = (*snl).llFirstSnapshot.begin(); it != (*snl).llFirstSnapshot.end(); ++it)
434 {
435 settings::Snapshot snap = (settings::Snapshot)(*it);
436 RTPrintf("it->uuid = %s , id = %s\n", snap.uuid.toStringCurly().c_str(), id.toStringCurly().c_str());
437 RTPrintf("it->strStateFile = %s , strFile = %s\n", it->strStateFile.c_str(), strFile.c_str());
438 if (it->uuid == id)
439 {
440 it->strStateFile = strFile;
441 RTPrintf("Modified it->strStateFile = %s\n", it->strStateFile.c_str());
442 }
443 else if (!it->llChildSnapshots.empty())
444 updateStateFile(it->llChildSnapshots, id, strFile);
445 }
446}
447
448void MachineMoveVM::updateStateFile(settings::SnapshotsList &snl, const Guid &id, const Utf8Str &strFile)
449{
450 settings::SnapshotsList::iterator it;
451 for (it = snl.begin(); it != snl.end(); ++it)
452 {
453 if (it->uuid == id)
454 {
455 settings::Snapshot snap = (settings::Snapshot)(*it);
456// RTPrintf("\nSnapshot %s address %#lx\n", snap.strName.c_str(), &(settings::Snapshot)(*it));
457 RTPrintf("it->uuid = %s , id = %s\n", snap.uuid.toStringCurly().c_str(), id.toStringCurly().c_str());
458 RTPrintf("it->strStateFile = %s , strFile = %s\n", snap.strStateFile.c_str(), strFile.c_str());
459 it->strStateFile = strFile;
460
461 RTPrintf("Modified it->strStateFile = %s\n", it->strStateFile.c_str());
462 m_pMachine->mSSData->strStateFilePath = strFile;
463 RTPrintf("Modified m_pMachine->mSSData->strStateFilePath = %s\n",
464 m_pMachine->mSSData->strStateFilePath.c_str());
465 }
466 else if (!it->llChildSnapshots.empty())
467 updateStateFile(it->llChildSnapshots, id, strFile);
468 }
469}
470
471void MachineMoveVM::printStateFile(settings::SnapshotsList &snl)
472{
473 settings::SnapshotsList::iterator it;
474 for (it = snl.begin(); it != snl.end(); ++it)
475 {
476 if (!it->strStateFile.isEmpty())
477 {
478 settings::Snapshot snap = (settings::Snapshot)(*it);
479// RTPrintf("Snapshot %s address %#lx\n", snap.strName.c_str(), &(settings::Snapshot)(*it));
480 RTPrintf("snap.uuid = %s\n", snap.uuid.toStringCurly().c_str());
481 RTPrintf("snap.strStateFile = %s\n", snap.strStateFile.c_str());
482 }
483
484 if (!it->llChildSnapshots.empty())
485 printStateFile(it->llChildSnapshots);
486 }
487}
488
489/* static */
490DECLCALLBACK(int) MachineMoveVM::updateProgress(unsigned uPercent, void *pvUser)
491{
492 MachineMoveVM* pTask = *(MachineMoveVM**)pvUser;
493
494 if ( pTask
495 && !pTask->m_pProgress.isNull())
496 {
497 BOOL fCanceled;
498 pTask->m_pProgress->COMGETTER(Canceled)(&fCanceled);
499 if (fCanceled)
500 return -1;
501 pTask->m_pProgress->SetCurrentOperationProgress(uPercent);
502 }
503 return VINF_SUCCESS;
504}
505
506/* static */
507DECLCALLBACK(int) MachineMoveVM::copyFileProgress(unsigned uPercentage, void *pvUser)
508{
509 ComObjPtr<Progress> pProgress = *static_cast< ComObjPtr<Progress>* >(pvUser);
510
511 BOOL fCanceled = false;
512 HRESULT rc = pProgress->COMGETTER(Canceled)(&fCanceled);
513 if (FAILED(rc)) return VERR_GENERAL_FAILURE;
514 /* If canceled by the user tell it to the copy operation. */
515 if (fCanceled) return VERR_CANCELLED;
516 /* Set the new process. */
517 rc = pProgress->SetCurrentOperationProgress(uPercentage);
518 if (FAILED(rc)) return VERR_GENERAL_FAILURE;
519
520 return VINF_SUCCESS;
521}
522
523
524/* static */
525void MachineMoveVM::i_MoveVMThreadTask(MachineMoveVM* task)
526{
527 LogFlowFuncEnter();
528 HRESULT rc = S_OK;
529
530 MachineMoveVM* taskMoveVM = task;
531 ComObjPtr<Machine> &machine = taskMoveVM->m_pMachine;
532
533 AutoCaller autoCaller(machine);
534// if (FAILED(autoCaller.rc())) return;//Should we return something here?
535
536 Utf8Str strTargetFolder = taskMoveVM->m_targetPath;
537 {
538 Bstr bstrMachineName;
539 taskMoveVM->m_pMachine->COMGETTER(Name)(bstrMachineName.asOutParam());
540 strTargetFolder.append(Utf8Str(bstrMachineName));
541 }
542
543 RTCList<Utf8Str> newFiles; /* All extra created files (save states, ...) */
544 RTCList<Utf8Str> originalFiles; /* All original files except images */
545 typedef std::map<Utf8Str, ComObjPtr<Medium> > MediumMap;
546// typedef std::pair<Utf8Str, ComObjPtr<Medium> > MediumPair;
547 MediumMap mapOriginalMedium;
548
549 /*
550 * We have the couple modes which user is able to request
551 * basic mode:
552 * - The images which are solely attached to the VM
553 * and located in the original VM folder will be moved.
554 *
555 * full mode:
556 * - All disks which are directly attached to the VM
557 * will be moved.
558 *
559 * Apart from that all other files located in the original VM
560 * folder will be moved.
561 */
562 /* Collect the shareable disks.
563 * Get the machines whom the shareable disks attach to.
564 * Return an error if the state of any VM doesn't allow to move a shareable disk.
565 * Check new destination whether enough room for the VM or not. if "not" return an error.
566 * Make a copy of VM settings and a list with all files which are moved. Save the list on the disk.
567 * Start "move" operation.
568 * Check the result of operation.
569 * if the operation was successful:
570 * - delete all files in the original VM folder;
571 * - update VM disks info with new location;
572 * - update all other VM if it's needed;
573 * - update global settings
574 */
575
576 try
577 {
578 /* Move all disks */
579 rc = taskMoveVM->moveAllDisks(taskMoveVM->finalMediumsMap, &strTargetFolder);
580 if (FAILED(rc))
581 throw rc;
582
583 {
584 RTPrintf("0 Print all state files\n");
585// Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
586// settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
587 taskMoveVM->printStateFile(taskMoveVM->m_pMachine->mData->pMachineConfigFile->llFirstSnapshot);
588 }
589
590 /* Copy all save state files. */
591 Utf8Str strTrgSnapshotFolder;
592 {
593 Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
594 settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
595
596 /* When the current snapshot folder is absolute we reset it to the
597 * default relative folder. */
598 if (RTPathStartsWithRoot((*machineConfFile).machineUserData.strSnapshotFolder.c_str()))
599 (*machineConfFile).machineUserData.strSnapshotFolder = "Snapshots";
600 (*machineConfFile).strStateFile = "";
601
602 /* The absolute name of the snapshot folder. */
603 strTrgSnapshotFolder = Utf8StrFmt("%s%c%s", strTargetFolder.c_str(), RTPATH_DELIMITER,
604 (*machineConfFile).machineUserData.strSnapshotFolder.c_str());
605
606 /* Check if a snapshot folder is necessary and if so doesn't already
607 * exists. */
608 if ( taskMoveVM->finalSaveStateFilesMap.size() != 0
609 && !RTDirExists(strTrgSnapshotFolder.c_str()))
610 {
611 int vrc = RTDirCreateFullPath(strTrgSnapshotFolder.c_str(), 0700);
612 if (RT_FAILURE(vrc))
613 throw machine->setError(VBOX_E_IPRT_ERROR,
614 machine->tr("Could not create snapshots folder '%s' (%Rrc)"),
615 strTrgSnapshotFolder.c_str(), vrc);
616 }
617
618 std::map<Utf8Str, SAVESTATETASK>::iterator itState = taskMoveVM->finalSaveStateFilesMap.begin();
619 while (itState != taskMoveVM->finalSaveStateFilesMap.end())
620 {
621 const SAVESTATETASK &sst = itState->second;
622 const Utf8Str &strTrgSaveState = Utf8StrFmt("%s%c%s", strTrgSnapshotFolder.c_str(), RTPATH_DELIMITER,
623 RTPathFilename(sst.strSaveStateFile.c_str()));
624
625 /* Move to next sub-operation. */
626 rc = taskMoveVM->m_pProgress->SetNextOperation(BstrFmt(machine->tr("Move save state file '%s' ..."),
627 RTPathFilename(sst.strSaveStateFile.c_str())).raw(), sst.uWeight);
628 if (FAILED(rc)) throw rc;
629
630 int vrc = RTFileCopyEx(sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), 0,
631 MachineMoveVM::copyFileProgress, &taskMoveVM->m_pProgress);
632 if (RT_FAILURE(vrc))
633 throw machine->setError(VBOX_E_IPRT_ERROR,
634 machine->tr("Could not move state file '%s' to '%s' (%Rrc)"),
635 sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), vrc);
636
637 /* save new file in case of restoring */
638 newFiles.append(strTrgSaveState);
639 /* save original file for deletion in the end */
640 originalFiles.append(sst.strSaveStateFile);
641 ++itState;
642 }
643 }
644
645 /* Update XML settings. */
646 rc = taskMoveVM->updateStateFilesXMLSettings(taskMoveVM->finalSaveStateFilesMap, strTrgSnapshotFolder);
647 if (FAILED(rc))
648 throw rc;
649
650 {
651 RTPrintf("\n1 Print all state files\n");
652// Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
653// settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
654 taskMoveVM->printStateFile(taskMoveVM->m_pMachine->mData->pMachineConfigFile->llFirstSnapshot);
655 }
656
657 /* Moving Machine settings file */
658 {
659 RTPrintf("\nMoving Machine settings file \n");
660 Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
661 settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
662
663 rc = taskMoveVM->m_pProgress->SetNextOperation(BstrFmt(machine->tr("Moving Machine settings '%s' ..."),
664 (*machineConfFile).machineUserData.strName.c_str()).raw(), 1);
665 if (FAILED(rc)) throw rc;
666
667 Utf8Str strTargetSettingsFilePath = strTargetFolder;
668 Bstr bstrMachineName;
669 taskMoveVM->m_pMachine->COMGETTER(Name)(bstrMachineName.asOutParam());
670 strTargetSettingsFilePath.append(RTPATH_DELIMITER).append(Utf8Str(bstrMachineName)).append(".vbox");
671
672 Utf8Str strSettingsFilePath;
673 Bstr bstr_settingsFilePath;
674 taskMoveVM->m_pMachine->COMGETTER(SettingsFilePath)(bstr_settingsFilePath.asOutParam());
675 strSettingsFilePath = bstr_settingsFilePath;
676
677 int vrc = RTFileCopyEx(strSettingsFilePath.c_str(), strTargetSettingsFilePath.c_str(), 0,
678 MachineMoveVM::copyFileProgress, &taskMoveVM->m_pProgress);
679 if (RT_FAILURE(vrc))
680 throw machine->setError(VBOX_E_IPRT_ERROR,
681 machine->tr("Could not move setting file '%s' to '%s' (%Rrc)"),
682 strSettingsFilePath.c_str(), strTargetSettingsFilePath.c_str(), vrc);
683
684 /* save new file in case of restoring */
685 newFiles.append(strTargetSettingsFilePath);
686 /* save original file for deletion in the end */
687 originalFiles.append(strSettingsFilePath);
688 }
689
690 {
691 RTPrintf("\n2 Print all state files\n");
692 Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
693// settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
694 RTPrintf("\nmachineData->m_strConfigFileFull = %s\n", machineData->m_strConfigFileFull.c_str());
695 RTPrintf("\nmachineData->m_strConfigFile = %s\n", machineData->m_strConfigFile.c_str());
696 taskMoveVM->printStateFile(taskMoveVM->m_pMachine->mData->pMachineConfigFile->llFirstSnapshot);
697 }
698
699 /* save all VM data */
700 {
701 RTPrintf("\nCall Machine::SaveSettings()\n");
702 rc = taskMoveVM->m_pMachine->SaveSettings();
703 }
704
705 {
706 RTPrintf("\n3 Print all state files\n");
707 Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
708// settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
709
710 Utf8Str strTargetSettingsFilePath = strTargetFolder;
711 Bstr bstrMachineName;
712 taskMoveVM->m_pMachine->COMGETTER(Name)(bstrMachineName.asOutParam());
713 strTargetSettingsFilePath.append(RTPATH_DELIMITER).append(Utf8Str(bstrMachineName)).append(".vbox");
714 machineData->m_strConfigFileFull = strTargetSettingsFilePath;
715 taskMoveVM->m_pMachine->mParent->i_copyPathRelativeToConfig(strTargetSettingsFilePath, machineData->m_strConfigFile);
716// machineData->m_strConfigFile = strTargetSettingsFilePath;
717 RTPrintf("\nmachineData->m_strConfigFileFull = %s\n", machineData->m_strConfigFileFull.c_str());
718 RTPrintf("\nmachineData->m_strConfigFile = %s\n", machineData->m_strConfigFile.c_str());
719 taskMoveVM->printStateFile(taskMoveVM->m_pMachine->mData->pMachineConfigFile->llFirstSnapshot);
720 }
721
722 /* Marks the global registry for uuid as modified */
723 {
724 Guid uuid = taskMoveVM->m_pMachine->mData->mUuid;
725 taskMoveVM->m_pMachine->mParent->i_markRegistryModified(uuid);
726
727 // save the global settings; for that we should hold only the VirtualBox lock
728 AutoWriteLock vboxLock(taskMoveVM->m_pMachine->mParent COMMA_LOCKVAL_SRC_POS);
729 RTPrintf("\nCall global VirtualBox i_saveSettings()\n");
730 rc = taskMoveVM->m_pMachine->mParent->i_saveSettings();
731 }
732
733 {
734 RTPrintf("\n4 Print all state files\n");
735// Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
736// settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
737 taskMoveVM->printStateFile(taskMoveVM->m_pMachine->mData->pMachineConfigFile->llFirstSnapshot);
738 }
739
740 }
741 catch(HRESULT hrc)
742 {
743 RTPrintf("\nHRESULT exception\n");
744 rc = hrc;
745 taskMoveVM->result = rc;
746 }
747 catch (...)
748 {
749 RTPrintf("\nUnknown exception\n");
750 rc = VirtualBoxBase::handleUnexpectedExceptions(taskMoveVM->m_pMachine, RT_SRC_POS);
751 taskMoveVM->result = rc;
752 }
753
754 /* Cleanup on failure */
755 if (FAILED(rc))
756 {
757 /* ! Apparently we should update the Progress object !*/
758
759 /* Restore the original XML settings. */
760 rc = taskMoveVM->updateStateFilesXMLSettings(taskMoveVM->finalSaveStateFilesMap,
761 taskMoveVM->vmFolders[VBox_StateFolder]);
762 if (FAILED(rc))
763 throw rc;
764
765 {
766 RTPrintf("\nFailed: Print all state files\n");
767 Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
768// settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
769 RTPrintf("\nmachineData->m_strConfigFileFull = %s\n", machineData->m_strConfigFileFull.c_str());
770 RTPrintf("\nmachineData->m_strConfigFile = %s\n", machineData->m_strConfigFile.c_str());
771 taskMoveVM->printStateFile(taskMoveVM->m_pMachine->mData->pMachineConfigFile->llFirstSnapshot);
772 }
773
774 /* Delete all created files. */
775 rc = taskMoveVM->deleteFiles(newFiles);
776 if (FAILED(rc))
777 RTPrintf("Can't delete all new files.");
778
779 /* Restoring the original mediums */
780 try
781 {
782 rc = taskMoveVM->moveAllDisks(taskMoveVM->finalMediumsMap);
783 if (FAILED(rc))
784 throw rc;
785 }
786 catch(HRESULT hrc)
787 {
788 RTPrintf("\nFailed: HRESULT exception\n");
789 taskMoveVM->result = hrc;
790 }
791 catch (...)
792 {
793 RTPrintf("\nFailed: Unknown exception\n");
794 rc = VirtualBoxBase::handleUnexpectedExceptions(taskMoveVM->m_pMachine, RT_SRC_POS);
795 taskMoveVM->result = rc;
796 }
797
798 /* save all VM data */
799 {
800 AutoWriteLock srcLock(machine COMMA_LOCKVAL_SRC_POS);
801 srcLock.release();
802 RTPrintf("\nFailed: Call SaveSettings()\n");
803 rc = taskMoveVM->m_pMachine->SaveSettings();
804 srcLock.acquire();
805 }
806
807 /* Restore an original path to XML setting file */
808 {
809 RTPrintf("\nFailed: Print all state files\n");
810 Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
811// settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
812
813 Utf8Str strOriginalSettingsFilePath = taskMoveVM->vmFolders[VBox_SettingFolder];
814 Bstr bstrMachineName;
815 taskMoveVM->m_pMachine->COMGETTER(Name)(bstrMachineName.asOutParam());
816 strOriginalSettingsFilePath.append(RTPATH_DELIMITER).append(Utf8Str(bstrMachineName)).append(".vbox");
817
818 machineData->m_strConfigFileFull = strOriginalSettingsFilePath;
819
820 taskMoveVM->m_pMachine->mParent->i_copyPathRelativeToConfig(strOriginalSettingsFilePath,
821 machineData->m_strConfigFile);
822
823 RTPrintf("\nmachineData->m_strConfigFileFull = %s\n", machineData->m_strConfigFileFull.c_str());
824 RTPrintf("\nmachineData->m_strConfigFile = %s\n", machineData->m_strConfigFile.c_str());
825
826 taskMoveVM->printStateFile(taskMoveVM->m_pMachine->mData->pMachineConfigFile->llFirstSnapshot);
827 }
828
829 /* Marks the global registry for uuid as modified */
830 {
831 AutoWriteLock srcLock(machine COMMA_LOCKVAL_SRC_POS);
832 srcLock.release();
833 Guid uuid = taskMoveVM->m_pMachine->mData->mUuid;
834 taskMoveVM->m_pMachine->mParent->i_markRegistryModified(uuid);
835 srcLock.acquire();
836
837 // save the global settings; for that we should hold only the VirtualBox lock
838 AutoWriteLock vboxLock(taskMoveVM->m_pMachine->mParent COMMA_LOCKVAL_SRC_POS);
839 RTPrintf("\nFailed: Call global VirtualBox i_saveSettings()\n");
840 rc = taskMoveVM->m_pMachine->mParent->i_saveSettings();
841 }
842 }
843 else /*Operation was successful and now we can delete the original files like the state files, XML setting, log files */
844 {
845 rc = taskMoveVM->deleteFiles(originalFiles);
846 if (FAILED(rc))
847 RTPrintf("Can't delete all original files.");
848 }
849
850 if (!taskMoveVM->m_pProgress.isNull())
851 taskMoveVM->m_pProgress->i_notifyComplete(taskMoveVM->result);
852
853 LogFlowFuncLeave();
854}
855
856HRESULT MachineMoveVM::moveAllDisks(const std::map<Utf8Str, MEDIUMTASK>& listOfDisks,
857 const Utf8Str* strTargetFolder)
858{
859 HRESULT rc = S_OK;
860 ComObjPtr<Machine> &machine = m_pMachine;
861
862 AutoWriteLock machineLock(machine COMMA_LOCKVAL_SRC_POS);
863
864 try{
865 std::map<Utf8Str, MEDIUMTASK>::const_iterator itMedium = listOfDisks.begin();
866 while(itMedium != listOfDisks.end())
867 {
868 const MEDIUMTASK &mt = itMedium->second;
869 ComPtr<IMedium> pMedium = mt.pMedium;
870 Utf8Str strTargetImageName;
871 Utf8Str strLocation;
872 Bstr bstrLocation;
873 Bstr bstrSrcName;
874
875 rc = pMedium->COMGETTER(Name)(bstrSrcName.asOutParam());
876 if (FAILED(rc)) throw rc;
877
878 if (strTargetFolder != NULL && !strTargetFolder->isEmpty())
879 {
880 strTargetImageName = *strTargetFolder;
881 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
882 if (FAILED(rc)) throw rc;
883 strLocation = bstrLocation;
884
885 if (mt.fSnapshot == true)
886 {
887 strLocation.stripFilename().stripPath().append(RTPATH_DELIMITER).append(Utf8Str(bstrSrcName));
888 }
889 else
890 {
891 strLocation.stripPath();
892 }
893
894 strTargetImageName.append(RTPATH_DELIMITER).append(strLocation);
895 rc = m_pProgress->SetNextOperation(BstrFmt(machine->tr("Moving Disk '%ls' ..."),
896 bstrSrcName.raw()).raw(),
897 mt.uWeight);
898 if (FAILED(rc)) throw rc;
899 }
900 else
901 {
902 strTargetImageName = mt.strBaseName;//Should contain full path to the image
903 rc = m_pRollBackProgress->SetNextOperation(BstrFmt(machine->tr("Moving back Disk '%ls' ..."),
904 bstrSrcName.raw()).raw(),
905 mt.uWeight);
906 if (FAILED(rc)) throw rc;
907 }
908
909
910
911 /* consistency: use \ if appropriate on the platform */
912 RTPathChangeToDosSlashes(strTargetImageName.mutableRaw(), false);
913 RTPrintf("\nTarget disk %s \n", strTargetImageName.c_str());
914
915 ComPtr<IProgress> moveDiskProgress;
916 bstrLocation = strTargetImageName.c_str();
917 rc = pMedium->SetLocation(bstrLocation.raw(), moveDiskProgress.asOutParam());
918
919 /* Wait until the async process has finished. */
920 machineLock.release();
921 if (strTargetFolder != NULL && !strTargetFolder->isEmpty())
922 {
923 rc = m_pProgress->WaitForAsyncProgressCompletion(moveDiskProgress);
924 }
925 else
926 {
927 rc = m_pRollBackProgress->WaitForAsyncProgressCompletion(moveDiskProgress);
928 }
929
930 machineLock.acquire();
931 if (FAILED(rc)) throw rc;
932
933 RTPrintf("\nMoving %s has finished\n", strTargetImageName.c_str());
934
935 /* Check the result of the async process. */
936 LONG iRc;
937 rc = moveDiskProgress->COMGETTER(ResultCode)(&iRc);
938 if (FAILED(rc)) throw rc;
939 /* If the thread of the progress object has an error, then
940 * retrieve the error info from there, or it'll be lost. */
941 if (FAILED(iRc))
942 throw machine->setError(ProgressErrorInfo(moveDiskProgress));
943
944 ++itMedium;
945 }
946
947 machineLock.release();
948 }
949 catch(HRESULT hrc)
950 {
951 RTPrintf("\nHRESULT exception\n");
952 rc = hrc;
953 machineLock.release();
954 }
955 catch (...)
956 {
957 RTPrintf("\nUnknown exception\n");
958 rc = VirtualBoxBase::handleUnexpectedExceptions(m_pMachine, RT_SRC_POS);
959 machineLock.release();
960 }
961
962 return rc;
963}
964
965HRESULT MachineMoveVM::updateStateFilesXMLSettings(const std::map<Utf8Str, SAVESTATETASK>& listOfFiles,
966 const Utf8Str& targetPath)
967{
968 HRESULT rc = S_OK;
969 Machine::Data *machineData = m_pMachine->mData.data();
970 settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
971
972 std::map<Utf8Str, SAVESTATETASK>::const_iterator itState = listOfFiles.begin();
973 while (itState != listOfFiles.end())
974 {
975 const SAVESTATETASK &sst = itState->second;
976 const Utf8Str &strTargetSaveStateFilePath = Utf8StrFmt("%s%c%s", targetPath.c_str(),
977 RTPATH_DELIMITER,
978 RTPathFilename(sst.strSaveStateFile.c_str()));
979
980 /* Update the path in the configuration either for the current
981 * machine state or the snapshots. */
982 if (!sst.snapshotUuid.isValid() || sst.snapshotUuid.isZero())
983 {
984 (*machineConfFile).strStateFile = strTargetSaveStateFilePath;
985 }
986 else
987 {
988 updateStateFile(m_pMachine->mData->pMachineConfigFile->llFirstSnapshot,
989 sst.snapshotUuid, strTargetSaveStateFilePath);
990 }
991
992 ++itState;
993 }
994
995 return rc;
996}
997
998HRESULT MachineMoveVM::getFilesList(const Utf8Str& strRootFolder, fileList_t &filesList)
999{
1000 RTDIR hDir;
1001 HRESULT rc = S_OK;
1002 int vrc = RTDirOpen(&hDir, strRootFolder.c_str());
1003 if (RT_SUCCESS(vrc))
1004 {
1005 RTDIRENTRY DirEntry;
1006 while (RT_SUCCESS(RTDirRead(hDir, &DirEntry, NULL)))
1007 {
1008 if (RTDirEntryIsStdDotLink(&DirEntry))
1009 continue;
1010
1011 if (DirEntry.enmType == RTDIRENTRYTYPE_FILE)
1012 {
1013 Utf8Str fullPath(strRootFolder);
1014 filesList.add(strRootFolder, DirEntry.szName);
1015 }
1016 else if (DirEntry.enmType == RTDIRENTRYTYPE_DIRECTORY)
1017 {
1018 Utf8Str strNextFolder(strRootFolder);
1019 strNextFolder.append(RTPATH_DELIMITER).append(DirEntry.szName);
1020 rc = getFilesList(strNextFolder, filesList);
1021 if (FAILED(rc))
1022 break;
1023 }
1024 }
1025
1026 vrc = RTDirClose(hDir);
1027 }
1028 else if (vrc == VERR_FILE_NOT_FOUND)
1029 {
1030 m_pMachine->setError(VBOX_E_IPRT_ERROR,
1031 m_pMachine->tr("Folder '%s' doesn't exist (%Rrc)"),
1032 strRootFolder.c_str(), vrc);
1033 rc = vrc;
1034 }
1035 else
1036 throw m_pMachine->setError(VBOX_E_IPRT_ERROR,
1037 m_pMachine->tr("Could not open folder '%s' (%Rrc)"),
1038 strRootFolder.c_str(), vrc);
1039 return rc;
1040}
1041
1042HRESULT MachineMoveVM::deleteFiles(const RTCList<Utf8Str>& listOfFiles)
1043{
1044 HRESULT rc = S_OK;
1045 /* Delete all created files. */
1046 try
1047 {
1048 for (size_t i = 0; i < listOfFiles.size(); ++i)
1049 {
1050 int vrc = RTFileDelete(listOfFiles.at(i).c_str());
1051 if (RT_FAILURE(vrc))
1052 rc = m_pMachine->setError(VBOX_E_IPRT_ERROR,
1053 m_pMachine->tr("Could not delete file '%s' (%Rrc)"),
1054 listOfFiles.at(i).c_str(), rc);
1055 else
1056 RTPrintf("\nFile %s has been deleted\n", listOfFiles.at(i).c_str());
1057 }
1058 }
1059 catch(HRESULT hrc)
1060 {
1061 rc = hrc;
1062 }
1063 catch (...)
1064 {
1065 rc = VirtualBoxBase::handleUnexpectedExceptions(m_pMachine, RT_SRC_POS);
1066 }
1067
1068 return rc;
1069}
1070
1071HRESULT MachineMoveVM::getFolderSize(const Utf8Str& strRootFolder, uint64_t& size)
1072{
1073 int vrc = 0;
1074 uint64_t totalFolderSize = 0;
1075 fileList_t filesList;
1076
1077 HRESULT rc = getFilesList(strRootFolder, filesList);
1078 if (SUCCEEDED(rc))
1079 {
1080 cit_t it = filesList.m_list.begin();
1081 while(it != filesList.m_list.end())
1082 {
1083 uint64_t cbFile = 0;
1084 Utf8Str fullPath = it->first;
1085 fullPath.append(RTPATH_DELIMITER).append(it->second);
1086 vrc = RTFileQuerySize(fullPath.c_str(), &cbFile);
1087 if (RT_SUCCESS(vrc))
1088 {
1089 totalFolderSize += cbFile;
1090 }
1091 else
1092 throw m_pMachine->setError(VBOX_E_IPRT_ERROR,
1093 m_pMachine->tr("Could not get the size of file '%s' (%Rrc)"),
1094 fullPath.c_str(), vrc);
1095 ++it;
1096 }
1097// RTPrintf("Total folder %s size %llu\n", strRootFolder.c_str(), totalFolderSize);
1098 size = totalFolderSize;
1099 }
1100 else
1101 m_pMachine->setError(VBOX_E_IPRT_ERROR,
1102 m_pMachine->tr("Could not calculate the size of folder '%s' (%Rrc)"),
1103 strRootFolder.c_str(), vrc);
1104 return rc;
1105}
1106
1107HRESULT MachineMoveVM::queryBaseName(const ComPtr<IMedium> &pMedium, Utf8Str &strBaseName) const
1108{
1109 ComPtr<IMedium> pBaseMedium;
1110 HRESULT rc = pMedium->COMGETTER(Base)(pBaseMedium.asOutParam());
1111 if (FAILED(rc)) return rc;
1112 Bstr bstrBaseName;
1113 rc = pBaseMedium->COMGETTER(Name)(bstrBaseName.asOutParam());
1114 if (FAILED(rc)) return rc;
1115 strBaseName = bstrBaseName;
1116 return rc;
1117}
1118
1119HRESULT MachineMoveVM::createMachineList(const ComPtr<ISnapshot> &pSnapshot,
1120 std::vector< ComObjPtr<Machine> > &aMachineList) const
1121{
1122 HRESULT rc = S_OK;
1123 Bstr name;
1124 rc = pSnapshot->COMGETTER(Name)(name.asOutParam());
1125 if (FAILED(rc)) return rc;
1126
1127 ComPtr<IMachine> l_pMachine;
1128 rc = pSnapshot->COMGETTER(Machine)(l_pMachine.asOutParam());
1129 if (FAILED(rc)) return rc;
1130 aMachineList.push_back((Machine*)(IMachine*)l_pMachine);
1131
1132 SafeIfaceArray<ISnapshot> sfaChilds;
1133 rc = pSnapshot->COMGETTER(Children)(ComSafeArrayAsOutParam(sfaChilds));
1134 if (FAILED(rc)) return rc;
1135 for (size_t i = 0; i < sfaChilds.size(); ++i)
1136 {
1137 rc = createMachineList(sfaChilds[i], aMachineList);
1138 if (FAILED(rc)) return rc;
1139 }
1140
1141 return rc;
1142}
1143
1144HRESULT MachineMoveVM::queryMediasForAllStates(const std::vector<ComObjPtr<Machine> > &aMachineList,
1145 ULONG &uCount, ULONG &uTotalWeight)
1146{
1147 /* In this case we create a exact copy of the original VM. This means just
1148 * adding all directly and indirectly attached disk images to the worker
1149 * list. */
1150 HRESULT rc = S_OK;
1151 for (size_t i = 0; i < aMachineList.size(); ++i)
1152 {
1153 const ComObjPtr<Machine> &machine = aMachineList.at(i);
1154
1155 /* Add all attachments (and their parents) of the different
1156 * machines to a worker list. */
1157 SafeIfaceArray<IMediumAttachment> sfaAttachments;
1158 rc = machine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
1159 if (FAILED(rc)) return rc;
1160 for (size_t a = 0; a < sfaAttachments.size(); ++a)
1161 {
1162 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a];
1163 DeviceType_T type;
1164 rc = pAtt->COMGETTER(Type)(&type);
1165 if (FAILED(rc)) return rc;
1166
1167 /* Only harddisks and floppies are of interest. */
1168 if ( type != DeviceType_HardDisk
1169 && type != DeviceType_Floppy)
1170 continue;
1171
1172 /* Valid medium attached? */
1173 ComPtr<IMedium> pSrcMedium;
1174 rc = pAtt->COMGETTER(Medium)(pSrcMedium.asOutParam());
1175 if (FAILED(rc)) return rc;
1176
1177 if (pSrcMedium.isNull())
1178 continue;
1179
1180 MEDIUMTASKCHAIN mtc;
1181 mtc.devType = type;
1182
1183 while (!pSrcMedium.isNull())
1184 {
1185 /* Refresh the state so that the file size get read. */
1186 MediumState_T e;
1187 rc = pSrcMedium->RefreshState(&e);
1188 if (FAILED(rc)) return rc;
1189 LONG64 lSize;
1190 rc = pSrcMedium->COMGETTER(Size)(&lSize);
1191 if (FAILED(rc)) return rc;
1192
1193 Bstr bstrLocation;
1194 rc = pSrcMedium->COMGETTER(Location)(bstrLocation.asOutParam());
1195 if (FAILED(rc)) throw rc;
1196
1197 /* Save the current medium, for later cloning. */
1198 MEDIUMTASK mt;
1199 mt.strBaseName = bstrLocation;
1200 Utf8Str strFolder = vmFolders[VBox_SnapshotFolder];
1201 if (strFolder.isNotEmpty() && mt.strBaseName.contains(strFolder))
1202 {
1203 mt.fSnapshot = true;
1204 }
1205 else
1206 mt.fSnapshot = false;
1207
1208 mt.uIdx = UINT32_MAX;
1209 mt.pMedium = pSrcMedium;
1210 mt.uWeight = (ULONG)((lSize + _1M - 1) / _1M);
1211 mtc.chain.append(mt);
1212
1213 /* Query next parent. */
1214 rc = pSrcMedium->COMGETTER(Parent)(pSrcMedium.asOutParam());
1215 if (FAILED(rc)) return rc;
1216 }
1217 /* Update the progress info. */
1218 updateProgressStats(mtc, uCount, uTotalWeight);
1219 /* Append the list of images which have to be moved. */
1220 llMedias.append(mtc);
1221 }
1222 /* Add the save state files of this machine if there is one. */
1223 rc = addSaveState(machine, uCount, uTotalWeight);
1224 if (FAILED(rc)) return rc;
1225
1226 }
1227 /* Build up the index list of the image chain. Unfortunately we can't do
1228 * that in the previous loop, cause there we go from child -> parent and
1229 * didn't know how many are between. */
1230 for (size_t i = 0; i < llMedias.size(); ++i)
1231 {
1232 uint32_t uIdx = 0;
1233 MEDIUMTASKCHAIN &mtc = llMedias.at(i);
1234 for (size_t a = mtc.chain.size(); a > 0; --a)
1235 mtc.chain[a - 1].uIdx = uIdx++;
1236 }
1237
1238 return rc;
1239}
1240
1241HRESULT MachineMoveVM::addSaveState(const ComObjPtr<Machine> &machine, ULONG &uCount, ULONG &uTotalWeight)
1242{
1243 Bstr bstrSrcSaveStatePath;
1244 HRESULT rc = machine->COMGETTER(StateFilePath)(bstrSrcSaveStatePath.asOutParam());
1245 if (FAILED(rc)) return rc;
1246 if (!bstrSrcSaveStatePath.isEmpty())
1247 {
1248 SAVESTATETASK sst;
1249
1250 sst.snapshotUuid = machine->i_getSnapshotId();
1251 sst.strSaveStateFile = bstrSrcSaveStatePath;
1252 uint64_t cbSize;
1253 int vrc = RTFileQuerySize(sst.strSaveStateFile.c_str(), &cbSize);
1254 if (RT_FAILURE(vrc))
1255 return m_pMachine->setError(VBOX_E_IPRT_ERROR, m_pMachine->tr("Could not query file size of '%s' (%Rrc)"),
1256 sst.strSaveStateFile.c_str(), vrc);
1257 /* same rule as above: count both the data which needs to
1258 * be read and written */
1259 sst.uWeight = (ULONG)(2 * (cbSize + _1M - 1) / _1M);
1260 llSaveStateFiles.append(sst);
1261 RTPrintf("Added state file %s into the llSaveStateFiles.\n", sst.strSaveStateFile.c_str());
1262 ++uCount;
1263 uTotalWeight += sst.uWeight;
1264 }
1265 return S_OK;
1266}
1267
1268void MachineMoveVM::updateProgressStats(MEDIUMTASKCHAIN &mtc, ULONG &uCount, ULONG &uTotalWeight) const
1269{
1270
1271 /* Currently the copying of diff images involves reading at least
1272 * the biggest parent in the previous chain. So even if the new
1273 * diff image is small in size, it could need some time to create
1274 * it. Adding the biggest size in the chain should balance this a
1275 * little bit more, i.e. the weight is the sum of the data which
1276 * needs to be read and written. */
1277 ULONG uMaxWeight = 0;
1278 for (size_t e = mtc.chain.size(); e > 0; --e)
1279 {
1280 MEDIUMTASK &mt = mtc.chain.at(e - 1);
1281 mt.uWeight += uMaxWeight;
1282
1283 /* Calculate progress data */
1284 ++uCount;
1285 uTotalWeight += mt.uWeight;
1286
1287 /* Save the max size for better weighting of diff image
1288 * creation. */
1289 uMaxWeight = RT_MAX(uMaxWeight, mt.uWeight);
1290 }
1291}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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