VirtualBox

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

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

bugref:8345. Added a check for read-only destination.

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