VirtualBox

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

Last change on this file since 71053 was 71053, checked in by vboxsync, 7 years ago

bugref: 8345. Added a function isMediumTypeSupportedForMoving(). Checking a trailing slash in the destination path. Cleaned up the code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.5 KB
Line 
1/* $Id: MachineImplMoveVM.cpp 71053 2018-02-19 13:35:01Z 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.virtualbox.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 = 0;//looks like it should be initialized by 1. See assertion in the Progress::setNextOperation()
281 ULONG uTotalWeight = 0;
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 if (strFolder.isNotEmpty())
366 {
367 uint64_t totalLogSize = 0;
368 rc = getFolderSize(strFolder, totalLogSize);
369 if (SUCCEEDED(rc))
370 {
371 neededFreeSpace += totalLogSize;
372 if (totalFreeSpace - neededFreeSpace <= 1024*1024)
373 {
374 throw VERR_OUT_OF_RESOURCES;//less than 1Mb free space on the target location
375 }
376
377 fileList_t filesList;
378 getFilesList(strFolder, filesList);
379 cit_t it = filesList.m_list.begin();
380 while(it != filesList.m_list.end())
381 {
382 Utf8Str strFile = it->first.c_str();
383 strFile.append(RTPATH_DELIMITER).append(it->second.c_str());
384
385 uint64_t cbFile = 0;
386 vrc = RTFileQuerySize(strFile.c_str(), &cbFile);
387 if (RT_SUCCESS(vrc))
388 {
389 uCount += 1;
390 uTotalWeight += (ULONG)((cbFile + _1M - 1) / _1M);
391 actualFileList.add(strFile);
392 LogRelFunc(("The log file %s added into the moved list\n", strFile.c_str()));
393 }
394 else
395 LogRelFunc(("The log file %s wasn't added into the moved list. Couldn't get the file size."
396 "\n", strFile.c_str()));
397 ++it;
398 }
399 }
400 }
401 }
402
403 LogRelFunc(("Total space needed is %lld bytes\n", neededFreeSpace));
404 /* Check a target location on enough room */
405 if (totalFreeSpace - neededFreeSpace <= 1024*1024)
406 {
407 LogRelFunc(("but free space on destination is %lld\n", totalFreeSpace));
408 throw VERR_OUT_OF_RESOURCES;//less than 1Mb free space on the target location
409 }
410
411 /* Add step for .vbox machine setting file */
412 {
413 ++uCount;
414 uTotalWeight += 1;
415 }
416
417 /* Reserve additional steps in case of failure and rollback all changes */
418 {
419 uTotalWeight += uCount;//just add 1 for each possible rollback operation
420 uCount += uCount;//and increase the steps twice
421 }
422
423 /* Init Progress instance */
424 {
425 rc = m_pProgress->init(m_pMachine->i_getVirtualBox(),
426 static_cast<IMachine*>(m_pMachine) /* aInitiator */,
427 Bstr(m_pMachine->tr("Moving Machine")).raw(),
428 true /* fCancellable */,
429 uCount,
430 uTotalWeight,
431 Bstr(m_pMachine->tr("Initialize Moving")).raw(),
432 1);
433 if (FAILED(rc))
434 {
435 throw m_pMachine->setError(VBOX_E_IPRT_ERROR,
436 m_pMachine->tr("Couldn't correctly setup the progress object "
437 "for moving VM operation (%Rrc)"),
438 rc);
439 }
440 }
441
442 /* save all VM data */
443 m_pMachine->i_setModified(Machine::IsModified_MachineData);
444 rc = m_pMachine->SaveSettings();
445 }
446 catch(HRESULT hrc)
447 {
448 rc = hrc;
449 }
450
451 LogFlowFuncLeave();
452
453 return rc;
454}
455
456void MachineMoveVM::printStateFile(settings::SnapshotsList &snl)
457{
458 settings::SnapshotsList::iterator it;
459 for (it = snl.begin(); it != snl.end(); ++it)
460 {
461 if (!it->strStateFile.isEmpty())
462 {
463 settings::Snapshot snap = (settings::Snapshot)(*it);
464 LogRelFunc(("snap.uuid = %s\n", snap.uuid.toStringCurly().c_str()));
465 LogRelFunc(("snap.strStateFile = %s\n", snap.strStateFile.c_str()));
466 }
467
468 if (!it->llChildSnapshots.empty())
469 printStateFile(it->llChildSnapshots);
470 }
471}
472
473/* static */
474DECLCALLBACK(int) MachineMoveVM::updateProgress(unsigned uPercent, void *pvUser)
475{
476 MachineMoveVM* pTask = *(MachineMoveVM**)pvUser;
477
478 if ( pTask
479 && !pTask->m_pProgress.isNull())
480 {
481 BOOL fCanceled;
482 pTask->m_pProgress->COMGETTER(Canceled)(&fCanceled);
483 if (fCanceled)
484 return -1;
485 pTask->m_pProgress->SetCurrentOperationProgress(uPercent);
486 }
487 return VINF_SUCCESS;
488}
489
490/* static */
491DECLCALLBACK(int) MachineMoveVM::copyFileProgress(unsigned uPercentage, void *pvUser)
492{
493 ComObjPtr<Progress> pProgress = *static_cast< ComObjPtr<Progress>* >(pvUser);
494
495 BOOL fCanceled = false;
496 HRESULT rc = pProgress->COMGETTER(Canceled)(&fCanceled);
497 if (FAILED(rc)) return VERR_GENERAL_FAILURE;
498 /* If canceled by the user tell it to the copy operation. */
499 if (fCanceled) return VERR_CANCELLED;
500 /* Set the new process. */
501 rc = pProgress->SetCurrentOperationProgress(uPercentage);
502 if (FAILED(rc)) return VERR_GENERAL_FAILURE;
503
504 return VINF_SUCCESS;
505}
506
507
508/* static */
509void MachineMoveVM::i_MoveVMThreadTask(MachineMoveVM* task)
510{
511 LogFlowFuncEnter();
512 HRESULT rc = S_OK;
513
514 MachineMoveVM* taskMoveVM = task;
515 ComObjPtr<Machine> &machine = taskMoveVM->m_pMachine;
516
517 AutoCaller autoCaller(machine);
518// if (FAILED(autoCaller.rc())) return;//Should we return something here?
519
520 Utf8Str strTargetFolder = taskMoveVM->m_targetPath;
521 {
522 Bstr bstrMachineName;
523 taskMoveVM->m_pMachine->COMGETTER(Name)(bstrMachineName.asOutParam());
524 strTargetFolder.append(Utf8Str(bstrMachineName));
525 }
526
527 RTCList<Utf8Str> newFiles; /* All extra created files (save states, ...) */
528 RTCList<Utf8Str> originalFiles; /* All original files except images */
529 typedef std::map<Utf8Str, ComObjPtr<Medium> > MediumMap;
530 MediumMap mapOriginalMedium;
531
532 /*
533 * We have the couple modes which user is able to request
534 * basic mode:
535 * - The images which are solely attached to the VM
536 * and located in the original VM folder will be moved.
537 *
538 * full mode:
539 * - All disks which are directly attached to the VM
540 * will be moved.
541 *
542 * Apart from that all other files located in the original VM
543 * folder will be moved.
544 */
545
546 /* Collect the shareable disks.
547 * Get the machines whom the shareable disks attach to.
548 * Return an error if the state of any VM doesn't allow to move a shareable disk.
549 * Check new destination whether enough room for the VM or not. if "not" return an error.
550 * Make a copy of VM settings and a list with all files which are moved. Save the list on the disk.
551 * Start "move" operation.
552 * Check the result of operation.
553 * if the operation was successful:
554 * - delete all files in the original VM folder;
555 * - update VM disks info with new location;
556 * - update all other VM if it's needed;
557 * - update global settings
558 */
559
560 try
561 {
562 /* Move all disks */
563 rc = taskMoveVM->moveAllDisks(taskMoveVM->finalMediumsMap, &strTargetFolder);
564 if (FAILED(rc))
565 throw rc;
566
567 /* Get Machine::Data here because moveAllDisks() change it */
568 Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
569 settings::MachineConfigFile *machineConfFile = machineData->pMachineConfigFile;
570
571 /* Copy all save state files. */
572 Utf8Str strTrgSnapshotFolder;
573 {
574 /* When the current snapshot folder is absolute we reset it to the
575 * default relative folder. */
576 if (RTPathStartsWithRoot((*machineConfFile).machineUserData.strSnapshotFolder.c_str()))
577 (*machineConfFile).machineUserData.strSnapshotFolder = "Snapshots";
578 (*machineConfFile).strStateFile = "";
579
580 /* The absolute name of the snapshot folder. */
581 strTrgSnapshotFolder = Utf8StrFmt("%s%c%s", strTargetFolder.c_str(), RTPATH_DELIMITER,
582 (*machineConfFile).machineUserData.strSnapshotFolder.c_str());
583
584 /* Check if a snapshot folder is necessary and if so doesn't already
585 * exists. */
586 if ( taskMoveVM->finalSaveStateFilesMap.size() != 0
587 && !RTDirExists(strTrgSnapshotFolder.c_str()))
588 {
589 int vrc = RTDirCreateFullPath(strTrgSnapshotFolder.c_str(), 0700);
590 if (RT_FAILURE(vrc))
591 throw machine->setError(VBOX_E_IPRT_ERROR,
592 machine->tr("Could not create snapshots folder '%s' (%Rrc)"),
593 strTrgSnapshotFolder.c_str(), vrc);
594 }
595
596 std::map<Utf8Str, SAVESTATETASK>::iterator itState = taskMoveVM->finalSaveStateFilesMap.begin();
597 while (itState != taskMoveVM->finalSaveStateFilesMap.end())
598 {
599 const SAVESTATETASK &sst = itState->second;
600 const Utf8Str &strTrgSaveState = Utf8StrFmt("%s%c%s", strTrgSnapshotFolder.c_str(), RTPATH_DELIMITER,
601 RTPathFilename(sst.strSaveStateFile.c_str()));
602
603 /* Move to next sub-operation. */
604 rc = taskMoveVM->m_pProgress->SetNextOperation(BstrFmt(machine->tr("Copy the save state file '%s' ..."),
605 RTPathFilename(sst.strSaveStateFile.c_str())).raw(), sst.uWeight);
606 if (FAILED(rc)) throw rc;
607
608 int vrc = RTFileCopyEx(sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), 0,
609 MachineMoveVM::copyFileProgress, &taskMoveVM->m_pProgress);
610 if (RT_FAILURE(vrc))
611 throw machine->setError(VBOX_E_IPRT_ERROR,
612 machine->tr("Could not copy state file '%s' to '%s' (%Rrc)"),
613 sst.strSaveStateFile.c_str(), strTrgSaveState.c_str(), vrc);
614
615 /* save new file in case of restoring */
616 newFiles.append(strTrgSaveState);
617 /* save original file for deletion in the end */
618 originalFiles.append(sst.strSaveStateFile);
619 ++itState;
620 }
621 }
622
623 /*
624 * Update state file path
625 * very important step!
626 */
627 rc = taskMoveVM->updatePathsToStateFiles(taskMoveVM->finalSaveStateFilesMap,
628 taskMoveVM->vmFolders[VBox_SettingFolder],
629 strTargetFolder);
630 if (FAILED(rc))
631 throw rc;
632
633 /* Moving Machine settings file */
634 {
635 LogRelFunc(("Copy Machine settings file \n"));
636
637 rc = taskMoveVM->m_pProgress->SetNextOperation(BstrFmt(machine->tr("Copy Machine settings file '%s' ..."),
638 (*machineConfFile).machineUserData.strName.c_str()).raw(), 1);
639 if (FAILED(rc)) throw rc;
640
641 Utf8Str strTargetSettingsFilePath = strTargetFolder;
642 Bstr bstrMachineName;
643 taskMoveVM->m_pMachine->COMGETTER(Name)(bstrMachineName.asOutParam());
644 strTargetSettingsFilePath.append(RTPATH_DELIMITER).append(Utf8Str(bstrMachineName)).append(".vbox");
645
646 Utf8Str strSettingsFilePath;
647 Bstr bstr_settingsFilePath;
648 taskMoveVM->m_pMachine->COMGETTER(SettingsFilePath)(bstr_settingsFilePath.asOutParam());
649 strSettingsFilePath = bstr_settingsFilePath;
650
651 int vrc = RTFileCopyEx(strSettingsFilePath.c_str(), strTargetSettingsFilePath.c_str(), 0,
652 MachineMoveVM::copyFileProgress, &taskMoveVM->m_pProgress);
653 if (RT_FAILURE(vrc))
654 throw machine->setError(VBOX_E_IPRT_ERROR,
655 machine->tr("Could not copy the setting file '%s' to '%s' (%Rrc)"),
656 strSettingsFilePath.c_str(), strTargetSettingsFilePath.stripFilename().c_str(), vrc);
657
658 LogRelFunc(("The setting file %s has been copied into the folder %s\n", strSettingsFilePath.c_str(),
659 strTargetSettingsFilePath.stripFilename().c_str()));
660 /* save new file in case of restoring */
661 newFiles.append(strTargetSettingsFilePath);
662 /* save original file for deletion in the end */
663 originalFiles.append(strSettingsFilePath);
664 }
665
666 /* Moving Machine log files */
667 {
668 LogRelFunc(("Copy machine log files \n"));
669
670 Utf8Str strTargetLogFolderPath = strTargetFolder;
671
672 if (taskMoveVM->vmFolders[VBox_LogFolder].isNotEmpty())
673 {
674 Bstr bstrMachineName;
675 taskMoveVM->m_pMachine->COMGETTER(Name)(bstrMachineName.asOutParam());
676 strTargetLogFolderPath.append(RTPATH_DELIMITER).append("Logs");
677
678 /* Check a log folder existing and create one if it's not */
679 if (!RTDirExists(strTargetLogFolderPath.c_str()))
680 {
681 int vrc = RTDirCreateFullPath(strTargetLogFolderPath.c_str(), 0700);
682 if (RT_FAILURE(vrc))
683 throw machine->setError(VBOX_E_IPRT_ERROR,
684 machine->tr("Could not create log folder '%s' (%Rrc)"),
685 strTargetLogFolderPath.c_str(), vrc);
686 }
687
688 fileList_t filesList;
689 taskMoveVM->getFilesList(taskMoveVM->vmFolders[VBox_LogFolder], filesList);
690 cit_t it = filesList.m_list.begin();
691 while(it != filesList.m_list.end())
692 {
693 Utf8Str strFullSourceFilePath = it->first.c_str();
694 strFullSourceFilePath.append(RTPATH_DELIMITER).append(it->second.c_str());
695
696 Utf8Str strFullTargetFilePath = strTargetLogFolderPath;
697 strFullTargetFilePath.append(RTPATH_DELIMITER).append(it->second.c_str());
698
699 /* Move to next sub-operation. */
700 rc = taskMoveVM->m_pProgress->SetNextOperation(BstrFmt(machine->tr("Copying the log file '%s' ..."),
701 RTPathFilename(strFullSourceFilePath.c_str())).raw(), 1);
702 if (FAILED(rc)) throw rc;
703
704 int vrc = RTFileCopyEx(strFullSourceFilePath.c_str(), strFullTargetFilePath.c_str(), 0,
705 MachineMoveVM::copyFileProgress, &taskMoveVM->m_pProgress);
706 if (RT_FAILURE(vrc))
707 throw machine->setError(VBOX_E_IPRT_ERROR,
708 machine->tr("Could not copy the log file '%s' to '%s' (%Rrc)"),
709 strFullSourceFilePath.c_str(), strFullTargetFilePath.stripFilename().c_str(), vrc);
710
711 LogRelFunc(("The log file %s has been copied into the folder %s\n", strFullSourceFilePath.c_str(),
712 strFullTargetFilePath.stripFilename().c_str()));
713
714 /* save new file in case of restoring */
715 newFiles.append(strFullTargetFilePath);
716 /* save original file for deletion in the end */
717 originalFiles.append(strFullSourceFilePath);
718
719 ++it;
720 }
721 }
722 }
723
724 /* save all VM data */
725 {
726 rc = taskMoveVM->m_pMachine->SaveSettings();
727 }
728
729 {
730 LogRelFunc(("Update path to XML setting file\n"));
731 Utf8Str strTargetSettingsFilePath = strTargetFolder;
732 Bstr bstrMachineName;
733 taskMoveVM->m_pMachine->COMGETTER(Name)(bstrMachineName.asOutParam());
734 strTargetSettingsFilePath.append(RTPATH_DELIMITER).append(Utf8Str(bstrMachineName)).append(".vbox");
735 machineData->m_strConfigFileFull = strTargetSettingsFilePath;
736 taskMoveVM->m_pMachine->mParent->i_copyPathRelativeToConfig(strTargetSettingsFilePath, machineData->m_strConfigFile);
737 }
738
739 /* Marks the global registry for uuid as modified */
740 {
741 Guid uuid = taskMoveVM->m_pMachine->mData->mUuid;
742 taskMoveVM->m_pMachine->mParent->i_markRegistryModified(uuid);
743
744 // save the global settings; for that we should hold only the VirtualBox lock
745 AutoWriteLock vboxLock(taskMoveVM->m_pMachine->mParent COMMA_LOCKVAL_SRC_POS);
746
747 rc = taskMoveVM->m_pMachine->mParent->i_saveSettings();
748 }
749 }
750 catch(HRESULT hrc)
751 {
752 LogRelFunc(("Moving machine to a new destination was failed. Check original and destination places.\n"));
753 rc = hrc;
754 taskMoveVM->result = rc;
755 }
756 catch (...)
757 {
758 LogRelFunc(("Moving machine to a new destination was failed. Check original and destination places.\n"));
759 rc = VirtualBoxBase::handleUnexpectedExceptions(taskMoveVM->m_pMachine, RT_SRC_POS);
760 taskMoveVM->result = rc;
761 }
762
763 /* Cleanup on failure */
764 if (FAILED(rc))
765 {
766 Machine::Data *machineData = taskMoveVM->m_pMachine->mData.data();
767
768 /* ! Apparently we should update the Progress object !*/
769 ULONG operationCount = 0;
770 rc = taskMoveVM->m_pProgress->COMGETTER(OperationCount)(&operationCount);
771 ULONG operation = 0;
772 rc = taskMoveVM->m_pProgress->COMGETTER(Operation)(&operation);
773 Bstr bstrOperationDescription;
774 rc = taskMoveVM->m_pProgress->COMGETTER(OperationDescription)(bstrOperationDescription.asOutParam());
775 Utf8Str strOperationDescription = bstrOperationDescription;
776 ULONG operationPercent = 0;
777 rc = taskMoveVM->m_pProgress->COMGETTER(OperationPercent)(&operationPercent);
778
779 Bstr bstrMachineName;
780 taskMoveVM->m_pMachine->COMGETTER(Name)(bstrMachineName.asOutParam());
781 LogRelFunc(("Moving machine %s was failed on operation %s\n",
782 Utf8Str(bstrMachineName.raw()).c_str(), Utf8Str(bstrOperationDescription.raw()).c_str()));
783
784 /* Restoring the original mediums */
785 try
786 {
787 /*
788 * Fix the progress count
789 * In instance, the whole "move vm" operation is failed on 9th step. But total count is 20.
790 * Where 20 = 2 * 10 operations, where 10 is the real number of operations. And this value was doubled
791 * earlier in the init() exactly for one reason - rollback operation. Because in this case we must do
792 * the same operations but in backward direction.
793 * Thus now we want to correct progress operation count from 9 to 11. Why?
794 * Because we should have evaluated count as "20/2 + (20/2 - 9)" = 11 or just "20 - 9" = 11
795 */
796 for (ULONG i = operation; i < operationCount - operation; ++i)
797 {
798 rc = taskMoveVM->m_pProgress->SetNextOperation(BstrFmt("Skip the empty operation %d...", i + 1).raw(), 1);
799 if (FAILED(rc)) throw rc;
800 }
801
802 rc = taskMoveVM->moveAllDisks(taskMoveVM->finalMediumsMap);
803 if (FAILED(rc))
804 throw rc;
805 }
806 catch(HRESULT hrc)
807 {
808 LogRelFunc(("Rollback scenario: restoration the original mediums were failed. Machine can be corrupted.\n"));
809 taskMoveVM->result = hrc;
810 }
811 catch (...)
812 {
813 LogRelFunc(("Rollback scenario: restoration the original mediums were failed. Machine can be corrupted.\n"));
814 rc = VirtualBoxBase::handleUnexpectedExceptions(taskMoveVM->m_pMachine, RT_SRC_POS);
815 taskMoveVM->result = rc;
816 }
817
818 /* Revert original paths to the state files */
819 rc = taskMoveVM->updatePathsToStateFiles(taskMoveVM->finalSaveStateFilesMap,
820 strTargetFolder,
821 taskMoveVM->vmFolders[VBox_SettingFolder]);
822 if (FAILED(rc))
823 {
824 LogRelFunc(("Rollback scenario: can't restore the original paths to the state files. "
825 "Machine settings %s can be corrupted.\n", machineData->m_strConfigFileFull.c_str()));
826 }
827
828 /* Delete all created files. Here we update progress object */
829 rc = taskMoveVM->deleteFiles(newFiles);
830 if (FAILED(rc))
831 LogRelFunc(("Rollback scenario: can't delete new created files. Check the destination folder."));
832
833 /* Delete destination folder */
834 RTDirRemove(strTargetFolder.c_str());
835
836 /* save all VM data */
837 {
838 AutoWriteLock srcLock(machine COMMA_LOCKVAL_SRC_POS);
839 srcLock.release();
840 rc = taskMoveVM->m_pMachine->SaveSettings();
841 srcLock.acquire();
842 }
843
844 /* Restore an original path to XML setting file */
845 {
846 LogRelFunc(("Rollback scenario: Restore an original path to XML setting file\n"));
847 Utf8Str strOriginalSettingsFilePath = taskMoveVM->vmFolders[VBox_SettingFolder];
848 strOriginalSettingsFilePath.append(RTPATH_DELIMITER).append(Utf8Str(bstrMachineName)).append(".vbox");
849 machineData->m_strConfigFileFull = strOriginalSettingsFilePath;
850 taskMoveVM->m_pMachine->mParent->i_copyPathRelativeToConfig(strOriginalSettingsFilePath,
851 machineData->m_strConfigFile);
852 }
853
854 /* Marks the global registry for uuid as modified */
855 {
856 AutoWriteLock srcLock(machine COMMA_LOCKVAL_SRC_POS);
857 srcLock.release();
858 Guid uuid = taskMoveVM->m_pMachine->mData->mUuid;
859 taskMoveVM->m_pMachine->mParent->i_markRegistryModified(uuid);
860 srcLock.acquire();
861
862 // save the global settings; for that we should hold only the VirtualBox lock
863 AutoWriteLock vboxLock(taskMoveVM->m_pMachine->mParent COMMA_LOCKVAL_SRC_POS);
864
865 rc = taskMoveVM->m_pMachine->mParent->i_saveSettings();
866 }
867
868 /* In case of failure the progress object on the other side (user side) get notification about operation
869 completion but the operation percentage may not be set to 100% */
870 }
871 else /*Operation was successful and now we can delete the original files like the state files, XML setting, log files */
872 {
873 /*
874 * In case of success it's not urgent to update the progress object because we call i_notifyComplete() with
875 * the success result. As result, the last number of progress operation can be not equal the number of operations
876 * because we doubled the number of operations for rollback case.
877 * But if we want to update the progress object corectly it's needed to add all medium moved by standard
878 * "move medium" logic (for us it's taskMoveVM->finalMediumsMap) to the current number of operation.
879 */
880
881 ULONG operationCount = 0;
882 rc = taskMoveVM->m_pProgress->COMGETTER(OperationCount)(&operationCount);
883 ULONG operation = 0;
884 rc = taskMoveVM->m_pProgress->COMGETTER(Operation)(&operation);
885
886 for (ULONG i = operation; i < operation + taskMoveVM->finalMediumsMap.size() - 1; ++i)
887 {
888 rc = taskMoveVM->m_pProgress->SetNextOperation(BstrFmt("Skip the empty operation %d...", i).raw(), 1);
889 if (FAILED(rc)) throw rc;
890 }
891
892 rc = taskMoveVM->deleteFiles(originalFiles);
893 if (FAILED(rc))
894 LogRelFunc(("Rollback scenario: can't delete all original files.\n"));
895 }
896
897 if (!taskMoveVM->m_pProgress.isNull())
898 taskMoveVM->m_pProgress->i_notifyComplete(taskMoveVM->result);
899
900 LogFlowFuncLeave();
901}
902
903HRESULT MachineMoveVM::moveAllDisks(const std::map<Utf8Str, MEDIUMTASK>& listOfDisks,
904 const Utf8Str* strTargetFolder)
905{
906 HRESULT rc = S_OK;
907 ComObjPtr<Machine> &machine = m_pMachine;
908 Utf8Str strLocation;
909
910 AutoWriteLock machineLock(machine COMMA_LOCKVAL_SRC_POS);
911
912 try{
913 std::map<Utf8Str, MEDIUMTASK>::const_iterator itMedium = listOfDisks.begin();
914 while(itMedium != listOfDisks.end())
915 {
916 const MEDIUMTASK &mt = itMedium->second;
917 ComPtr<IMedium> pMedium = mt.pMedium;
918 Utf8Str strTargetImageName;
919 Bstr bstrLocation;
920 Bstr bstrSrcName;
921
922 rc = pMedium->COMGETTER(Name)(bstrSrcName.asOutParam());
923 if (FAILED(rc)) throw rc;
924
925 if (strTargetFolder != NULL && !strTargetFolder->isEmpty())
926 {
927 strTargetImageName = *strTargetFolder;
928 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
929 if (FAILED(rc)) throw rc;
930 strLocation = bstrLocation;
931
932 if (mt.fSnapshot == true)
933 {
934 strLocation.stripFilename().stripPath().append(RTPATH_DELIMITER).append(Utf8Str(bstrSrcName));
935 }
936 else
937 {
938 strLocation.stripPath();
939 }
940
941 strTargetImageName.append(RTPATH_DELIMITER).append(strLocation);
942 rc = m_pProgress->SetNextOperation(BstrFmt(machine->tr("Moving medium '%ls' ..."),
943 bstrSrcName.raw()).raw(),
944 mt.uWeight);
945 if (FAILED(rc)) throw rc;
946 }
947 else
948 {
949 strTargetImageName = mt.strBaseName;//Should contain full path to the image
950 rc = m_pProgress->SetNextOperation(BstrFmt(machine->tr("Moving medium '%ls' back..."),
951 bstrSrcName.raw()).raw(),
952 mt.uWeight);
953 if (FAILED(rc)) throw rc;
954 }
955
956
957
958 /* consistency: use \ if appropriate on the platform */
959 RTPathChangeToDosSlashes(strTargetImageName.mutableRaw(), false);
960
961 bstrLocation = strTargetImageName.c_str();
962
963 MediumType_T mediumType;//immutable, shared, passthrough
964 rc = pMedium->COMGETTER(Type)(&mediumType);
965 if (FAILED(rc)) throw rc;
966
967 DeviceType_T deviceType;//floppy, hard, DVD
968 rc = pMedium->COMGETTER(DeviceType)(&deviceType);
969 if (FAILED(rc)) throw rc;
970
971 ComPtr<IProgress> moveDiskProgress;
972 rc = pMedium->SetLocation(bstrLocation.raw(), moveDiskProgress.asOutParam());
973 /* Wait until the async process has finished. */
974 machineLock.release();
975
976 rc = m_pProgress->WaitForAsyncProgressCompletion(moveDiskProgress);
977
978 machineLock.acquire();
979 if (FAILED(rc)) throw rc;
980
981 LogRelFunc(("Moving %s has been finished\n", strTargetImageName.c_str()));
982
983 /* Check the result of the async process. */
984 LONG iRc;
985 rc = moveDiskProgress->COMGETTER(ResultCode)(&iRc);
986 if (FAILED(rc)) throw rc;
987 /* If the thread of the progress object has an error, then
988 * retrieve the error info from there, or it'll be lost. */
989 if (FAILED(iRc))
990 throw machine->setError(ProgressErrorInfo(moveDiskProgress));
991
992 ++itMedium;
993 }
994
995 machineLock.release();
996 }
997 catch(HRESULT hrc)
998 {
999 LogRelFunc(("\nException during moving the disk %s\n", strLocation.c_str()));
1000 rc = hrc;
1001 machineLock.release();
1002 }
1003 catch (...)
1004 {
1005 LogRelFunc(("\nException during moving the disk %s\n", strLocation.c_str()));
1006 rc = VirtualBoxBase::handleUnexpectedExceptions(m_pMachine, RT_SRC_POS);
1007 machineLock.release();
1008 }
1009
1010 return rc;
1011}
1012
1013HRESULT MachineMoveVM::updatePathsToStateFiles(const std::map<Utf8Str, SAVESTATETASK>& listOfFiles,
1014 const Utf8Str& sourcePath, const Utf8Str& targetPath)
1015{
1016 HRESULT rc = S_OK;
1017
1018 std::map<Utf8Str, SAVESTATETASK>::const_iterator itState = listOfFiles.begin();
1019 while (itState != listOfFiles.end())
1020 {
1021 const SAVESTATETASK &sst = itState->second;
1022
1023 Utf8Str strGuidMachine = sst.snapshotUuid.toString();
1024 ComObjPtr<Snapshot> snapshotMachineObj;
1025
1026 rc = m_pMachine->i_findSnapshotById(sst.snapshotUuid, snapshotMachineObj, true);
1027 if (SUCCEEDED(rc) && !snapshotMachineObj.isNull())
1028 {
1029 snapshotMachineObj->i_updateSavedStatePaths(sourcePath.c_str(),
1030 targetPath.c_str());
1031 }
1032
1033 ++itState;
1034 }
1035
1036 return rc;
1037}
1038
1039HRESULT MachineMoveVM::getFilesList(const Utf8Str& strRootFolder, fileList_t &filesList)
1040{
1041 RTDIR hDir;
1042 HRESULT rc = S_OK;
1043 int vrc = RTDirOpen(&hDir, strRootFolder.c_str());
1044 if (RT_SUCCESS(vrc))
1045 {
1046 RTDIRENTRY DirEntry;
1047 while (RT_SUCCESS(RTDirRead(hDir, &DirEntry, NULL)))
1048 {
1049 if (RTDirEntryIsStdDotLink(&DirEntry))
1050 continue;
1051
1052 if (DirEntry.enmType == RTDIRENTRYTYPE_FILE)
1053 {
1054 Utf8Str fullPath(strRootFolder);
1055 filesList.add(strRootFolder, DirEntry.szName);
1056 }
1057 else if (DirEntry.enmType == RTDIRENTRYTYPE_DIRECTORY)
1058 {
1059 Utf8Str strNextFolder(strRootFolder);
1060 strNextFolder.append(RTPATH_DELIMITER).append(DirEntry.szName);
1061 rc = getFilesList(strNextFolder, filesList);
1062 if (FAILED(rc))
1063 break;
1064 }
1065 }
1066
1067 vrc = RTDirClose(hDir);
1068 }
1069 else if (vrc == VERR_FILE_NOT_FOUND)
1070 {
1071 m_pMachine->setError(VBOX_E_IPRT_ERROR,
1072 m_pMachine->tr("Folder '%s' doesn't exist (%Rrc)"),
1073 strRootFolder.c_str(), vrc);
1074 rc = vrc;
1075 }
1076 else
1077 throw m_pMachine->setError(VBOX_E_IPRT_ERROR,
1078 m_pMachine->tr("Could not open folder '%s' (%Rrc)"),
1079 strRootFolder.c_str(), vrc);
1080 return rc;
1081}
1082
1083HRESULT MachineMoveVM::deleteFiles(const RTCList<Utf8Str>& listOfFiles)
1084{
1085 HRESULT rc = S_OK;
1086 /* Delete all created files. */
1087 try
1088 {
1089 for (size_t i = 0; i < listOfFiles.size(); ++i)
1090 {
1091 rc = m_pProgress->SetNextOperation(BstrFmt("Deleting file %s...", listOfFiles.at(i).c_str()).raw(), 1);
1092 if (FAILED(rc)) throw rc;
1093
1094 Bstr bstrOperationDescription;
1095 rc = m_pProgress->COMGETTER(OperationDescription)(bstrOperationDescription.asOutParam());
1096 Utf8Str strOperationDescription = bstrOperationDescription;
1097 LogRelFunc(("%s\n", strOperationDescription.c_str()));
1098
1099 int vrc = RTFileDelete(listOfFiles.at(i).c_str());
1100 if (RT_FAILURE(vrc))
1101 rc = m_pMachine->setError(VBOX_E_IPRT_ERROR,
1102 m_pMachine->tr("Could not delete file '%s' (%Rrc)"),
1103 listOfFiles.at(i).c_str(), rc);
1104 else
1105 LogRelFunc(("File %s has been deleted\n", listOfFiles.at(i).c_str()));
1106 }
1107 }
1108 catch(HRESULT hrc)
1109 {
1110 rc = hrc;
1111 }
1112 catch (...)
1113 {
1114 rc = VirtualBoxBase::handleUnexpectedExceptions(m_pMachine, RT_SRC_POS);
1115 }
1116
1117 return rc;
1118}
1119
1120HRESULT MachineMoveVM::getFolderSize(const Utf8Str& strRootFolder, uint64_t& size)
1121{
1122 int vrc = 0;
1123 uint64_t totalFolderSize = 0;
1124 fileList_t filesList;
1125
1126 HRESULT rc = getFilesList(strRootFolder, filesList);
1127 if (SUCCEEDED(rc))
1128 {
1129 cit_t it = filesList.m_list.begin();
1130 while(it != filesList.m_list.end())
1131 {
1132 uint64_t cbFile = 0;
1133 Utf8Str fullPath = it->first;
1134 fullPath.append(RTPATH_DELIMITER).append(it->second);
1135 vrc = RTFileQuerySize(fullPath.c_str(), &cbFile);
1136 if (RT_SUCCESS(vrc))
1137 {
1138 totalFolderSize += cbFile;
1139 }
1140 else
1141 throw m_pMachine->setError(VBOX_E_IPRT_ERROR,
1142 m_pMachine->tr("Could not get the size of file '%s' (%Rrc)"),
1143 fullPath.c_str(), vrc);
1144 ++it;
1145 }
1146
1147 size = totalFolderSize;
1148 }
1149 else
1150 m_pMachine->setError(VBOX_E_IPRT_ERROR,
1151 m_pMachine->tr("Could not calculate the size of folder '%s' (%Rrc)"),
1152 strRootFolder.c_str(), vrc);
1153 return rc;
1154}
1155
1156HRESULT MachineMoveVM::queryBaseName(const ComPtr<IMedium> &pMedium, Utf8Str &strBaseName) const
1157{
1158 ComPtr<IMedium> pBaseMedium;
1159 HRESULT rc = pMedium->COMGETTER(Base)(pBaseMedium.asOutParam());
1160 if (FAILED(rc)) return rc;
1161 Bstr bstrBaseName;
1162 rc = pBaseMedium->COMGETTER(Name)(bstrBaseName.asOutParam());
1163 if (FAILED(rc)) return rc;
1164 strBaseName = bstrBaseName;
1165 return rc;
1166}
1167
1168HRESULT MachineMoveVM::createMachineList(const ComPtr<ISnapshot> &pSnapshot,
1169 std::vector< ComObjPtr<Machine> > &aMachineList) const
1170{
1171 HRESULT rc = S_OK;
1172 Bstr name;
1173 rc = pSnapshot->COMGETTER(Name)(name.asOutParam());
1174 if (FAILED(rc)) return rc;
1175
1176 ComPtr<IMachine> l_pMachine;
1177 rc = pSnapshot->COMGETTER(Machine)(l_pMachine.asOutParam());
1178 if (FAILED(rc)) return rc;
1179 aMachineList.push_back((Machine*)(IMachine*)l_pMachine);
1180
1181 SafeIfaceArray<ISnapshot> sfaChilds;
1182 rc = pSnapshot->COMGETTER(Children)(ComSafeArrayAsOutParam(sfaChilds));
1183 if (FAILED(rc)) return rc;
1184 for (size_t i = 0; i < sfaChilds.size(); ++i)
1185 {
1186 rc = createMachineList(sfaChilds[i], aMachineList);
1187 if (FAILED(rc)) return rc;
1188 }
1189
1190 return rc;
1191}
1192
1193HRESULT MachineMoveVM::queryMediasForAllStates(const std::vector<ComObjPtr<Machine> > &aMachineList)
1194{
1195 /* In this case we create a exact copy of the original VM. This means just
1196 * adding all directly and indirectly attached disk images to the worker
1197 * list. */
1198 HRESULT rc = S_OK;
1199 for (size_t i = 0; i < aMachineList.size(); ++i)
1200 {
1201 const ComObjPtr<Machine> &machine = aMachineList.at(i);
1202
1203 /* Add all attachments (and their parents) of the different
1204 * machines to a worker list. */
1205 SafeIfaceArray<IMediumAttachment> sfaAttachments;
1206 rc = machine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(sfaAttachments));
1207 if (FAILED(rc)) return rc;
1208 for (size_t a = 0; a < sfaAttachments.size(); ++a)
1209 {
1210 const ComPtr<IMediumAttachment> &pAtt = sfaAttachments[a];
1211 DeviceType_T deviceType;//floppy, hard, DVD
1212 rc = pAtt->COMGETTER(Type)(&deviceType);
1213 if (FAILED(rc)) return rc;
1214
1215 /* Valid medium attached? */
1216 ComPtr<IMedium> pMedium;
1217 rc = pAtt->COMGETTER(Medium)(pMedium.asOutParam());
1218 if (FAILED(rc)) return rc;
1219
1220 if (pMedium.isNull())
1221 continue;
1222
1223 Bstr bstrLocation;
1224 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
1225 if (FAILED(rc)) throw rc;
1226
1227 /* Cast to ComObjPtr<Medium> */
1228 ComObjPtr<Medium> pObjMedium = (Medium *)(IMedium *)pMedium;
1229
1230 /*Check for "read-only" medium in terms that VBox can't create this one */
1231 bool fPass = isMediumTypeSupportedForMoving(pMedium);
1232 if(!fPass)
1233 {
1234 LogRelFunc(("Skipping file %s because of this medium type hasn't been supported for moving.\n",
1235 Utf8Str(bstrLocation.raw()).c_str()));
1236 continue;
1237 }
1238
1239 MEDIUMTASKCHAIN mtc;
1240 mtc.devType = deviceType;
1241
1242 while (!pMedium.isNull())
1243 {
1244 /* Refresh the state so that the file size get read. */
1245 MediumState_T e;
1246 rc = pMedium->RefreshState(&e);
1247 if (FAILED(rc)) return rc;
1248
1249 LONG64 lSize;
1250 rc = pMedium->COMGETTER(Size)(&lSize);
1251 if (FAILED(rc)) return rc;
1252
1253 MediumType_T mediumType;//immutable, shared, passthrough
1254 rc = pMedium->COMGETTER(Type)(&mediumType);
1255 if (FAILED(rc)) throw rc;
1256
1257 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
1258 if (FAILED(rc)) throw rc;
1259
1260 MEDIUMTASK mt;
1261 mt.strBaseName = bstrLocation;
1262 Utf8Str strFolder = vmFolders[VBox_SnapshotFolder];
1263 if (strFolder.isNotEmpty() && mt.strBaseName.contains(strFolder))
1264 {
1265 mt.fSnapshot = true;
1266 }
1267 else
1268 mt.fSnapshot = false;
1269
1270 mt.uIdx = UINT32_MAX;
1271 mt.pMedium = pMedium;
1272 mt.uWeight = (ULONG)((lSize + _1M - 1) / _1M);
1273 mtc.chain.append(mt);
1274
1275 /* Query next parent. */
1276 rc = pMedium->COMGETTER(Parent)(pMedium.asOutParam());
1277 if (FAILED(rc)) return rc;
1278 }
1279
1280 llMedias.append(mtc);
1281 }
1282 /* Add the save state files of this machine if there is one. */
1283 rc = addSaveState(machine);
1284 if (FAILED(rc)) return rc;
1285
1286 }
1287 /* Build up the index list of the image chain. Unfortunately we can't do
1288 * that in the previous loop, cause there we go from child -> parent and
1289 * didn't know how many are between. */
1290 for (size_t i = 0; i < llMedias.size(); ++i)
1291 {
1292 uint32_t uIdx = 0;
1293 MEDIUMTASKCHAIN &mtc = llMedias.at(i);
1294 for (size_t a = mtc.chain.size(); a > 0; --a)
1295 mtc.chain[a - 1].uIdx = uIdx++;
1296 }
1297
1298 return rc;
1299}
1300
1301HRESULT MachineMoveVM::addSaveState(const ComObjPtr<Machine> &machine)
1302{
1303 Bstr bstrSrcSaveStatePath;
1304 HRESULT rc = machine->COMGETTER(StateFilePath)(bstrSrcSaveStatePath.asOutParam());
1305 if (FAILED(rc)) return rc;
1306 if (!bstrSrcSaveStatePath.isEmpty())
1307 {
1308 SAVESTATETASK sst;
1309
1310 sst.snapshotUuid = machine->i_getSnapshotId();
1311 sst.strSaveStateFile = bstrSrcSaveStatePath;
1312 uint64_t cbSize;
1313
1314 int vrc = RTFileQuerySize(sst.strSaveStateFile.c_str(), &cbSize);
1315 if (RT_FAILURE(vrc))
1316 return m_pMachine->setError(VBOX_E_IPRT_ERROR, m_pMachine->tr("Could not query file size of '%s' (%Rrc)"),
1317 sst.strSaveStateFile.c_str(), vrc);
1318 /* same rule as above: count both the data which needs to
1319 * be read and written */
1320 sst.uWeight = (ULONG)(2 * (cbSize + _1M - 1) / _1M);
1321 llSaveStateFiles.append(sst);
1322 }
1323 return S_OK;
1324}
1325
1326void MachineMoveVM::updateProgressStats(MEDIUMTASKCHAIN &mtc, ULONG &uCount, ULONG &uTotalWeight) const
1327{
1328
1329 /* Currently the copying of diff images involves reading at least
1330 * the biggest parent in the previous chain. So even if the new
1331 * diff image is small in size, it could need some time to create
1332 * it. Adding the biggest size in the chain should balance this a
1333 * little bit more, i.e. the weight is the sum of the data which
1334 * needs to be read and written. */
1335 ULONG uMaxWeight = 0;
1336 for (size_t e = mtc.chain.size(); e > 0; --e)
1337 {
1338 MEDIUMTASK &mt = mtc.chain.at(e - 1);
1339 mt.uWeight += uMaxWeight;
1340
1341 /* Calculate progress data */
1342 ++uCount;
1343 uTotalWeight += mt.uWeight;
1344
1345 /* Save the max size for better weighting of diff image
1346 * creation. */
1347 uMaxWeight = RT_MAX(uMaxWeight, mt.uWeight);
1348 }
1349}
1350
1351bool MachineMoveVM::isMediumTypeSupportedForMoving(const ComPtr<IMedium> &pMedium)
1352{
1353 HRESULT rc = S_OK;
1354 bool fSupported = true;
1355 Bstr bstrLocation;
1356 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
1357 if (FAILED(rc))
1358 {
1359 fSupported = false;
1360 throw rc;
1361 }
1362
1363 DeviceType_T deviceType;
1364 rc = pMedium->COMGETTER(DeviceType)(&deviceType);
1365 if (FAILED(rc))
1366 {
1367 fSupported = false;
1368 throw rc;
1369 }
1370
1371 ComPtr<IMediumFormat> mediumFormat;
1372 rc = pMedium->COMGETTER(MediumFormat)(mediumFormat.asOutParam());
1373 if (FAILED(rc))
1374 {
1375 fSupported = false;
1376 throw rc;
1377 }
1378
1379 /*Check whether VBox is able to create this medium format or not, i.e. medium can be "read-only" */
1380 Bstr bstrFormatName;
1381 rc = mediumFormat->COMGETTER(Name)(bstrFormatName.asOutParam());
1382 if (FAILED(rc))
1383 {
1384 fSupported = false;
1385 throw rc;
1386 }
1387
1388 Utf8Str formatName = Utf8Str(bstrFormatName);
1389 if (formatName.compare("VHDX", Utf8Str::CaseInsensitive) == 0)
1390 {
1391 LogRelFunc(("Skipping medium %s. VHDX format is supported in \"read-only\" mode only. \n",
1392 Utf8Str(bstrLocation.raw()).c_str()));
1393 fSupported = false;
1394 }
1395
1396 /* Check whether medium is represented by file on the disk or not */
1397 if (fSupported)
1398 {
1399 ComObjPtr<Medium> pObjMedium = (Medium *)(IMedium *)pMedium;
1400 fSupported = pObjMedium->i_isMediumFormatFile();
1401 if (!fSupported)
1402 {
1403 LogRelFunc(("Skipping medium %s because it's not a real file on the disk.\n",
1404 Utf8Str(bstrLocation.raw()).c_str()));
1405 }
1406 }
1407
1408 /* some special checks for DVD */
1409 if (fSupported && deviceType == DeviceType_DVD)
1410 {
1411 Utf8Str ext = bstrLocation;
1412 ext.assignEx(RTPathSuffix(ext.c_str()));//returns extension with dot (".iso")
1413
1414 //only ISO image is moved. Otherwise adding some information into log file
1415 int equality = ext.compare(".iso", Utf8Str::CaseInsensitive);
1416 if (equality != false)
1417 {
1418 LogRelFunc(("Skipping file %s. Only ISO images are supported for now.\n",
1419 Utf8Str(bstrLocation.raw()).c_str()));
1420 fSupported = false;
1421 }
1422 }
1423
1424 return fSupported;
1425}
Note: See TracBrowser for help on using the repository browser.

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