VirtualBox

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

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

bugref:8345. Fixed wrong manipulation with iterator.

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