VirtualBox

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

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

bugref:8345. replace wrong usage Utf8Str::contains() by RTPathStartsWith().

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

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