VirtualBox

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

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

bugref:8345. Fixed trailing blanks.

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