VirtualBox

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

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

bugref:8345. Improved logic updating the path of state files.

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

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