VirtualBox

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

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

bugref:8345. Fixed updating a saved state path for a snapshot.

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

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