VirtualBox

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

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

bugref:8345. Added a check into MachineMoveVM::updatePathsToStateFiles().

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

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