VirtualBox

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

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

bugref:8345. The part for moving the log files was added.

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

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