VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MediumImpl.cpp@ 98035

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

reimplemented changes for bugref:10180 after seperating the changes from bugref:4787

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 391.4 KB
 
1/* $Id: MediumImpl.cpp 98035 2023-01-10 06:35:56Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2008-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.alldomusa.eu.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MEDIUM
29#include "MediumImpl.h"
30#include "MediumIOImpl.h"
31#include "TokenImpl.h"
32#include "ProgressImpl.h"
33#include "SystemPropertiesImpl.h"
34#include "VirtualBoxImpl.h"
35#include "ExtPackManagerImpl.h"
36
37#include "AutoCaller.h"
38#include "Global.h"
39#include "LoggingNew.h"
40#include "ThreadTask.h"
41#include "VBox/com/MultiResult.h"
42#include "VBox/com/ErrorInfo.h"
43
44#include <VBox/err.h>
45#include <VBox/settings.h>
46
47#include <iprt/param.h>
48#include <iprt/path.h>
49#include <iprt/file.h>
50#include <iprt/cpp/utils.h>
51#include <iprt/memsafer.h>
52#include <iprt/base64.h>
53#include <iprt/vfs.h>
54#include <iprt/fsvfs.h>
55
56#include <VBox/vd.h>
57
58#include <algorithm>
59#include <list>
60#include <set>
61#include <map>
62
63
64typedef std::list<Guid> GuidList;
65
66
67#ifdef VBOX_WITH_EXTPACK
68static const char g_szVDPlugin[] = "VDPluginCrypt";
69#endif
70
71
72////////////////////////////////////////////////////////////////////////////////
73//
74// Medium data definition
75//
76////////////////////////////////////////////////////////////////////////////////
77#if __cplusplus < 201700 && RT_GNUC_PREREQ(11,0) /* gcc/libstdc++ 12.1.1 errors out here because unary_function is deprecated */
78# pragma GCC diagnostic push
79# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
80#endif
81
82struct SnapshotRef
83{
84 /** Equality predicate for stdc++. */
85 struct EqualsTo
86#if __cplusplus < 201700 /* deprecated in C++11, removed in C++17. */
87 : public std::unary_function <SnapshotRef, bool>
88#endif
89 {
90 explicit EqualsTo(const Guid &aSnapshotId) : snapshotId(aSnapshotId) {}
91
92#if __cplusplus < 201700 /* deprecated in C++11, removed in C++17. */
93 bool operator()(const argument_type &aThat) const
94#else
95 bool operator()(const SnapshotRef &aThat) const
96#endif
97 {
98 return aThat.snapshotId == snapshotId;
99 }
100
101 const Guid snapshotId;
102 };
103
104 SnapshotRef(const Guid &aSnapshotId,
105 const int &aRefCnt = 1)
106 : snapshotId(aSnapshotId),
107 iRefCnt(aRefCnt) {}
108
109 Guid snapshotId;
110 /*
111 * The number of attachments of the medium in the same snapshot.
112 * Used for MediumType_Readonly. It is always equal to 1 for other types.
113 * Usual int is used because any changes in the BackRef are guarded by
114 * AutoWriteLock.
115 */
116 int iRefCnt;
117};
118
119/** Describes how a machine refers to this medium. */
120struct BackRef
121{
122 /** Equality predicate for stdc++. */
123 struct EqualsTo
124#if __cplusplus < 201700 /* deprecated in C++11, removed in C++17. */
125 : public std::unary_function <BackRef, bool>
126#endif
127 {
128 explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
129
130#if __cplusplus < 201700 /* deprecated in C++11, removed in C++17. */
131 bool operator()(const argument_type &aThat) const
132#else
133 bool operator()(const BackRef &aThat) const
134#endif
135 {
136 return aThat.machineId == machineId;
137 }
138
139 const Guid machineId;
140 };
141
142 BackRef(const Guid &aMachineId,
143 const Guid &aSnapshotId = Guid::Empty)
144 : machineId(aMachineId),
145 iRefCnt(1),
146 fInCurState(aSnapshotId.isZero())
147 {
148 if (aSnapshotId.isValid() && !aSnapshotId.isZero())
149 llSnapshotIds.push_back(SnapshotRef(aSnapshotId));
150 }
151
152 Guid machineId;
153 /*
154 * The number of attachments of the medium in the same machine.
155 * Used for MediumType_Readonly. It is always equal to 1 for other types.
156 * Usual int is used because any changes in the BackRef are guarded by
157 * AutoWriteLock.
158 */
159 int iRefCnt;
160 bool fInCurState : 1;
161 std::list<SnapshotRef> llSnapshotIds;
162};
163
164typedef std::list<BackRef> BackRefList;
165
166#if __cplusplus < 201700 && RT_GNUC_PREREQ(11,0)
167# pragma GCC diagnostic pop
168#endif
169
170
171struct Medium::Data
172{
173 Data()
174 : pVirtualBox(NULL),
175 state(MediumState_NotCreated),
176 variant(MediumVariant_Standard),
177 size(0),
178 readers(0),
179 preLockState(MediumState_NotCreated),
180 queryInfoSem(LOCKCLASS_MEDIUMQUERY),
181 queryInfoRunning(false),
182 type(MediumType_Normal),
183 devType(DeviceType_HardDisk),
184 logicalSize(0),
185 hddOpenMode(OpenReadWrite),
186 autoReset(false),
187 hostDrive(false),
188 implicit(false),
189 fClosing(false),
190 uOpenFlagsDef(VD_OPEN_FLAGS_IGNORE_FLUSH),
191 numCreateDiffTasks(0),
192 vdDiskIfaces(NULL),
193 vdImageIfaces(NULL),
194 fMoveThisMedium(false)
195 { }
196
197 /** weak VirtualBox parent */
198 VirtualBox * const pVirtualBox;
199
200 // pParent and llChildren are protected by VirtualBox::i_getMediaTreeLockHandle()
201 ComObjPtr<Medium> pParent;
202 MediaList llChildren; // to add a child, just call push_back; to remove
203 // a child, call child->deparent() which does a lookup
204
205 GuidList llRegistryIDs; // media registries in which this medium is listed
206
207 const Guid id;
208 Utf8Str strDescription;
209 MediumState_T state;
210 MediumVariant_T variant;
211 Utf8Str strLocationFull;
212 uint64_t size;
213 Utf8Str strLastAccessError;
214
215 BackRefList backRefs;
216
217 size_t readers;
218 MediumState_T preLockState;
219
220 /** Special synchronization for operations which must wait for
221 * Medium::i_queryInfo in another thread to complete. Using a SemRW is
222 * not quite ideal, but at least it is subject to the lock validator,
223 * unlike the SemEventMulti which we had here for many years. Catching
224 * possible deadlocks is more important than a tiny bit of efficiency. */
225 RWLockHandle queryInfoSem;
226 bool queryInfoRunning : 1;
227
228 const Utf8Str strFormat;
229 ComObjPtr<MediumFormat> formatObj;
230
231 MediumType_T type;
232 DeviceType_T devType;
233 uint64_t logicalSize;
234
235 HDDOpenMode hddOpenMode;
236
237 bool autoReset : 1;
238
239 /** New UUID to be set on the next Medium::i_queryInfo call. */
240 const Guid uuidImage;
241 /** New parent UUID to be set on the next Medium::i_queryInfo call. */
242 const Guid uuidParentImage;
243
244/** @todo r=bird: the boolean bitfields are pointless if they're not grouped! */
245 bool hostDrive : 1;
246
247 settings::StringsMap mapProperties;
248
249 bool implicit : 1;
250 /** Flag whether the medium is in the process of being closed. */
251 bool fClosing: 1;
252
253 /** Default flags passed to VDOpen(). */
254 unsigned uOpenFlagsDef;
255
256 uint32_t numCreateDiffTasks;
257
258 Utf8Str vdError; /*< Error remembered by the VD error callback. */
259
260 VDINTERFACEERROR vdIfError;
261
262 VDINTERFACECONFIG vdIfConfig;
263
264 /** The handle to the default VD TCP/IP interface. */
265 VDIFINST hTcpNetInst;
266
267 PVDINTERFACE vdDiskIfaces;
268 PVDINTERFACE vdImageIfaces;
269
270 /** Flag if the medium is going to move to a new
271 * location. */
272 bool fMoveThisMedium;
273 /** new location path */
274 Utf8Str strNewLocationFull;
275};
276
277typedef struct VDSOCKETINT
278{
279 /** Socket handle. */
280 RTSOCKET hSocket;
281} VDSOCKETINT, *PVDSOCKETINT;
282
283////////////////////////////////////////////////////////////////////////////////
284//
285// Globals
286//
287////////////////////////////////////////////////////////////////////////////////
288
289/**
290 * Medium::Task class for asynchronous operations.
291 *
292 * @note Instances of this class must be created using new() because the
293 * task thread function will delete them when the task is complete.
294 *
295 * @note The constructor of this class adds a caller on the managed Medium
296 * object which is automatically released upon destruction.
297 */
298class Medium::Task : public ThreadTask
299{
300public:
301 Task(Medium *aMedium, Progress *aProgress, bool fNotifyAboutChanges = true)
302 : ThreadTask("Medium::Task"),
303 mVDOperationIfaces(NULL),
304 mMedium(aMedium),
305 mMediumCaller(aMedium),
306 mProgress(aProgress),
307 mVirtualBoxCaller(NULL),
308 mNotifyAboutChanges(fNotifyAboutChanges)
309 {
310 AssertReturnVoidStmt(aMedium, mRC = E_FAIL);
311 mRC = mMediumCaller.rc();
312 if (FAILED(mRC))
313 return;
314
315 /* Get strong VirtualBox reference, see below. */
316 VirtualBox *pVirtualBox = aMedium->m->pVirtualBox;
317 mVirtualBox = pVirtualBox;
318 mVirtualBoxCaller.attach(pVirtualBox);
319 mRC = mVirtualBoxCaller.rc();
320 if (FAILED(mRC))
321 return;
322
323 /* Set up a per-operation progress interface, can be used freely (for
324 * binary operations you can use it either on the source or target). */
325 if (mProgress)
326 {
327 mVDIfProgress.pfnProgress = aProgress->i_vdProgressCallback;
328 int vrc = VDInterfaceAdd(&mVDIfProgress.Core,
329 "Medium::Task::vdInterfaceProgress",
330 VDINTERFACETYPE_PROGRESS,
331 mProgress,
332 sizeof(mVDIfProgress),
333 &mVDOperationIfaces);
334 AssertRC(vrc);
335 if (RT_FAILURE(vrc))
336 mRC = E_FAIL;
337 }
338 }
339
340 // Make all destructors virtual. Just in case.
341 virtual ~Task()
342 {
343 /* send the notification of completion.*/
344 if ( isAsync()
345 && !mProgress.isNull())
346 mProgress->i_notifyComplete(mRC);
347 }
348
349 HRESULT rc() const { return mRC; }
350 bool isOk() const { return SUCCEEDED(rc()); }
351 bool NotifyAboutChanges() const { return mNotifyAboutChanges; }
352
353 const ComPtr<Progress>& GetProgressObject() const {return mProgress;}
354
355 /**
356 * Runs Medium::Task::executeTask() on the current thread
357 * instead of creating a new one.
358 */
359 HRESULT runNow()
360 {
361 LogFlowFuncEnter();
362
363 mRC = executeTask();
364
365 LogFlowFunc(("rc=%Rhrc\n", mRC));
366 LogFlowFuncLeave();
367 return mRC;
368 }
369
370 /**
371 * Implementation code for the "create base" task.
372 * Used as function for execution from a standalone thread.
373 */
374 void handler()
375 {
376 LogFlowFuncEnter();
377 try
378 {
379 mRC = executeTask(); /* (destructor picks up mRC, see above) */
380 LogFlowFunc(("rc=%Rhrc\n", mRC));
381 }
382 catch (...)
383 {
384 LogRel(("Some exception in the function Medium::Task:handler()\n"));
385 }
386
387 LogFlowFuncLeave();
388 }
389
390 PVDINTERFACE mVDOperationIfaces;
391
392 const ComObjPtr<Medium> mMedium;
393 AutoCaller mMediumCaller;
394
395protected:
396 HRESULT mRC;
397
398private:
399 virtual HRESULT executeTask() = 0;
400
401 const ComObjPtr<Progress> mProgress;
402
403 VDINTERFACEPROGRESS mVDIfProgress;
404
405 /* Must have a strong VirtualBox reference during a task otherwise the
406 * reference count might drop to 0 while a task is still running. This
407 * would result in weird behavior, including deadlocks due to uninit and
408 * locking order issues. The deadlock often is not detectable because the
409 * uninit uses event semaphores which sabotages deadlock detection. */
410 ComObjPtr<VirtualBox> mVirtualBox;
411 AutoCaller mVirtualBoxCaller;
412 bool mNotifyAboutChanges;
413};
414
415class Medium::CreateBaseTask : public Medium::Task
416{
417public:
418 CreateBaseTask(Medium *aMedium,
419 Progress *aProgress,
420 uint64_t aSize,
421 MediumVariant_T aVariant,
422 bool fNotifyAboutChanges = true)
423 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
424 mSize(aSize),
425 mVariant(aVariant)
426 {
427 m_strTaskName = "createBase";
428 }
429
430 uint64_t mSize;
431 MediumVariant_T mVariant;
432
433private:
434 HRESULT executeTask()
435 {
436 return mMedium->i_taskCreateBaseHandler(*this);
437 }
438};
439
440class Medium::CreateDiffTask : public Medium::Task
441{
442public:
443 CreateDiffTask(Medium *aMedium,
444 Progress *aProgress,
445 Medium *aTarget,
446 MediumVariant_T aVariant,
447 MediumLockList *aMediumLockList,
448 bool fKeepMediumLockList = false,
449 bool fNotifyAboutChanges = true)
450 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
451 mpMediumLockList(aMediumLockList),
452 mTarget(aTarget),
453 mVariant(aVariant),
454 mTargetCaller(aTarget),
455 mfKeepMediumLockList(fKeepMediumLockList)
456 {
457 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
458 mRC = mTargetCaller.rc();
459 if (FAILED(mRC))
460 return;
461 m_strTaskName = "createDiff";
462 }
463
464 ~CreateDiffTask()
465 {
466 if (!mfKeepMediumLockList && mpMediumLockList)
467 delete mpMediumLockList;
468 }
469
470 MediumLockList *mpMediumLockList;
471
472 const ComObjPtr<Medium> mTarget;
473 MediumVariant_T mVariant;
474
475private:
476 HRESULT executeTask()
477 {
478 return mMedium->i_taskCreateDiffHandler(*this);
479 }
480
481 AutoCaller mTargetCaller;
482 bool mfKeepMediumLockList;
483};
484
485class Medium::CloneTask : public Medium::Task
486{
487public:
488 CloneTask(Medium *aMedium,
489 Progress *aProgress,
490 Medium *aTarget,
491 MediumVariant_T aVariant,
492 Medium *aParent,
493 uint32_t idxSrcImageSame,
494 uint32_t idxDstImageSame,
495 MediumLockList *aSourceMediumLockList,
496 MediumLockList *aTargetMediumLockList,
497 bool fKeepSourceMediumLockList = false,
498 bool fKeepTargetMediumLockList = false,
499 bool fNotifyAboutChanges = true,
500 LONG64 aTargetLogicalSize = 0)
501 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
502 mTarget(aTarget),
503 mParent(aParent),
504 mTargetLogicalSize(aTargetLogicalSize),
505 mpSourceMediumLockList(aSourceMediumLockList),
506 mpTargetMediumLockList(aTargetMediumLockList),
507 mVariant(aVariant),
508 midxSrcImageSame(idxSrcImageSame),
509 midxDstImageSame(idxDstImageSame),
510 mTargetCaller(aTarget),
511 mParentCaller(aParent),
512 mfKeepSourceMediumLockList(fKeepSourceMediumLockList),
513 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
514
515 {
516 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
517 mRC = mTargetCaller.rc();
518 if (FAILED(mRC))
519 return;
520 /* aParent may be NULL */
521 mRC = mParentCaller.rc();
522 if (FAILED(mRC))
523 return;
524 AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
525 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
526 m_strTaskName = "createClone";
527 }
528
529 ~CloneTask()
530 {
531 if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
532 delete mpSourceMediumLockList;
533 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
534 delete mpTargetMediumLockList;
535 }
536
537 const ComObjPtr<Medium> mTarget;
538 const ComObjPtr<Medium> mParent;
539 LONG64 mTargetLogicalSize;
540 MediumLockList *mpSourceMediumLockList;
541 MediumLockList *mpTargetMediumLockList;
542 MediumVariant_T mVariant;
543 uint32_t midxSrcImageSame;
544 uint32_t midxDstImageSame;
545
546private:
547 HRESULT executeTask()
548 {
549 return mMedium->i_taskCloneHandler(*this);
550 }
551
552 AutoCaller mTargetCaller;
553 AutoCaller mParentCaller;
554 bool mfKeepSourceMediumLockList;
555 bool mfKeepTargetMediumLockList;
556};
557
558class Medium::MoveTask : public Medium::Task
559{
560public:
561 MoveTask(Medium *aMedium,
562 Progress *aProgress,
563 MediumVariant_T aVariant,
564 MediumLockList *aMediumLockList,
565 bool fKeepMediumLockList = false,
566 bool fNotifyAboutChanges = true)
567 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
568 mpMediumLockList(aMediumLockList),
569 mVariant(aVariant),
570 mfKeepMediumLockList(fKeepMediumLockList)
571 {
572 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
573 m_strTaskName = "createMove";
574 }
575
576 ~MoveTask()
577 {
578 if (!mfKeepMediumLockList && mpMediumLockList)
579 delete mpMediumLockList;
580 }
581
582 MediumLockList *mpMediumLockList;
583 MediumVariant_T mVariant;
584
585private:
586 HRESULT executeTask()
587 {
588 return mMedium->i_taskMoveHandler(*this);
589 }
590
591 bool mfKeepMediumLockList;
592};
593
594class Medium::CompactTask : public Medium::Task
595{
596public:
597 CompactTask(Medium *aMedium,
598 Progress *aProgress,
599 MediumLockList *aMediumLockList,
600 bool fKeepMediumLockList = false,
601 bool fNotifyAboutChanges = true)
602 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
603 mpMediumLockList(aMediumLockList),
604 mfKeepMediumLockList(fKeepMediumLockList)
605 {
606 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
607 m_strTaskName = "createCompact";
608 }
609
610 ~CompactTask()
611 {
612 if (!mfKeepMediumLockList && mpMediumLockList)
613 delete mpMediumLockList;
614 }
615
616 MediumLockList *mpMediumLockList;
617
618private:
619 HRESULT executeTask()
620 {
621 return mMedium->i_taskCompactHandler(*this);
622 }
623
624 bool mfKeepMediumLockList;
625};
626
627class Medium::ResizeTask : public Medium::Task
628{
629public:
630 ResizeTask(Medium *aMedium,
631 uint64_t aSize,
632 Progress *aProgress,
633 MediumLockList *aMediumLockList,
634 bool fKeepMediumLockList = false,
635 bool fNotifyAboutChanges = true)
636 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
637 mSize(aSize),
638 mpMediumLockList(aMediumLockList),
639 mfKeepMediumLockList(fKeepMediumLockList)
640 {
641 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
642 m_strTaskName = "createResize";
643 }
644
645 ~ResizeTask()
646 {
647 if (!mfKeepMediumLockList && mpMediumLockList)
648 delete mpMediumLockList;
649 }
650
651 uint64_t mSize;
652 MediumLockList *mpMediumLockList;
653
654private:
655 HRESULT executeTask()
656 {
657 return mMedium->i_taskResizeHandler(*this);
658 }
659
660 bool mfKeepMediumLockList;
661};
662
663class Medium::ResetTask : public Medium::Task
664{
665public:
666 ResetTask(Medium *aMedium,
667 Progress *aProgress,
668 MediumLockList *aMediumLockList,
669 bool fKeepMediumLockList = false,
670 bool fNotifyAboutChanges = true)
671 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
672 mpMediumLockList(aMediumLockList),
673 mfKeepMediumLockList(fKeepMediumLockList)
674 {
675 m_strTaskName = "createReset";
676 }
677
678 ~ResetTask()
679 {
680 if (!mfKeepMediumLockList && mpMediumLockList)
681 delete mpMediumLockList;
682 }
683
684 MediumLockList *mpMediumLockList;
685
686private:
687 HRESULT executeTask()
688 {
689 return mMedium->i_taskResetHandler(*this);
690 }
691
692 bool mfKeepMediumLockList;
693};
694
695class Medium::DeleteTask : public Medium::Task
696{
697public:
698 DeleteTask(Medium *aMedium,
699 Progress *aProgress,
700 MediumLockList *aMediumLockList,
701 bool fKeepMediumLockList = false,
702 bool fNotifyAboutChanges = true)
703 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
704 mpMediumLockList(aMediumLockList),
705 mfKeepMediumLockList(fKeepMediumLockList)
706 {
707 m_strTaskName = "createDelete";
708 }
709
710 ~DeleteTask()
711 {
712 if (!mfKeepMediumLockList && mpMediumLockList)
713 delete mpMediumLockList;
714 }
715
716 MediumLockList *mpMediumLockList;
717
718private:
719 HRESULT executeTask()
720 {
721 return mMedium->i_taskDeleteHandler(*this);
722 }
723
724 bool mfKeepMediumLockList;
725};
726
727class Medium::MergeTask : public Medium::Task
728{
729public:
730 MergeTask(Medium *aMedium,
731 Medium *aTarget,
732 bool fMergeForward,
733 Medium *aParentForTarget,
734 MediumLockList *aChildrenToReparent,
735 Progress *aProgress,
736 MediumLockList *aMediumLockList,
737 bool fKeepMediumLockList = false,
738 bool fNotifyAboutChanges = true)
739 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
740 mTarget(aTarget),
741 mfMergeForward(fMergeForward),
742 mParentForTarget(aParentForTarget),
743 mpChildrenToReparent(aChildrenToReparent),
744 mpMediumLockList(aMediumLockList),
745 mTargetCaller(aTarget),
746 mParentForTargetCaller(aParentForTarget),
747 mfKeepMediumLockList(fKeepMediumLockList)
748 {
749 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
750 m_strTaskName = "createMerge";
751 }
752
753 ~MergeTask()
754 {
755 if (!mfKeepMediumLockList && mpMediumLockList)
756 delete mpMediumLockList;
757 if (mpChildrenToReparent)
758 delete mpChildrenToReparent;
759 }
760
761 const ComObjPtr<Medium> mTarget;
762 bool mfMergeForward;
763 /* When mpChildrenToReparent is null then mParentForTarget is non-null and
764 * vice versa. In other words: they are used in different cases. */
765 const ComObjPtr<Medium> mParentForTarget;
766 MediumLockList *mpChildrenToReparent;
767 MediumLockList *mpMediumLockList;
768
769private:
770 HRESULT executeTask()
771 {
772 return mMedium->i_taskMergeHandler(*this);
773 }
774
775 AutoCaller mTargetCaller;
776 AutoCaller mParentForTargetCaller;
777 bool mfKeepMediumLockList;
778};
779
780class Medium::ImportTask : public Medium::Task
781{
782public:
783 ImportTask(Medium *aMedium,
784 Progress *aProgress,
785 const char *aFilename,
786 MediumFormat *aFormat,
787 MediumVariant_T aVariant,
788 RTVFSIOSTREAM aVfsIosSrc,
789 Medium *aParent,
790 MediumLockList *aTargetMediumLockList,
791 bool fKeepTargetMediumLockList = false,
792 bool fNotifyAboutChanges = true)
793 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
794 mFilename(aFilename),
795 mFormat(aFormat),
796 mVariant(aVariant),
797 mParent(aParent),
798 mpTargetMediumLockList(aTargetMediumLockList),
799 mpVfsIoIf(NULL),
800 mParentCaller(aParent),
801 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
802 {
803 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
804 /* aParent may be NULL */
805 mRC = mParentCaller.rc();
806 if (FAILED(mRC))
807 return;
808
809 mVDImageIfaces = aMedium->m->vdImageIfaces;
810
811 int vrc = VDIfCreateFromVfsStream(aVfsIosSrc, RTFILE_O_READ, &mpVfsIoIf);
812 AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
813
814 vrc = VDInterfaceAdd(&mpVfsIoIf->Core, "Medium::ImportTaskVfsIos",
815 VDINTERFACETYPE_IO, mpVfsIoIf,
816 sizeof(VDINTERFACEIO), &mVDImageIfaces);
817 AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
818 m_strTaskName = "createImport";
819 }
820
821 ~ImportTask()
822 {
823 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
824 delete mpTargetMediumLockList;
825 if (mpVfsIoIf)
826 {
827 VDIfDestroyFromVfsStream(mpVfsIoIf);
828 mpVfsIoIf = NULL;
829 }
830 }
831
832 Utf8Str mFilename;
833 ComObjPtr<MediumFormat> mFormat;
834 MediumVariant_T mVariant;
835 const ComObjPtr<Medium> mParent;
836 MediumLockList *mpTargetMediumLockList;
837 PVDINTERFACE mVDImageIfaces;
838 PVDINTERFACEIO mpVfsIoIf; /**< Pointer to the VFS I/O stream to VD I/O interface wrapper. */
839
840private:
841 HRESULT executeTask()
842 {
843 return mMedium->i_taskImportHandler(*this);
844 }
845
846 AutoCaller mParentCaller;
847 bool mfKeepTargetMediumLockList;
848};
849
850class Medium::EncryptTask : public Medium::Task
851{
852public:
853 EncryptTask(Medium *aMedium,
854 const com::Utf8Str &strNewPassword,
855 const com::Utf8Str &strCurrentPassword,
856 const com::Utf8Str &strCipher,
857 const com::Utf8Str &strNewPasswordId,
858 Progress *aProgress,
859 MediumLockList *aMediumLockList)
860 : Medium::Task(aMedium, aProgress, false),
861 mstrNewPassword(strNewPassword),
862 mstrCurrentPassword(strCurrentPassword),
863 mstrCipher(strCipher),
864 mstrNewPasswordId(strNewPasswordId),
865 mpMediumLockList(aMediumLockList)
866 {
867 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
868 /* aParent may be NULL */
869 mRC = mParentCaller.rc();
870 if (FAILED(mRC))
871 return;
872
873 mVDImageIfaces = aMedium->m->vdImageIfaces;
874 m_strTaskName = "createEncrypt";
875 }
876
877 ~EncryptTask()
878 {
879 if (mstrNewPassword.length())
880 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
881 if (mstrCurrentPassword.length())
882 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
883
884 /* Keep any errors which might be set when deleting the lock list. */
885 ErrorInfoKeeper eik;
886 delete mpMediumLockList;
887 }
888
889 Utf8Str mstrNewPassword;
890 Utf8Str mstrCurrentPassword;
891 Utf8Str mstrCipher;
892 Utf8Str mstrNewPasswordId;
893 MediumLockList *mpMediumLockList;
894 PVDINTERFACE mVDImageIfaces;
895
896private:
897 HRESULT executeTask()
898 {
899 return mMedium->i_taskEncryptHandler(*this);
900 }
901
902 AutoCaller mParentCaller;
903};
904
905
906
907/**
908 * Converts the Medium device type to the VD type.
909 */
910static const char *getVDTypeName(VDTYPE enmType)
911{
912 switch (enmType)
913 {
914 case VDTYPE_HDD: return "HDD";
915 case VDTYPE_OPTICAL_DISC: return "DVD";
916 case VDTYPE_FLOPPY: return "floppy";
917 case VDTYPE_INVALID: return "invalid";
918 default:
919 AssertFailedReturn("unknown");
920 }
921}
922
923/**
924 * Converts the Medium device type to the VD type.
925 */
926static const char *getDeviceTypeName(DeviceType_T enmType)
927{
928 switch (enmType)
929 {
930 case DeviceType_HardDisk: return "HDD";
931 case DeviceType_DVD: return "DVD";
932 case DeviceType_Floppy: return "floppy";
933 case DeviceType_Null: return "null";
934 case DeviceType_Network: return "network";
935 case DeviceType_USB: return "USB";
936 case DeviceType_SharedFolder: return "shared folder";
937 case DeviceType_Graphics3D: return "graphics 3d";
938 default:
939 AssertFailedReturn("unknown");
940 }
941}
942
943
944
945////////////////////////////////////////////////////////////////////////////////
946//
947// Medium constructor / destructor
948//
949////////////////////////////////////////////////////////////////////////////////
950
951DEFINE_EMPTY_CTOR_DTOR(Medium)
952
953HRESULT Medium::FinalConstruct()
954{
955 m = new Data;
956
957 /* Initialize the callbacks of the VD error interface */
958 m->vdIfError.pfnError = i_vdErrorCall;
959 m->vdIfError.pfnMessage = NULL;
960
961 /* Initialize the callbacks of the VD config interface */
962 m->vdIfConfig.pfnAreKeysValid = i_vdConfigAreKeysValid;
963 m->vdIfConfig.pfnQuerySize = i_vdConfigQuerySize;
964 m->vdIfConfig.pfnQuery = i_vdConfigQuery;
965 m->vdIfConfig.pfnUpdate = i_vdConfigUpdate;
966 m->vdIfConfig.pfnQueryBytes = NULL;
967
968 /* Initialize the per-disk interface chain (could be done more globally,
969 * but it's not wasting much time or space so it's not worth it). */
970 int vrc;
971 vrc = VDInterfaceAdd(&m->vdIfError.Core,
972 "Medium::vdInterfaceError",
973 VDINTERFACETYPE_ERROR, this,
974 sizeof(VDINTERFACEERROR), &m->vdDiskIfaces);
975 AssertRCReturn(vrc, E_FAIL);
976
977 /* Initialize the per-image interface chain */
978 vrc = VDInterfaceAdd(&m->vdIfConfig.Core,
979 "Medium::vdInterfaceConfig",
980 VDINTERFACETYPE_CONFIG, this,
981 sizeof(VDINTERFACECONFIG), &m->vdImageIfaces);
982 AssertRCReturn(vrc, E_FAIL);
983
984 /* Initialize the callbacks of the VD TCP interface (we always use the host
985 * IP stack for now) */
986 vrc = VDIfTcpNetInstDefaultCreate(&m->hTcpNetInst, &m->vdImageIfaces);
987 AssertRCReturn(vrc, E_FAIL);
988
989 return BaseFinalConstruct();
990}
991
992void Medium::FinalRelease()
993{
994 uninit();
995
996 VDIfTcpNetInstDefaultDestroy(m->hTcpNetInst);
997 delete m;
998
999 BaseFinalRelease();
1000}
1001
1002/**
1003 * Initializes an empty hard disk object without creating or opening an associated
1004 * storage unit.
1005 *
1006 * This gets called by VirtualBox::CreateMedium() in which case uuidMachineRegistry
1007 * is empty since starting with VirtualBox 4.0, we no longer add opened media to a
1008 * registry automatically (this is deferred until the medium is attached to a machine).
1009 *
1010 * This also gets called when VirtualBox creates diff images; in this case uuidMachineRegistry
1011 * is set to the registry of the parent image to make sure they all end up in the same
1012 * file.
1013 *
1014 * For hard disks that don't have the MediumFormatCapabilities_CreateFixed or
1015 * MediumFormatCapabilities_CreateDynamic capability (and therefore cannot be created or deleted
1016 * with the means of VirtualBox) the associated storage unit is assumed to be
1017 * ready for use so the state of the hard disk object will be set to Created.
1018 *
1019 * @param aVirtualBox VirtualBox object.
1020 * @param aFormat
1021 * @param aLocation Storage unit location.
1022 * @param uuidMachineRegistry The registry to which this medium should be added
1023 * (global registry UUID or machine UUID or empty if none).
1024 * @param aDeviceType Device Type.
1025 */
1026HRESULT Medium::init(VirtualBox *aVirtualBox,
1027 const Utf8Str &aFormat,
1028 const Utf8Str &aLocation,
1029 const Guid &uuidMachineRegistry,
1030 const DeviceType_T aDeviceType)
1031{
1032 AssertReturn(aVirtualBox != NULL, E_FAIL);
1033 AssertReturn(!aFormat.isEmpty(), E_FAIL);
1034
1035 /* Enclose the state transition NotReady->InInit->Ready */
1036 AutoInitSpan autoInitSpan(this);
1037 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1038
1039 HRESULT rc = S_OK;
1040
1041 unconst(m->pVirtualBox) = aVirtualBox;
1042
1043 if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
1044 m->llRegistryIDs.push_back(uuidMachineRegistry);
1045
1046 /* no storage yet */
1047 m->state = MediumState_NotCreated;
1048
1049 /* cannot be a host drive */
1050 m->hostDrive = false;
1051
1052 m->devType = aDeviceType;
1053
1054 /* No storage unit is created yet, no need to call Medium::i_queryInfo */
1055
1056 rc = i_setFormat(aFormat);
1057 if (FAILED(rc)) return rc;
1058
1059 rc = i_setLocation(aLocation);
1060 if (FAILED(rc)) return rc;
1061
1062 if (!(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateFixed
1063 | MediumFormatCapabilities_CreateDynamic
1064 | MediumFormatCapabilities_File))
1065 )
1066 {
1067 /* Storage for mediums of this format can neither be explicitly
1068 * created by VirtualBox nor deleted, so we place the medium to
1069 * Inaccessible state here and also add it to the registry. The
1070 * state means that one has to use RefreshState() to update the
1071 * medium format specific fields. */
1072 m->state = MediumState_Inaccessible;
1073 // create new UUID
1074 unconst(m->id).create();
1075
1076 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1077 ComObjPtr<Medium> pMedium;
1078
1079 /*
1080 * Check whether the UUID is taken already and create a new one
1081 * if required.
1082 * Try this only a limited amount of times in case the PRNG is broken
1083 * in some way to prevent an endless loop.
1084 */
1085 for (unsigned i = 0; i < 5; i++)
1086 {
1087 bool fInUse;
1088
1089 fInUse = m->pVirtualBox->i_isMediaUuidInUse(m->id, aDeviceType);
1090 if (fInUse)
1091 {
1092 // create new UUID
1093 unconst(m->id).create();
1094 }
1095 else
1096 break;
1097 }
1098
1099 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
1100 Assert(this == pMedium || FAILED(rc));
1101 }
1102
1103 /* Confirm a successful initialization when it's the case */
1104 if (SUCCEEDED(rc))
1105 autoInitSpan.setSucceeded();
1106
1107 return rc;
1108}
1109
1110/**
1111 * Initializes the medium object by opening the storage unit at the specified
1112 * location. The enOpenMode parameter defines whether the medium will be opened
1113 * read/write or read-only.
1114 *
1115 * This gets called by VirtualBox::OpenMedium() and also by
1116 * Machine::AttachDevice() and createImplicitDiffs() when new diff
1117 * images are created.
1118 *
1119 * There is no registry for this case since starting with VirtualBox 4.0, we
1120 * no longer add opened media to a registry automatically (this is deferred
1121 * until the medium is attached to a machine).
1122 *
1123 * For hard disks, the UUID, format and the parent of this medium will be
1124 * determined when reading the medium storage unit. For DVD and floppy images,
1125 * which have no UUIDs in their storage units, new UUIDs are created.
1126 * If the detected or set parent is not known to VirtualBox, then this method
1127 * will fail.
1128 *
1129 * @param aVirtualBox VirtualBox object.
1130 * @param aLocation Storage unit location.
1131 * @param enOpenMode Whether to open the medium read/write or read-only.
1132 * @param fForceNewUuid Whether a new UUID should be set to avoid duplicates.
1133 * @param aDeviceType Device type of medium.
1134 */
1135HRESULT Medium::init(VirtualBox *aVirtualBox,
1136 const Utf8Str &aLocation,
1137 HDDOpenMode enOpenMode,
1138 bool fForceNewUuid,
1139 DeviceType_T aDeviceType)
1140{
1141 AssertReturn(aVirtualBox, E_INVALIDARG);
1142 AssertReturn(!aLocation.isEmpty(), E_INVALIDARG);
1143
1144 HRESULT rc = S_OK;
1145
1146 {
1147 /* Enclose the state transition NotReady->InInit->Ready */
1148 AutoInitSpan autoInitSpan(this);
1149 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1150
1151 unconst(m->pVirtualBox) = aVirtualBox;
1152
1153 /* there must be a storage unit */
1154 m->state = MediumState_Created;
1155
1156 /* remember device type for correct unregistering later */
1157 m->devType = aDeviceType;
1158
1159 /* cannot be a host drive */
1160 m->hostDrive = false;
1161
1162 /* remember the open mode (defaults to ReadWrite) */
1163 m->hddOpenMode = enOpenMode;
1164
1165 if (aDeviceType == DeviceType_DVD)
1166 m->type = MediumType_Readonly;
1167 else if (aDeviceType == DeviceType_Floppy)
1168 m->type = MediumType_Writethrough;
1169
1170 rc = i_setLocation(aLocation);
1171 if (FAILED(rc)) return rc;
1172
1173 /* get all the information about the medium from the storage unit */
1174 if (fForceNewUuid)
1175 unconst(m->uuidImage).create();
1176
1177 m->state = MediumState_Inaccessible;
1178 m->strLastAccessError = tr("Accessibility check was not yet performed");
1179
1180 /* Confirm a successful initialization before the call to i_queryInfo.
1181 * Otherwise we can end up with a AutoCaller deadlock because the
1182 * medium becomes visible but is not marked as initialized. Causes
1183 * locking trouble (e.g. trying to save media registries) which is
1184 * hard to solve. */
1185 autoInitSpan.setSucceeded();
1186 }
1187
1188 /* we're normal code from now on, no longer init */
1189 AutoCaller autoCaller(this);
1190 if (FAILED(autoCaller.rc()))
1191 return autoCaller.rc();
1192
1193 /* need to call i_queryInfo immediately to correctly place the medium in
1194 * the respective media tree and update other information such as uuid */
1195 rc = i_queryInfo(fForceNewUuid /* fSetImageId */, false /* fSetParentId */,
1196 autoCaller);
1197 if (SUCCEEDED(rc))
1198 {
1199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1200
1201 /* if the storage unit is not accessible, it's not acceptable for the
1202 * newly opened media so convert this into an error */
1203 if (m->state == MediumState_Inaccessible)
1204 {
1205 Assert(!m->strLastAccessError.isEmpty());
1206 rc = setError(E_FAIL, "%s", m->strLastAccessError.c_str());
1207 alock.release();
1208 autoCaller.release();
1209 uninit();
1210 }
1211 else
1212 {
1213 AssertStmt(!m->id.isZero(),
1214 alock.release(); autoCaller.release(); uninit(); return E_FAIL);
1215
1216 /* storage format must be detected by Medium::i_queryInfo if the
1217 * medium is accessible */
1218 AssertStmt(!m->strFormat.isEmpty(),
1219 alock.release(); autoCaller.release(); uninit(); return E_FAIL);
1220 }
1221 }
1222 else
1223 {
1224 /* opening this image failed, mark the object as dead */
1225 autoCaller.release();
1226 uninit();
1227 }
1228
1229 return rc;
1230}
1231
1232/**
1233 * Initializes the medium object by loading its data from the given settings
1234 * node. The medium will always be opened read/write.
1235 *
1236 * In this case, since we're loading from a registry, uuidMachineRegistry is
1237 * always set: it's either the global registry UUID or a machine UUID when
1238 * loading from a per-machine registry.
1239 *
1240 * @param aParent Parent medium disk or NULL for a root (base) medium.
1241 * @param aDeviceType Device type of the medium.
1242 * @param uuidMachineRegistry The registry to which this medium should be
1243 * added (global registry UUID or machine UUID).
1244 * @param data Configuration settings.
1245 * @param strMachineFolder The machine folder with which to resolve relative paths;
1246 * if empty, then we use the VirtualBox home directory
1247 *
1248 * @note Locks the medium tree for writing.
1249 */
1250HRESULT Medium::initOne(Medium *aParent,
1251 DeviceType_T aDeviceType,
1252 const Guid &uuidMachineRegistry,
1253 const Utf8Str &strMachineFolder,
1254 const settings::Medium &data)
1255{
1256 HRESULT rc;
1257
1258 if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
1259 m->llRegistryIDs.push_back(uuidMachineRegistry);
1260
1261 /* register with VirtualBox/parent early, since uninit() will
1262 * unconditionally unregister on failure */
1263 if (aParent)
1264 {
1265 // differencing medium: add to parent
1266 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1267 // no need to check maximum depth as settings reading did it
1268 i_setParent(aParent);
1269 }
1270
1271 /* see below why we don't call Medium::i_queryInfo (and therefore treat
1272 * the medium as inaccessible for now */
1273 m->state = MediumState_Inaccessible;
1274 m->strLastAccessError = tr("Accessibility check was not yet performed");
1275
1276 /* required */
1277 unconst(m->id) = data.uuid;
1278
1279 /* assume not a host drive */
1280 m->hostDrive = false;
1281
1282 /* optional */
1283 m->strDescription = data.strDescription;
1284
1285 /* required */
1286 if (aDeviceType == DeviceType_HardDisk)
1287 {
1288 AssertReturn(!data.strFormat.isEmpty(), E_FAIL);
1289 rc = i_setFormat(data.strFormat);
1290 if (FAILED(rc)) return rc;
1291 }
1292 else
1293 {
1294 /// @todo handle host drive settings here as well?
1295 if (!data.strFormat.isEmpty())
1296 rc = i_setFormat(data.strFormat);
1297 else
1298 rc = i_setFormat("RAW");
1299 if (FAILED(rc)) return rc;
1300 }
1301
1302 /* optional, only for diffs, default is false; we can only auto-reset
1303 * diff media so they must have a parent */
1304 if (aParent != NULL)
1305 m->autoReset = data.fAutoReset;
1306 else
1307 m->autoReset = false;
1308
1309 /* properties (after setting the format as it populates the map). Note that
1310 * if some properties are not supported but present in the settings file,
1311 * they will still be read and accessible (for possible backward
1312 * compatibility; we can also clean them up from the XML upon next
1313 * XML format version change if we wish) */
1314 for (settings::StringsMap::const_iterator it = data.properties.begin();
1315 it != data.properties.end();
1316 ++it)
1317 {
1318 const Utf8Str &name = it->first;
1319 const Utf8Str &value = it->second;
1320 m->mapProperties[name] = value;
1321 }
1322
1323 /* try to decrypt an optional iSCSI initiator secret */
1324 settings::StringsMap::const_iterator itCph = data.properties.find("InitiatorSecretEncrypted");
1325 if ( itCph != data.properties.end()
1326 && !itCph->second.isEmpty())
1327 {
1328 Utf8Str strPlaintext;
1329 int vrc = m->pVirtualBox->i_decryptSetting(&strPlaintext, itCph->second);
1330 if (RT_SUCCESS(vrc))
1331 m->mapProperties["InitiatorSecret"] = strPlaintext;
1332 }
1333
1334 Utf8Str strFull;
1335 if (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
1336 {
1337 // compose full path of the medium, if it's not fully qualified...
1338 // slightly convoluted logic here. If the caller has given us a
1339 // machine folder, then a relative path will be relative to that:
1340 if ( !strMachineFolder.isEmpty()
1341 && !RTPathStartsWithRoot(data.strLocation.c_str())
1342 )
1343 {
1344 strFull = strMachineFolder;
1345 strFull += RTPATH_SLASH;
1346 strFull += data.strLocation;
1347 }
1348 else
1349 {
1350 // Otherwise use the old VirtualBox "make absolute path" logic:
1351 int vrc = m->pVirtualBox->i_calculateFullPath(data.strLocation, strFull);
1352 if (RT_FAILURE(vrc))
1353 return Global::vboxStatusCodeToCOM(vrc);
1354 }
1355 }
1356 else
1357 strFull = data.strLocation;
1358
1359 rc = i_setLocation(strFull);
1360 if (FAILED(rc)) return rc;
1361
1362 if (aDeviceType == DeviceType_HardDisk)
1363 {
1364 /* type is only for base hard disks */
1365 if (m->pParent.isNull())
1366 m->type = data.hdType;
1367 }
1368 else if (aDeviceType == DeviceType_DVD)
1369 m->type = MediumType_Readonly;
1370 else
1371 m->type = MediumType_Writethrough;
1372
1373 /* remember device type for correct unregistering later */
1374 m->devType = aDeviceType;
1375
1376 LogFlowThisFunc(("m->strLocationFull='%s', m->strFormat=%s, m->id={%RTuuid}\n",
1377 m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw()));
1378
1379 return S_OK;
1380}
1381
1382/**
1383 * Initializes and registers the medium object and its children by loading its
1384 * data from the given settings node. The medium will always be opened
1385 * read/write.
1386 *
1387 * @todo r=bird: What's that stuff about 'always be opened read/write'?
1388 *
1389 * In this case, since we're loading from a registry, uuidMachineRegistry is
1390 * always set: it's either the global registry UUID or a machine UUID when
1391 * loading from a per-machine registry.
1392 *
1393 * The only caller is currently VirtualBox::initMedia().
1394 *
1395 * @param aVirtualBox VirtualBox object.
1396 * @param aDeviceType Device type of the medium.
1397 * @param uuidMachineRegistry The registry to which this medium should be added
1398 * (global registry UUID or machine UUID).
1399 * @param strMachineFolder The machine folder with which to resolve relative
1400 * paths; if empty, then we use the VirtualBox home directory
1401 * @param data Configuration settings.
1402 * @param mediaTreeLock Autolock.
1403 * @param uIdsForNotify List to be updated with newly registered media.
1404 *
1405 * @note Assumes that the medium tree lock is held for writing. May release
1406 * and lock it again. At the end it is always held.
1407 */
1408/* static */
1409HRESULT Medium::initFromSettings(VirtualBox *aVirtualBox,
1410 DeviceType_T aDeviceType,
1411 const Guid &uuidMachineRegistry,
1412 const Utf8Str &strMachineFolder,
1413 const settings::Medium &data,
1414 AutoWriteLock &mediaTreeLock,
1415 std::list<std::pair<Guid, DeviceType_T> > &uIdsForNotify)
1416{
1417 Assert(aVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
1418 AssertReturn(aVirtualBox, E_INVALIDARG);
1419
1420 HRESULT rc = S_OK;
1421
1422 MediaList llMediaTocleanup;
1423
1424 std::list<const settings::Medium *> llSettingsTodo;
1425 llSettingsTodo.push_back(&data);
1426 MediaList llParentsTodo;
1427 llParentsTodo.push_back(NULL);
1428
1429 while (!llSettingsTodo.empty())
1430 {
1431 const settings::Medium *current = llSettingsTodo.front();
1432 llSettingsTodo.pop_front();
1433 ComObjPtr<Medium> pParent = llParentsTodo.front();
1434 llParentsTodo.pop_front();
1435
1436 bool fReleasedMediaTreeLock = false;
1437 ComObjPtr<Medium> pMedium;
1438 rc = pMedium.createObject();
1439 if (FAILED(rc))
1440 break;
1441 ComObjPtr<Medium> pActualMedium(pMedium);
1442
1443 {
1444 AutoInitSpan autoInitSpan(pMedium);
1445 AssertBreakStmt(autoInitSpan.isOk(), rc = E_FAIL);
1446
1447 unconst(pMedium->m->pVirtualBox) = aVirtualBox;
1448 rc = pMedium->initOne(pParent, aDeviceType, uuidMachineRegistry, strMachineFolder, *current);
1449 if (FAILED(rc))
1450 break;
1451 rc = aVirtualBox->i_registerMedium(pActualMedium, &pActualMedium, mediaTreeLock, true /*fCalledFromMediumInit*/);
1452 if (FAILED(rc))
1453 break;
1454
1455 if (pActualMedium == pMedium)
1456 {
1457 /* It is a truly new medium, remember details for cleanup. */
1458 autoInitSpan.setSucceeded();
1459 llMediaTocleanup.push_front(pMedium);
1460 }
1461 else
1462 {
1463 /* Since the newly created medium was replaced by an already
1464 * known one when merging medium trees, we can immediately mark
1465 * it as failed. */
1466 autoInitSpan.setFailed();
1467 mediaTreeLock.release();
1468 fReleasedMediaTreeLock = true;
1469 }
1470 }
1471 if (fReleasedMediaTreeLock)
1472 {
1473 /* With the InitSpan out of the way it's safe to let the refcount
1474 * drop to 0 without causing uninit trouble. */
1475 pMedium.setNull();
1476 mediaTreeLock.acquire();
1477 }
1478
1479 /* create all children */
1480 std::list<settings::Medium>::const_iterator itBegin = current->llChildren.begin();
1481 std::list<settings::Medium>::const_iterator itEnd = current->llChildren.end();
1482 for (std::list<settings::Medium>::const_iterator it = itBegin; it != itEnd; ++it)
1483 {
1484 llSettingsTodo.push_back(&*it);
1485 llParentsTodo.push_back(pActualMedium);
1486 }
1487 }
1488
1489 if (SUCCEEDED(rc))
1490 {
1491 /* Check for consistency. */
1492 Assert(llSettingsTodo.size() == 0);
1493 Assert(llParentsTodo.size() == 0);
1494 /* Create the list of notifications, parent first. */
1495 MediaList::const_reverse_iterator itBegin = llMediaTocleanup.rbegin();
1496 MediaList::const_reverse_iterator itEnd = llMediaTocleanup.rend();
1497 for (MediaList::const_reverse_iterator it = itBegin; it != itEnd; --it)
1498 {
1499 ComObjPtr<Medium> pMedium = *it;
1500 AutoCaller mediumCaller(pMedium);
1501 if (FAILED(mediumCaller.rc())) continue;
1502 const Guid &id = pMedium->i_getId();
1503 uIdsForNotify.push_back(std::pair<Guid, DeviceType_T>(id, aDeviceType));
1504 }
1505 }
1506 else
1507 {
1508 /* Forget state of the settings processing. */
1509 llSettingsTodo.clear();
1510 llParentsTodo.clear();
1511 /* Unregister all accumulated medium objects in the right order (last
1512 * created to first created, avoiding config leftovers). */
1513 MediaList::const_iterator itBegin = llMediaTocleanup.begin();
1514 MediaList::const_iterator itEnd = llMediaTocleanup.end();
1515 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
1516 {
1517 ComObjPtr<Medium> pMedium = *it;
1518 pMedium->i_unregisterWithVirtualBox();
1519 }
1520 /* Forget the only references to all newly created medium objects,
1521 * triggering freeing (uninit happened in unregistering above). */
1522 mediaTreeLock.release();
1523 llMediaTocleanup.clear();
1524 mediaTreeLock.acquire();
1525 }
1526
1527 return rc;
1528}
1529
1530/**
1531 * Initializes the medium object by providing the host drive information.
1532 * Not used for anything but the host floppy/host DVD case.
1533 *
1534 * There is no registry for this case.
1535 *
1536 * @param aVirtualBox VirtualBox object.
1537 * @param aDeviceType Device type of the medium.
1538 * @param aLocation Location of the host drive.
1539 * @param aDescription Comment for this host drive.
1540 *
1541 * @note Locks VirtualBox lock for writing.
1542 */
1543HRESULT Medium::init(VirtualBox *aVirtualBox,
1544 DeviceType_T aDeviceType,
1545 const Utf8Str &aLocation,
1546 const Utf8Str &aDescription /* = Utf8Str::Empty */)
1547{
1548 ComAssertRet(aDeviceType == DeviceType_DVD || aDeviceType == DeviceType_Floppy, E_INVALIDARG);
1549 ComAssertRet(!aLocation.isEmpty(), E_INVALIDARG);
1550
1551 /* Enclose the state transition NotReady->InInit->Ready */
1552 AutoInitSpan autoInitSpan(this);
1553 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1554
1555 unconst(m->pVirtualBox) = aVirtualBox;
1556
1557 // We do not store host drives in VirtualBox.xml or anywhere else, so if we want
1558 // host drives to be identifiable by UUID and not give the drive a different UUID
1559 // every time VirtualBox starts, we need to fake a reproducible UUID here:
1560 RTUUID uuid;
1561 RTUuidClear(&uuid);
1562 if (aDeviceType == DeviceType_DVD)
1563 memcpy(&uuid.au8[0], "DVD", 3);
1564 else
1565 memcpy(&uuid.au8[0], "FD", 2);
1566 /* use device name, adjusted to the end of uuid, shortened if necessary */
1567 size_t lenLocation = aLocation.length();
1568 if (lenLocation > 12)
1569 memcpy(&uuid.au8[4], aLocation.c_str() + (lenLocation - 12), 12);
1570 else
1571 memcpy(&uuid.au8[4 + 12 - lenLocation], aLocation.c_str(), lenLocation);
1572 unconst(m->id) = uuid;
1573
1574 if (aDeviceType == DeviceType_DVD)
1575 m->type = MediumType_Readonly;
1576 else
1577 m->type = MediumType_Writethrough;
1578 m->devType = aDeviceType;
1579 m->state = MediumState_Created;
1580 m->hostDrive = true;
1581 HRESULT rc = i_setFormat("RAW");
1582 if (FAILED(rc)) return rc;
1583 rc = i_setLocation(aLocation);
1584 if (FAILED(rc)) return rc;
1585 m->strDescription = aDescription;
1586
1587 autoInitSpan.setSucceeded();
1588 return S_OK;
1589}
1590
1591/**
1592 * Uninitializes the instance.
1593 *
1594 * Called either from FinalRelease() or by the parent when it gets destroyed.
1595 *
1596 * @note All children of this medium get uninitialized, too, in a stack
1597 * friendly manner.
1598 */
1599void Medium::uninit()
1600{
1601 /* It is possible that some previous/concurrent uninit has already cleared
1602 * the pVirtualBox reference, and in this case we don't need to continue.
1603 * Normally this would be handled through the AutoUninitSpan magic, however
1604 * this cannot be done at this point as the media tree must be locked
1605 * before reaching the AutoUninitSpan, otherwise deadlocks can happen.
1606 *
1607 * NOTE: The tree lock is higher priority than the medium caller and medium
1608 * object locks, i.e. the medium caller may have to be released and be
1609 * re-acquired in the right place later. See Medium::getParent() for sample
1610 * code how to do this safely. */
1611 VirtualBox *pVirtualBox = m->pVirtualBox;
1612 if (!pVirtualBox)
1613 return;
1614
1615 /* Caller must not hold the object (checked below) or media tree lock. */
1616 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
1617
1618 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1619
1620 /* Must use a list without refcounting help since "this" might already have
1621 * reached 0, and then the refcount must not be increased again since it
1622 * would otherwise trigger a double free. For all other list entries this
1623 * needs manual refcount updating, to make sure the refcount for children
1624 * does not drop to 0 too early. */
1625 std::list<Medium *> llMediaTodo;
1626 llMediaTodo.push_back(this);
1627
1628 while (!llMediaTodo.empty())
1629 {
1630 Medium *pMedium = llMediaTodo.front();
1631 llMediaTodo.pop_front();
1632
1633 /* Enclose the state transition Ready->InUninit->NotReady */
1634 AutoUninitSpan autoUninitSpan(pMedium);
1635 if (autoUninitSpan.uninitDone())
1636 {
1637 if (pMedium != this)
1638 pMedium->Release();
1639 continue;
1640 }
1641
1642 Assert(!pMedium->isWriteLockOnCurrentThread());
1643#ifdef DEBUG
1644 if (!pMedium->m->backRefs.empty())
1645 pMedium->i_dumpBackRefs();
1646#endif
1647 Assert(pMedium->m->backRefs.empty());
1648
1649 pMedium->m->formatObj.setNull();
1650
1651 if (pMedium->m->state == MediumState_Deleting)
1652 {
1653 /* This medium has been already deleted (directly or as part of a
1654 * merge). Reparenting has already been done. */
1655 Assert(pMedium->m->pParent.isNull());
1656 Assert(pMedium->m->llChildren.empty());
1657 if (pMedium != this)
1658 pMedium->Release();
1659 continue;
1660 }
1661
1662 //Assert(!pMedium->m->pParent);
1663 /** @todo r=klaus Should not be necessary, since the caller should be
1664 * doing the deparenting. No time right now to test everything. */
1665 if (pMedium == this && pMedium->m->pParent)
1666 pMedium->i_deparent();
1667
1668 /* Process all children */
1669 MediaList::const_iterator itBegin = pMedium->m->llChildren.begin();
1670 MediaList::const_iterator itEnd = pMedium->m->llChildren.end();
1671 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
1672 {
1673 Medium *pChild = *it;
1674 pChild->m->pParent.setNull();
1675 pChild->AddRef();
1676 llMediaTodo.push_back(pChild);
1677 }
1678
1679 /* Children information obsolete, will be processed anyway. */
1680 pMedium->m->llChildren.clear();
1681
1682 unconst(pMedium->m->pVirtualBox) = NULL;
1683
1684 if (pMedium != this)
1685 pMedium->Release();
1686
1687 autoUninitSpan.setSucceeded();
1688 }
1689}
1690
1691/**
1692 * Internal helper that removes "this" from the list of children of its
1693 * parent. Used in uninit() and other places when reparenting is necessary.
1694 *
1695 * The caller must hold the medium tree lock!
1696 */
1697void Medium::i_deparent()
1698{
1699 MediaList &llParent = m->pParent->m->llChildren;
1700 for (MediaList::iterator it = llParent.begin();
1701 it != llParent.end();
1702 ++it)
1703 {
1704 Medium *pParentsChild = *it;
1705 if (this == pParentsChild)
1706 {
1707 llParent.erase(it);
1708 break;
1709 }
1710 }
1711 m->pParent.setNull();
1712}
1713
1714/**
1715 * Internal helper that removes "this" from the list of children of its
1716 * parent. Used in uninit() and other places when reparenting is necessary.
1717 *
1718 * The caller must hold the medium tree lock!
1719 */
1720void Medium::i_setParent(const ComObjPtr<Medium> &pParent)
1721{
1722 m->pParent = pParent;
1723 if (pParent)
1724 pParent->m->llChildren.push_back(this);
1725}
1726
1727
1728////////////////////////////////////////////////////////////////////////////////
1729//
1730// IMedium public methods
1731//
1732////////////////////////////////////////////////////////////////////////////////
1733
1734HRESULT Medium::getId(com::Guid &aId)
1735{
1736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 aId = m->id;
1739
1740 return S_OK;
1741}
1742
1743HRESULT Medium::getDescription(AutoCaller &autoCaller, com::Utf8Str &aDescription)
1744{
1745 NOREF(autoCaller);
1746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1747
1748 aDescription = m->strDescription;
1749
1750 return S_OK;
1751}
1752
1753HRESULT Medium::setDescription(AutoCaller &autoCaller, const com::Utf8Str &aDescription)
1754{
1755 /// @todo update m->strDescription and save the global registry (and local
1756 /// registries of portable VMs referring to this medium), this will also
1757 /// require to add the mRegistered flag to data
1758
1759 HRESULT rc = S_OK;
1760
1761 MediumLockList *pMediumLockList(new MediumLockList());
1762
1763 try
1764 {
1765 autoCaller.release();
1766
1767 // to avoid redundant locking, which just takes a time, just call required functions.
1768 // the error will be just stored and will be reported after locks will be acquired again
1769
1770 const char *pszError = NULL;
1771
1772
1773 /* Build the lock list. */
1774 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
1775 this /* pToLockWrite */,
1776 true /* fMediumLockWriteAll */,
1777 NULL,
1778 *pMediumLockList);
1779 if (FAILED(rc))
1780 {
1781 pszError = tr("Failed to create medium lock list for '%s'");
1782 }
1783 else
1784 {
1785 rc = pMediumLockList->Lock();
1786 if (FAILED(rc))
1787 pszError = tr("Failed to lock media '%s'");
1788 }
1789
1790 // locking: we need the tree lock first because we access parent pointers
1791 // and we need to write-lock the media involved
1792 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1793
1794 autoCaller.add();
1795 AssertComRCThrowRC(autoCaller.rc());
1796
1797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1798
1799 if (FAILED(rc))
1800 throw setError(rc, pszError, i_getLocationFull().c_str());
1801
1802 /* Set a new description */
1803 m->strDescription = aDescription;
1804
1805 // save the settings
1806 alock.release();
1807 autoCaller.release();
1808 treeLock.release();
1809 i_markRegistriesModified();
1810 m->pVirtualBox->i_saveModifiedRegistries();
1811 m->pVirtualBox->i_onMediumConfigChanged(this);
1812 }
1813 catch (HRESULT aRC) { rc = aRC; }
1814
1815 delete pMediumLockList;
1816
1817 return rc;
1818}
1819
1820HRESULT Medium::getState(MediumState_T *aState)
1821{
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823 *aState = m->state;
1824
1825 return S_OK;
1826}
1827
1828HRESULT Medium::getVariant(std::vector<MediumVariant_T> &aVariant)
1829{
1830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1831
1832 const size_t cBits = sizeof(MediumVariant_T) * 8;
1833 aVariant.resize(cBits);
1834 for (size_t i = 0; i < cBits; ++i)
1835 aVariant[i] = (MediumVariant_T)(m->variant & RT_BIT(i));
1836
1837 return S_OK;
1838}
1839
1840HRESULT Medium::getLocation(com::Utf8Str &aLocation)
1841{
1842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1843
1844 aLocation = m->strLocationFull;
1845
1846 return S_OK;
1847}
1848
1849HRESULT Medium::getName(com::Utf8Str &aName)
1850{
1851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1852
1853 aName = i_getName();
1854
1855 return S_OK;
1856}
1857
1858HRESULT Medium::getDeviceType(DeviceType_T *aDeviceType)
1859{
1860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 *aDeviceType = m->devType;
1863
1864 return S_OK;
1865}
1866
1867HRESULT Medium::getHostDrive(BOOL *aHostDrive)
1868{
1869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 *aHostDrive = m->hostDrive;
1872
1873 return S_OK;
1874}
1875
1876HRESULT Medium::getSize(LONG64 *aSize)
1877{
1878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1879
1880 *aSize = (LONG64)m->size;
1881
1882 return S_OK;
1883}
1884
1885HRESULT Medium::getFormat(com::Utf8Str &aFormat)
1886{
1887 /* no need to lock, m->strFormat is const */
1888
1889 aFormat = m->strFormat;
1890 return S_OK;
1891}
1892
1893HRESULT Medium::getMediumFormat(ComPtr<IMediumFormat> &aMediumFormat)
1894{
1895 /* no need to lock, m->formatObj is const */
1896 m->formatObj.queryInterfaceTo(aMediumFormat.asOutParam());
1897
1898 return S_OK;
1899}
1900
1901HRESULT Medium::getType(AutoCaller &autoCaller, MediumType_T *aType)
1902{
1903 NOREF(autoCaller);
1904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1905
1906 *aType = m->type;
1907
1908 return S_OK;
1909}
1910
1911HRESULT Medium::setType(AutoCaller &autoCaller, MediumType_T aType)
1912{
1913 autoCaller.release();
1914
1915 /* It is possible that some previous/concurrent uninit has already cleared
1916 * the pVirtualBox reference, see #uninit(). */
1917 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1918
1919 // we access m->pParent
1920 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
1921
1922 autoCaller.add();
1923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1924
1925 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
1928 while (m->queryInfoRunning)
1929 {
1930 mlock.release();
1931 autoCaller.release();
1932 treeLock.release();
1933 /* Must not hold the media tree lock, as Medium::i_queryInfo needs
1934 * this lock and thus we would run into a deadlock here. */
1935 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
1936 /* must not hold the object lock now */
1937 Assert(!isWriteLockOnCurrentThread());
1938 {
1939 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
1940 }
1941 treeLock.acquire();
1942 autoCaller.add();
1943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1944 mlock.acquire();
1945 }
1946
1947 switch (m->state)
1948 {
1949 case MediumState_Created:
1950 case MediumState_Inaccessible:
1951 break;
1952 default:
1953 return i_setStateError();
1954 }
1955
1956 if (m->type == aType)
1957 {
1958 /* Nothing to do */
1959 return S_OK;
1960 }
1961
1962 DeviceType_T devType = i_getDeviceType();
1963 // DVD media can only be readonly.
1964 if (devType == DeviceType_DVD && aType != MediumType_Readonly)
1965 return setError(VBOX_E_INVALID_OBJECT_STATE,
1966 tr("Cannot change the type of DVD medium '%s'"),
1967 m->strLocationFull.c_str());
1968 // Floppy media can only be writethrough or readonly.
1969 if ( devType == DeviceType_Floppy
1970 && aType != MediumType_Writethrough
1971 && aType != MediumType_Readonly)
1972 return setError(VBOX_E_INVALID_OBJECT_STATE,
1973 tr("Cannot change the type of floppy medium '%s'"),
1974 m->strLocationFull.c_str());
1975
1976 /* cannot change the type of a differencing medium */
1977 if (m->pParent)
1978 return setError(VBOX_E_INVALID_OBJECT_STATE,
1979 tr("Cannot change the type of medium '%s' because it is a differencing medium"),
1980 m->strLocationFull.c_str());
1981
1982 /* Cannot change the type of a medium being in use by more than one VM.
1983 * If the change is to Immutable or MultiAttach then it must not be
1984 * directly attached to any VM, otherwise the assumptions about indirect
1985 * attachment elsewhere are violated and the VM becomes inaccessible.
1986 * Attaching an immutable medium triggers the diff creation, and this is
1987 * vital for the correct operation. */
1988 if ( m->backRefs.size() > 1
1989 || ( ( aType == MediumType_Immutable
1990 || aType == MediumType_MultiAttach)
1991 && m->backRefs.size() > 0))
1992 return setError(VBOX_E_INVALID_OBJECT_STATE,
1993 tr("Cannot change the type of medium '%s' because it is attached to %d virtual machines",
1994 "", m->backRefs.size()),
1995 m->strLocationFull.c_str(), m->backRefs.size());
1996
1997 switch (aType)
1998 {
1999 case MediumType_Normal:
2000 case MediumType_Immutable:
2001 case MediumType_MultiAttach:
2002 {
2003 /* normal can be easily converted to immutable and vice versa even
2004 * if they have children as long as they are not attached to any
2005 * machine themselves */
2006 break;
2007 }
2008 case MediumType_Writethrough:
2009 case MediumType_Shareable:
2010 case MediumType_Readonly:
2011 {
2012 /* cannot change to writethrough, shareable or readonly
2013 * if there are children */
2014 if (i_getChildren().size() != 0)
2015 return setError(VBOX_E_OBJECT_IN_USE,
2016 tr("Cannot change type for medium '%s' since it has %d child media", "", i_getChildren().size()),
2017 m->strLocationFull.c_str(), i_getChildren().size());
2018 if (aType == MediumType_Shareable)
2019 {
2020 MediumVariant_T variant = i_getVariant();
2021 if (!(variant & MediumVariant_Fixed))
2022 return setError(VBOX_E_INVALID_OBJECT_STATE,
2023 tr("Cannot change type for medium '%s' to 'Shareable' since it is a dynamic medium storage unit"),
2024 m->strLocationFull.c_str());
2025 }
2026 else if (aType == MediumType_Readonly && devType == DeviceType_HardDisk)
2027 {
2028 // Readonly hard disks are not allowed, this medium type is reserved for
2029 // DVDs and floppy images at the moment. Later we might allow readonly hard
2030 // disks, but that's extremely unusual and many guest OSes will have trouble.
2031 return setError(VBOX_E_INVALID_OBJECT_STATE,
2032 tr("Cannot change type for medium '%s' to 'Readonly' since it is a hard disk"),
2033 m->strLocationFull.c_str());
2034 }
2035 break;
2036 }
2037 default:
2038 AssertFailedReturn(E_FAIL);
2039 }
2040
2041 if (aType == MediumType_MultiAttach)
2042 {
2043 // This type is new with VirtualBox 4.0 and therefore requires settings
2044 // version 1.11 in the settings backend. Unfortunately it is not enough to do
2045 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
2046 // two reasons: The medium type is a property of the media registry tree, which
2047 // can reside in the global config file (for pre-4.0 media); we would therefore
2048 // possibly need to bump the global config version. We don't want to do that though
2049 // because that might make downgrading to pre-4.0 impossible.
2050 // As a result, we can only use these two new types if the medium is NOT in the
2051 // global registry:
2052 const Guid &uuidGlobalRegistry = m->pVirtualBox->i_getGlobalRegistryId();
2053 if (i_isInRegistry(uuidGlobalRegistry))
2054 return setError(VBOX_E_INVALID_OBJECT_STATE,
2055 tr("Cannot change type for medium '%s': the media type 'MultiAttach' can only be used "
2056 "on media registered with a machine that was created with VirtualBox 4.0 or later"),
2057 m->strLocationFull.c_str());
2058 }
2059
2060 m->type = aType;
2061
2062 // save the settings
2063 mlock.release();
2064 autoCaller.release();
2065 treeLock.release();
2066 i_markRegistriesModified();
2067 m->pVirtualBox->i_saveModifiedRegistries();
2068 m->pVirtualBox->i_onMediumConfigChanged(this);
2069
2070 return S_OK;
2071}
2072
2073HRESULT Medium::getAllowedTypes(std::vector<MediumType_T> &aAllowedTypes)
2074{
2075 NOREF(aAllowedTypes);
2076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2077
2078 ReturnComNotImplemented();
2079}
2080
2081HRESULT Medium::getParent(AutoCaller &autoCaller, ComPtr<IMedium> &aParent)
2082{
2083 autoCaller.release();
2084
2085 /* It is possible that some previous/concurrent uninit has already cleared
2086 * the pVirtualBox reference, see #uninit(). */
2087 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
2088
2089 /* we access m->pParent */
2090 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
2091
2092 autoCaller.add();
2093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2094
2095 m->pParent.queryInterfaceTo(aParent.asOutParam());
2096
2097 return S_OK;
2098}
2099
2100HRESULT Medium::getChildren(AutoCaller &autoCaller, std::vector<ComPtr<IMedium> > &aChildren)
2101{
2102 autoCaller.release();
2103
2104 /* It is possible that some previous/concurrent uninit has already cleared
2105 * the pVirtualBox reference, see #uninit(). */
2106 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
2107
2108 /* we access children */
2109 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
2110
2111 autoCaller.add();
2112 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2113
2114 MediaList children(this->i_getChildren());
2115 aChildren.resize(children.size());
2116 size_t i = 0;
2117 for (MediaList::const_iterator it = children.begin(); it != children.end(); ++it, ++i)
2118 (*it).queryInterfaceTo(aChildren[i].asOutParam());
2119 return S_OK;
2120}
2121
2122HRESULT Medium::getBase(AutoCaller &autoCaller, ComPtr<IMedium> &aBase)
2123{
2124 autoCaller.release();
2125
2126 /* i_getBase() will do callers/locking */
2127 i_getBase().queryInterfaceTo(aBase.asOutParam());
2128
2129 return S_OK;
2130}
2131
2132HRESULT Medium::getReadOnly(AutoCaller &autoCaller, BOOL *aReadOnly)
2133{
2134 autoCaller.release();
2135
2136 /* isReadOnly() will do locking */
2137 *aReadOnly = i_isReadOnly();
2138
2139 return S_OK;
2140}
2141
2142HRESULT Medium::getLogicalSize(LONG64 *aLogicalSize)
2143{
2144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2145
2146 *aLogicalSize = (LONG64)m->logicalSize;
2147
2148 return S_OK;
2149}
2150
2151HRESULT Medium::getAutoReset(BOOL *aAutoReset)
2152{
2153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2154
2155 if (m->pParent.isNull())
2156 *aAutoReset = FALSE;
2157 else
2158 *aAutoReset = m->autoReset;
2159
2160 return S_OK;
2161}
2162
2163HRESULT Medium::setAutoReset(BOOL aAutoReset)
2164{
2165 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2166
2167 if (m->pParent.isNull())
2168 return setError(VBOX_E_NOT_SUPPORTED,
2169 tr("Medium '%s' is not differencing"),
2170 m->strLocationFull.c_str());
2171
2172 if (m->autoReset != !!aAutoReset)
2173 {
2174 m->autoReset = !!aAutoReset;
2175
2176 // save the settings
2177 mlock.release();
2178 i_markRegistriesModified();
2179 m->pVirtualBox->i_saveModifiedRegistries();
2180 m->pVirtualBox->i_onMediumConfigChanged(this);
2181 }
2182
2183 return S_OK;
2184}
2185
2186HRESULT Medium::getLastAccessError(com::Utf8Str &aLastAccessError)
2187{
2188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2189
2190 aLastAccessError = m->strLastAccessError;
2191
2192 return S_OK;
2193}
2194
2195HRESULT Medium::getMachineIds(std::vector<com::Guid> &aMachineIds)
2196{
2197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2198
2199 if (m->backRefs.size() != 0)
2200 {
2201 BackRefList brlist(m->backRefs);
2202 aMachineIds.resize(brlist.size());
2203 size_t i = 0;
2204 for (BackRefList::const_iterator it = brlist.begin(); it != brlist.end(); ++it, ++i)
2205 aMachineIds[i] = it->machineId;
2206 }
2207
2208 return S_OK;
2209}
2210
2211HRESULT Medium::setIds(AutoCaller &autoCaller,
2212 BOOL aSetImageId,
2213 const com::Guid &aImageId,
2214 BOOL aSetParentId,
2215 const com::Guid &aParentId)
2216{
2217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2218
2219 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
2220 if (m->queryInfoRunning)
2221 {
2222 /* Must not hold the media tree lock, as Medium::i_queryInfo needs this
2223 * lock and thus we would run into a deadlock here. */
2224 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2225 while (m->queryInfoRunning)
2226 {
2227 alock.release();
2228 /* must not hold the object lock now */
2229 Assert(!isWriteLockOnCurrentThread());
2230 {
2231 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
2232 }
2233 alock.acquire();
2234 }
2235 }
2236
2237 switch (m->state)
2238 {
2239 case MediumState_Created:
2240 break;
2241 default:
2242 return i_setStateError();
2243 }
2244
2245 Guid imageId, parentId;
2246 if (aSetImageId)
2247 {
2248 if (aImageId.isZero())
2249 imageId.create();
2250 else
2251 {
2252 imageId = aImageId;
2253 if (!imageId.isValid())
2254 return setError(E_INVALIDARG, tr("Argument %s is invalid"), "aImageId");
2255 }
2256 }
2257 if (aSetParentId)
2258 {
2259 if (aParentId.isZero())
2260 parentId.create();
2261 else
2262 parentId = aParentId;
2263 }
2264
2265 const Guid uPrevImage = m->uuidImage;
2266 unconst(m->uuidImage) = imageId;
2267 ComObjPtr<Medium> pPrevParent = i_getParent();
2268 unconst(m->uuidParentImage) = parentId;
2269
2270 // must not hold any locks before calling Medium::i_queryInfo
2271 alock.release();
2272
2273 HRESULT rc = i_queryInfo(!!aSetImageId /* fSetImageId */,
2274 !!aSetParentId /* fSetParentId */,
2275 autoCaller);
2276
2277 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
2278 const Guid uCurrImage = m->uuidImage;
2279 ComObjPtr<Medium> pCurrParent = i_getParent();
2280 arlock.release();
2281
2282 if (SUCCEEDED(rc))
2283 {
2284 if (uCurrImage != uPrevImage)
2285 m->pVirtualBox->i_onMediumConfigChanged(this);
2286 if (pPrevParent != pCurrParent)
2287 {
2288 if (pPrevParent)
2289 m->pVirtualBox->i_onMediumConfigChanged(pPrevParent);
2290 if (pCurrParent)
2291 m->pVirtualBox->i_onMediumConfigChanged(pCurrParent);
2292 }
2293 }
2294
2295 return rc;
2296}
2297
2298HRESULT Medium::refreshState(AutoCaller &autoCaller, MediumState_T *aState)
2299{
2300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2301
2302 HRESULT rc = S_OK;
2303
2304 switch (m->state)
2305 {
2306 case MediumState_Created:
2307 case MediumState_Inaccessible:
2308 case MediumState_LockedRead:
2309 {
2310 // must not hold any locks before calling Medium::i_queryInfo
2311 alock.release();
2312
2313 rc = i_queryInfo(false /* fSetImageId */, false /* fSetParentId */,
2314 autoCaller);
2315
2316 alock.acquire();
2317 break;
2318 }
2319 default:
2320 break;
2321 }
2322
2323 *aState = m->state;
2324
2325 return rc;
2326}
2327
2328HRESULT Medium::getSnapshotIds(const com::Guid &aMachineId,
2329 std::vector<com::Guid> &aSnapshotIds)
2330{
2331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2332
2333 for (BackRefList::const_iterator it = m->backRefs.begin();
2334 it != m->backRefs.end(); ++it)
2335 {
2336 if (it->machineId == aMachineId)
2337 {
2338 size_t size = it->llSnapshotIds.size();
2339
2340 /* if the medium is attached to the machine in the current state, we
2341 * return its ID as the first element of the array */
2342 if (it->fInCurState)
2343 ++size;
2344
2345 if (size > 0)
2346 {
2347 aSnapshotIds.resize(size);
2348
2349 size_t j = 0;
2350 if (it->fInCurState)
2351 aSnapshotIds[j++] = it->machineId.toUtf16();
2352
2353 for(std::list<SnapshotRef>::const_iterator jt = it->llSnapshotIds.begin(); jt != it->llSnapshotIds.end(); ++jt, ++j)
2354 aSnapshotIds[j] = jt->snapshotId;
2355 }
2356
2357 break;
2358 }
2359 }
2360
2361 return S_OK;
2362}
2363
2364HRESULT Medium::lockRead(ComPtr<IToken> &aToken)
2365{
2366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2367
2368 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
2369 if (m->queryInfoRunning)
2370 {
2371 /* Must not hold the media tree lock, as Medium::i_queryInfo needs this
2372 * lock and thus we would run into a deadlock here. */
2373 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2374 while (m->queryInfoRunning)
2375 {
2376 alock.release();
2377 /* must not hold the object lock now */
2378 Assert(!isWriteLockOnCurrentThread());
2379 {
2380 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
2381 }
2382 alock.acquire();
2383 }
2384 }
2385
2386 HRESULT rc = S_OK;
2387
2388 switch (m->state)
2389 {
2390 case MediumState_Created:
2391 case MediumState_Inaccessible:
2392 case MediumState_LockedRead:
2393 {
2394 ++m->readers;
2395
2396 ComAssertMsgBreak(m->readers != 0, (tr("Counter overflow")), rc = E_FAIL);
2397
2398 /* Remember pre-lock state */
2399 if (m->state != MediumState_LockedRead)
2400 m->preLockState = m->state;
2401
2402 LogFlowThisFunc(("Okay - prev state=%d readers=%d\n", m->state, m->readers));
2403 m->state = MediumState_LockedRead;
2404
2405 ComObjPtr<MediumLockToken> pToken;
2406 rc = pToken.createObject();
2407 if (SUCCEEDED(rc))
2408 rc = pToken->init(this, false /* fWrite */);
2409 if (FAILED(rc))
2410 {
2411 --m->readers;
2412 if (m->readers == 0)
2413 m->state = m->preLockState;
2414 return rc;
2415 }
2416
2417 pToken.queryInterfaceTo(aToken.asOutParam());
2418 break;
2419 }
2420 default:
2421 {
2422 LogFlowThisFunc(("Failing - state=%d\n", m->state));
2423 rc = i_setStateError();
2424 break;
2425 }
2426 }
2427
2428 return rc;
2429}
2430
2431/**
2432 * @note @a aState may be NULL if the state value is not needed (only for
2433 * in-process calls).
2434 */
2435HRESULT Medium::i_unlockRead(MediumState_T *aState)
2436{
2437 AutoCaller autoCaller(this);
2438 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2439
2440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2441
2442 HRESULT rc = S_OK;
2443
2444 switch (m->state)
2445 {
2446 case MediumState_LockedRead:
2447 {
2448 ComAssertMsgBreak(m->readers != 0, (tr("Counter underflow")), rc = E_FAIL);
2449 --m->readers;
2450
2451 /* Reset the state after the last reader */
2452 if (m->readers == 0)
2453 {
2454 m->state = m->preLockState;
2455 /* There are cases where we inject the deleting state into
2456 * a medium locked for reading. Make sure #unmarkForDeletion()
2457 * gets the right state afterwards. */
2458 if (m->preLockState == MediumState_Deleting)
2459 m->preLockState = MediumState_Created;
2460 }
2461
2462 LogFlowThisFunc(("new state=%d\n", m->state));
2463 break;
2464 }
2465 default:
2466 {
2467 LogFlowThisFunc(("Failing - state=%d\n", m->state));
2468 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
2469 tr("Medium '%s' is not locked for reading"),
2470 m->strLocationFull.c_str());
2471 break;
2472 }
2473 }
2474
2475 /* return the current state after */
2476 if (aState)
2477 *aState = m->state;
2478
2479 return rc;
2480}
2481HRESULT Medium::lockWrite(ComPtr<IToken> &aToken)
2482{
2483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2484
2485 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
2486 if (m->queryInfoRunning)
2487 {
2488 /* Must not hold the media tree lock, as Medium::i_queryInfo needs this
2489 * lock and thus we would run into a deadlock here. */
2490 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2491 while (m->queryInfoRunning)
2492 {
2493 alock.release();
2494 /* must not hold the object lock now */
2495 Assert(!isWriteLockOnCurrentThread());
2496 {
2497 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
2498 }
2499 alock.acquire();
2500 }
2501 }
2502
2503 HRESULT rc = S_OK;
2504
2505 switch (m->state)
2506 {
2507 case MediumState_Created:
2508 case MediumState_Inaccessible:
2509 {
2510 m->preLockState = m->state;
2511
2512 LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2513 m->state = MediumState_LockedWrite;
2514
2515 ComObjPtr<MediumLockToken> pToken;
2516 rc = pToken.createObject();
2517 if (SUCCEEDED(rc))
2518 rc = pToken->init(this, true /* fWrite */);
2519 if (FAILED(rc))
2520 {
2521 m->state = m->preLockState;
2522 return rc;
2523 }
2524
2525 pToken.queryInterfaceTo(aToken.asOutParam());
2526 break;
2527 }
2528 default:
2529 {
2530 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2531 rc = i_setStateError();
2532 break;
2533 }
2534 }
2535
2536 return rc;
2537}
2538
2539/**
2540 * @note @a aState may be NULL if the state value is not needed (only for
2541 * in-process calls).
2542 */
2543HRESULT Medium::i_unlockWrite(MediumState_T *aState)
2544{
2545 AutoCaller autoCaller(this);
2546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2547
2548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2549
2550 HRESULT rc = S_OK;
2551
2552 switch (m->state)
2553 {
2554 case MediumState_LockedWrite:
2555 {
2556 m->state = m->preLockState;
2557 /* There are cases where we inject the deleting state into
2558 * a medium locked for writing. Make sure #unmarkForDeletion()
2559 * gets the right state afterwards. */
2560 if (m->preLockState == MediumState_Deleting)
2561 m->preLockState = MediumState_Created;
2562 LogFlowThisFunc(("new state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2563 break;
2564 }
2565 default:
2566 {
2567 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2568 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
2569 tr("Medium '%s' is not locked for writing"),
2570 m->strLocationFull.c_str());
2571 break;
2572 }
2573 }
2574
2575 /* return the current state after */
2576 if (aState)
2577 *aState = m->state;
2578
2579 return rc;
2580}
2581
2582HRESULT Medium::close(AutoCaller &aAutoCaller)
2583{
2584 // make a copy of VirtualBox pointer which gets nulled by uninit()
2585 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
2586
2587 Guid uId = i_getId();
2588 DeviceType_T devType = i_getDeviceType();
2589 MultiResult mrc = i_close(aAutoCaller);
2590
2591 pVirtualBox->i_saveModifiedRegistries();
2592
2593 if (SUCCEEDED(mrc) && uId.isValid() && !uId.isZero())
2594 pVirtualBox->i_onMediumRegistered(uId, devType, FALSE);
2595
2596 return mrc;
2597}
2598
2599HRESULT Medium::getProperty(const com::Utf8Str &aName,
2600 com::Utf8Str &aValue)
2601{
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 settings::StringsMap::const_iterator it = m->mapProperties.find(aName);
2605 if (it == m->mapProperties.end())
2606 {
2607 if (!aName.startsWith("Special/"))
2608 return setError(VBOX_E_OBJECT_NOT_FOUND,
2609 tr("Property '%s' does not exist"), aName.c_str());
2610 else
2611 /* be more silent here */
2612 return VBOX_E_OBJECT_NOT_FOUND;
2613 }
2614
2615 aValue = it->second;
2616
2617 return S_OK;
2618}
2619
2620HRESULT Medium::setProperty(const com::Utf8Str &aName,
2621 const com::Utf8Str &aValue)
2622{
2623 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2624
2625 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
2626 if (m->queryInfoRunning)
2627 {
2628 /* Must not hold the media tree lock, as Medium::i_queryInfo needs this
2629 * lock and thus we would run into a deadlock here. */
2630 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2631 while (m->queryInfoRunning)
2632 {
2633 mlock.release();
2634 /* must not hold the object lock now */
2635 Assert(!isWriteLockOnCurrentThread());
2636 {
2637 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
2638 }
2639 mlock.acquire();
2640 }
2641 }
2642
2643 switch (m->state)
2644 {
2645 case MediumState_NotCreated:
2646 case MediumState_Created:
2647 case MediumState_Inaccessible:
2648 break;
2649 default:
2650 return i_setStateError();
2651 }
2652
2653 settings::StringsMap::iterator it = m->mapProperties.find(aName);
2654 if ( !aName.startsWith("Special/")
2655 && !i_isPropertyForFilter(aName))
2656 {
2657 if (it == m->mapProperties.end())
2658 return setError(VBOX_E_OBJECT_NOT_FOUND,
2659 tr("Property '%s' does not exist"),
2660 aName.c_str());
2661 it->second = aValue;
2662 }
2663 else
2664 {
2665 if (it == m->mapProperties.end())
2666 {
2667 if (!aValue.isEmpty())
2668 m->mapProperties[aName] = aValue;
2669 }
2670 else
2671 {
2672 if (!aValue.isEmpty())
2673 it->second = aValue;
2674 else
2675 m->mapProperties.erase(it);
2676 }
2677 }
2678
2679 // save the settings
2680 mlock.release();
2681 i_markRegistriesModified();
2682 m->pVirtualBox->i_saveModifiedRegistries();
2683 m->pVirtualBox->i_onMediumConfigChanged(this);
2684
2685 return S_OK;
2686}
2687
2688HRESULT Medium::getProperties(const com::Utf8Str &aNames,
2689 std::vector<com::Utf8Str> &aReturnNames,
2690 std::vector<com::Utf8Str> &aReturnValues)
2691{
2692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2693
2694 /// @todo make use of aNames according to the documentation
2695 NOREF(aNames);
2696
2697 aReturnNames.resize(m->mapProperties.size());
2698 aReturnValues.resize(m->mapProperties.size());
2699 size_t i = 0;
2700 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
2701 it != m->mapProperties.end();
2702 ++it, ++i)
2703 {
2704 aReturnNames[i] = it->first;
2705 aReturnValues[i] = it->second;
2706 }
2707 return S_OK;
2708}
2709
2710HRESULT Medium::setProperties(const std::vector<com::Utf8Str> &aNames,
2711 const std::vector<com::Utf8Str> &aValues)
2712{
2713 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 /* first pass: validate names */
2716 for (size_t i = 0;
2717 i < aNames.size();
2718 ++i)
2719 {
2720 Utf8Str strName(aNames[i]);
2721 if ( !strName.startsWith("Special/")
2722 && !i_isPropertyForFilter(strName)
2723 && m->mapProperties.find(strName) == m->mapProperties.end())
2724 return setError(VBOX_E_OBJECT_NOT_FOUND,
2725 tr("Property '%s' does not exist"), strName.c_str());
2726 }
2727
2728 /* second pass: assign */
2729 for (size_t i = 0;
2730 i < aNames.size();
2731 ++i)
2732 {
2733 Utf8Str strName(aNames[i]);
2734 Utf8Str strValue(aValues[i]);
2735 settings::StringsMap::iterator it = m->mapProperties.find(strName);
2736 if ( !strName.startsWith("Special/")
2737 && !i_isPropertyForFilter(strName))
2738 {
2739 AssertReturn(it != m->mapProperties.end(), E_FAIL);
2740 it->second = strValue;
2741 }
2742 else
2743 {
2744 if (it == m->mapProperties.end())
2745 {
2746 if (!strValue.isEmpty())
2747 m->mapProperties[strName] = strValue;
2748 }
2749 else
2750 {
2751 if (!strValue.isEmpty())
2752 it->second = strValue;
2753 else
2754 m->mapProperties.erase(it);
2755 }
2756 }
2757 }
2758
2759 // save the settings
2760 mlock.release();
2761 i_markRegistriesModified();
2762 m->pVirtualBox->i_saveModifiedRegistries();
2763 m->pVirtualBox->i_onMediumConfigChanged(this);
2764
2765 return S_OK;
2766}
2767
2768HRESULT Medium::createBaseStorage(LONG64 aLogicalSize,
2769 const std::vector<MediumVariant_T> &aVariant,
2770 ComPtr<IProgress> &aProgress)
2771{
2772 if (aLogicalSize < 0)
2773 return setError(E_INVALIDARG, tr("The medium size argument (%lld) is negative"), aLogicalSize);
2774
2775 HRESULT rc = S_OK;
2776 ComObjPtr<Progress> pProgress;
2777 Medium::Task *pTask = NULL;
2778
2779 try
2780 {
2781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2782
2783 ULONG mediumVariantFlags = 0;
2784
2785 if (aVariant.size())
2786 {
2787 for (size_t i = 0; i < aVariant.size(); i++)
2788 mediumVariantFlags |= (ULONG)aVariant[i];
2789 }
2790
2791 mediumVariantFlags &= ((unsigned)~MediumVariant_Diff);
2792
2793 if ( !(mediumVariantFlags & MediumVariant_Fixed)
2794 && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateDynamic))
2795 throw setError(VBOX_E_NOT_SUPPORTED,
2796 tr("Medium format '%s' does not support dynamic storage creation"),
2797 m->strFormat.c_str());
2798
2799 if ( (mediumVariantFlags & MediumVariant_Fixed)
2800 && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateFixed))
2801 throw setError(VBOX_E_NOT_SUPPORTED,
2802 tr("Medium format '%s' does not support fixed storage creation"),
2803 m->strFormat.c_str());
2804
2805 if ( (mediumVariantFlags & MediumVariant_Formatted)
2806 && i_getDeviceType() != DeviceType_Floppy)
2807 throw setError(VBOX_E_NOT_SUPPORTED,
2808 tr("Medium variant 'formatted' applies to floppy images only"));
2809
2810 if (m->state != MediumState_NotCreated)
2811 throw i_setStateError();
2812
2813 pProgress.createObject();
2814 rc = pProgress->init(m->pVirtualBox,
2815 static_cast<IMedium*>(this),
2816 (mediumVariantFlags & MediumVariant_Fixed)
2817 ? BstrFmt(tr("Creating fixed medium storage unit '%s'"), m->strLocationFull.c_str()).raw()
2818 : BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
2819 TRUE /* aCancelable */);
2820 if (FAILED(rc))
2821 throw rc;
2822
2823 /* setup task object to carry out the operation asynchronously */
2824 pTask = new Medium::CreateBaseTask(this, pProgress, (uint64_t)aLogicalSize,
2825 (MediumVariant_T)mediumVariantFlags);
2826 rc = pTask->rc();
2827 AssertComRC(rc);
2828 if (FAILED(rc))
2829 throw rc;
2830
2831 m->state = MediumState_Creating;
2832 }
2833 catch (HRESULT aRC) { rc = aRC; }
2834
2835 if (SUCCEEDED(rc))
2836 {
2837 rc = pTask->createThread();
2838 pTask = NULL;
2839
2840 if (SUCCEEDED(rc))
2841 pProgress.queryInterfaceTo(aProgress.asOutParam());
2842 }
2843 else if (pTask != NULL)
2844 delete pTask;
2845
2846 return rc;
2847}
2848
2849HRESULT Medium::deleteStorage(ComPtr<IProgress> &aProgress)
2850{
2851 ComObjPtr<Progress> pProgress;
2852
2853 MultiResult mrc = i_deleteStorage(&pProgress,
2854 false /* aWait */,
2855 true /* aNotify */);
2856 /* Must save the registries in any case, since an entry was removed. */
2857 m->pVirtualBox->i_saveModifiedRegistries();
2858
2859 if (SUCCEEDED(mrc))
2860 pProgress.queryInterfaceTo(aProgress.asOutParam());
2861
2862 return mrc;
2863}
2864
2865HRESULT Medium::createDiffStorage(AutoCaller &autoCaller,
2866 const ComPtr<IMedium> &aTarget,
2867 const std::vector<MediumVariant_T> &aVariant,
2868 ComPtr<IProgress> &aProgress)
2869{
2870 IMedium *aT = aTarget;
2871 ComObjPtr<Medium> diff = static_cast<Medium*>(aT);
2872
2873 autoCaller.release();
2874
2875 /* It is possible that some previous/concurrent uninit has already cleared
2876 * the pVirtualBox reference, see #uninit(). */
2877 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
2878
2879 // we access m->pParent
2880 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
2881
2882 autoCaller.add();
2883 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2884
2885 AutoMultiWriteLock2 alock(this, diff COMMA_LOCKVAL_SRC_POS);
2886
2887 if (m->type == MediumType_Writethrough)
2888 return setError(VBOX_E_INVALID_OBJECT_STATE,
2889 tr("Medium type of '%s' is Writethrough"),
2890 m->strLocationFull.c_str());
2891 else if (m->type == MediumType_Shareable)
2892 return setError(VBOX_E_INVALID_OBJECT_STATE,
2893 tr("Medium type of '%s' is Shareable"),
2894 m->strLocationFull.c_str());
2895 else if (m->type == MediumType_Readonly)
2896 return setError(VBOX_E_INVALID_OBJECT_STATE,
2897 tr("Medium type of '%s' is Readonly"),
2898 m->strLocationFull.c_str());
2899
2900 /* Apply the normal locking logic to the entire chain. */
2901 MediumLockList *pMediumLockList(new MediumLockList());
2902 alock.release();
2903 autoCaller.release();
2904 treeLock.release();
2905 HRESULT rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
2906 diff /* pToLockWrite */,
2907 false /* fMediumLockWriteAll */,
2908 this,
2909 *pMediumLockList);
2910 treeLock.acquire();
2911 autoCaller.add();
2912 if (FAILED(autoCaller.rc()))
2913 rc = autoCaller.rc();
2914 alock.acquire();
2915 if (FAILED(rc))
2916 {
2917 delete pMediumLockList;
2918 return rc;
2919 }
2920
2921 alock.release();
2922 autoCaller.release();
2923 treeLock.release();
2924 rc = pMediumLockList->Lock();
2925 treeLock.acquire();
2926 autoCaller.add();
2927 if (FAILED(autoCaller.rc()))
2928 rc = autoCaller.rc();
2929 alock.acquire();
2930 if (FAILED(rc))
2931 {
2932 delete pMediumLockList;
2933
2934 return setError(rc, tr("Could not lock medium when creating diff '%s'"),
2935 diff->i_getLocationFull().c_str());
2936 }
2937
2938 Guid parentMachineRegistry;
2939 if (i_getFirstRegistryMachineId(parentMachineRegistry))
2940 {
2941 /* since this medium has been just created it isn't associated yet */
2942 diff->m->llRegistryIDs.push_back(parentMachineRegistry);
2943 alock.release();
2944 autoCaller.release();
2945 treeLock.release();
2946 diff->i_markRegistriesModified();
2947 treeLock.acquire();
2948 autoCaller.add();
2949 alock.acquire();
2950 }
2951
2952 alock.release();
2953 autoCaller.release();
2954 treeLock.release();
2955
2956 ComObjPtr<Progress> pProgress;
2957
2958 ULONG mediumVariantFlags = 0;
2959
2960 if (aVariant.size())
2961 {
2962 for (size_t i = 0; i < aVariant.size(); i++)
2963 mediumVariantFlags |= (ULONG)aVariant[i];
2964 }
2965
2966 if (mediumVariantFlags & MediumVariant_Formatted)
2967 {
2968 delete pMediumLockList;
2969 return setError(VBOX_E_NOT_SUPPORTED,
2970 tr("Medium variant 'formatted' applies to floppy images only"));
2971 }
2972
2973 rc = i_createDiffStorage(diff, (MediumVariant_T)mediumVariantFlags, pMediumLockList,
2974 &pProgress, false /* aWait */, true /* aNotify */);
2975 if (FAILED(rc))
2976 delete pMediumLockList;
2977 else
2978 pProgress.queryInterfaceTo(aProgress.asOutParam());
2979
2980 return rc;
2981}
2982
2983HRESULT Medium::mergeTo(const ComPtr<IMedium> &aTarget,
2984 ComPtr<IProgress> &aProgress)
2985{
2986 IMedium *aT = aTarget;
2987
2988 ComAssertRet(aT != this, E_INVALIDARG);
2989
2990 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aT);
2991
2992 bool fMergeForward = false;
2993 ComObjPtr<Medium> pParentForTarget;
2994 MediumLockList *pChildrenToReparent = NULL;
2995 MediumLockList *pMediumLockList = NULL;
2996
2997 HRESULT rc = S_OK;
2998
2999 rc = i_prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward,
3000 pParentForTarget, pChildrenToReparent, pMediumLockList);
3001 if (FAILED(rc)) return rc;
3002
3003 ComObjPtr<Progress> pProgress;
3004
3005 rc = i_mergeTo(pTarget, fMergeForward, pParentForTarget, pChildrenToReparent,
3006 pMediumLockList, &pProgress, false /* aWait */, true /* aNotify */);
3007 if (FAILED(rc))
3008 i_cancelMergeTo(pChildrenToReparent, pMediumLockList);
3009 else
3010 pProgress.queryInterfaceTo(aProgress.asOutParam());
3011
3012 return rc;
3013}
3014
3015HRESULT Medium::cloneToBase(const ComPtr<IMedium> &aTarget,
3016 const std::vector<MediumVariant_T> &aVariant,
3017 ComPtr<IProgress> &aProgress)
3018{
3019 return cloneTo(aTarget, aVariant, NULL, aProgress);
3020}
3021
3022HRESULT Medium::cloneTo(const ComPtr<IMedium> &aTarget,
3023 const std::vector<MediumVariant_T> &aVariant,
3024 const ComPtr<IMedium> &aParent,
3025 ComPtr<IProgress> &aProgress)
3026{
3027 return resizeAndCloneTo(aTarget, 0, aVariant, aParent, aProgress);
3028}
3029
3030/**
3031 * This is a helper function that combines the functionality of
3032 * Medium::cloneTo() and Medium::resize(). The target medium will take the
3033 * contents of the calling medium.
3034 *
3035 * @param aTarget Medium to resize and clone to
3036 * @param aLogicalSize Desired size for targer medium
3037 * @param aVariant
3038 * @param aParent
3039 * @param aProgress
3040 * @return HRESULT
3041 */
3042HRESULT Medium::resizeAndCloneTo(const ComPtr<IMedium> &aTarget,
3043 LONG64 aLogicalSize,
3044 const std::vector<MediumVariant_T> &aVariant,
3045 const ComPtr<IMedium> &aParent,
3046 ComPtr<IProgress> &aProgress)
3047{
3048 /* Check for valid args */
3049 ComAssertRet(aTarget != this, E_INVALIDARG);
3050 CheckComArgExpr(aLogicalSize, aLogicalSize >= 0);
3051
3052 /* Convert args to usable/needed types */
3053 IMedium *aT = aTarget;
3054 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aT);
3055 ComObjPtr<Medium> pParent;
3056 if (aParent)
3057 {
3058 IMedium *aP = aParent;
3059 pParent = static_cast<Medium*>(aP);
3060 }
3061
3062 /* Set up variables. Fetch needed data in lockable blocks */
3063 HRESULT rc = S_OK;
3064 ComObjPtr<Progress> pTmpProgress;
3065 Medium::Task *pTask = NULL;
3066
3067 Utf8Str strSourceName;
3068 {
3069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3070 strSourceName = i_getName();
3071 }
3072
3073 uint64_t uTargetExistingSize = 0;
3074 Utf8Str strTargetName;
3075 {
3076 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
3077 uTargetExistingSize = pTarget->i_getLogicalSize();
3078 strTargetName = pTarget->i_getName();
3079 }
3080
3081 /* Set up internal multi-subprocess progress object */
3082 ComObjPtr<Progress> pProgress;
3083 pProgress.createObject();
3084 rc = pProgress->init(m->pVirtualBox,
3085 static_cast<IMedium*>(this),
3086 BstrFmt(tr("Resizing medium and cloning into it")).raw(),
3087 TRUE, /* aCancelable */
3088 2, /* Number of opearations */
3089 BstrFmt(tr("Resizing medium before clone")).raw()
3090 );
3091
3092 if (FAILED(rc))
3093 throw rc;
3094
3095 /* If target does not exist, handle resize. */
3096 if (pTarget->m->state != MediumState_NotCreated && aLogicalSize > 0)
3097 {
3098 if ((LONG64)uTargetExistingSize != aLogicalSize) {
3099 if (!i_isMediumFormatFile())
3100 {
3101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3102 rc = setError(VBOX_E_NOT_SUPPORTED,
3103 tr("Sizes of '%s' and '%s' are different and \
3104 medium format does not support resing"),
3105 strSourceName.c_str(), strTargetName.c_str());
3106 throw rc;
3107 }
3108
3109 /**
3110 * Need to lock the target medium as i_resize does do so
3111 * automatically.
3112 */
3113
3114 ComPtr<IToken> pToken;
3115 rc = pTarget->LockWrite(pToken.asOutParam());
3116
3117 if (FAILED(rc)) throw rc;
3118
3119 /**
3120 * Have to make own lock list, because "resize" method resizes only
3121 * last image in the lock chain.
3122 */
3123
3124 MediumLockList* pMediumLockListForResize = new MediumLockList();
3125 pMediumLockListForResize->Append(pTarget, pTarget->m->state == MediumState_LockedWrite);
3126
3127 rc = pMediumLockListForResize->Lock(true /* fSkipOverLockedMedia */);
3128
3129 if (FAILED(rc))
3130 {
3131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3132 rc = setError(rc,
3133 tr("Failed to lock the medium '%s' to resize before merge"),
3134 strTargetName.c_str());
3135 delete pMediumLockListForResize;
3136 throw rc;
3137 }
3138
3139
3140 rc = pTarget->i_resize((uint64_t)aLogicalSize, pMediumLockListForResize, &pProgress, true, false);
3141
3142 if (FAILED(rc))
3143 {
3144 /* No need to setError becasue i_resize and i_taskResizeHandler handle this automatically. */
3145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3146 delete pMediumLockListForResize;
3147 throw rc;
3148 }
3149
3150 delete pMediumLockListForResize;
3151
3152 pTarget->m->logicalSize = (uint64_t)aLogicalSize;
3153
3154 pToken->Abandon();
3155 pToken.setNull();
3156 }
3157 }
3158
3159 /* Report progress to supplied progress argument */
3160 if (SUCCEEDED(rc))
3161 {
3162 pProgress.queryInterfaceTo(aProgress.asOutParam());
3163 }
3164
3165 try
3166 {
3167 // locking: we need the tree lock first because we access parent pointers
3168 // and we need to write-lock the media involved
3169 uint32_t cHandles = 3;
3170 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
3171 this->lockHandle(),
3172 pTarget->lockHandle() };
3173 /* Only add parent to the lock if it is not null */
3174 if (!pParent.isNull())
3175 pHandles[cHandles++] = pParent->lockHandle();
3176 AutoWriteLock alock(cHandles,
3177 pHandles
3178 COMMA_LOCKVAL_SRC_POS);
3179
3180 if ( pTarget->m->state != MediumState_NotCreated
3181 && pTarget->m->state != MediumState_Created)
3182 throw pTarget->i_setStateError();
3183
3184 /* Build the source lock list. */
3185 MediumLockList *pSourceMediumLockList(new MediumLockList());
3186 alock.release();
3187 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
3188 NULL /* pToLockWrite */,
3189 false /* fMediumLockWriteAll */,
3190 NULL,
3191 *pSourceMediumLockList);
3192 alock.acquire();
3193 if (FAILED(rc))
3194 {
3195 delete pSourceMediumLockList;
3196 throw rc;
3197 }
3198
3199 /* Build the target lock list (including the to-be parent chain). */
3200 MediumLockList *pTargetMediumLockList(new MediumLockList());
3201 alock.release();
3202 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
3203 pTarget /* pToLockWrite */,
3204 false /* fMediumLockWriteAll */,
3205 pParent,
3206 *pTargetMediumLockList);
3207 alock.acquire();
3208 if (FAILED(rc))
3209 {
3210 delete pSourceMediumLockList;
3211 delete pTargetMediumLockList;
3212 throw rc;
3213 }
3214
3215 alock.release();
3216 rc = pSourceMediumLockList->Lock();
3217 alock.acquire();
3218 if (FAILED(rc))
3219 {
3220 delete pSourceMediumLockList;
3221 delete pTargetMediumLockList;
3222 throw setError(rc,
3223 tr("Failed to lock source media '%s'"),
3224 i_getLocationFull().c_str());
3225 }
3226 alock.release();
3227 rc = pTargetMediumLockList->Lock();
3228 alock.acquire();
3229 if (FAILED(rc))
3230 {
3231 delete pSourceMediumLockList;
3232 delete pTargetMediumLockList;
3233 throw setError(rc,
3234 tr("Failed to lock target media '%s'"),
3235 pTarget->i_getLocationFull().c_str());
3236 }
3237
3238 ULONG mediumVariantFlags = 0;
3239
3240 if (aVariant.size())
3241 {
3242 for (size_t i = 0; i < aVariant.size(); i++)
3243 mediumVariantFlags |= (ULONG)aVariant[i];
3244 }
3245
3246 if (mediumVariantFlags & MediumVariant_Formatted)
3247 {
3248 delete pSourceMediumLockList;
3249 delete pTargetMediumLockList;
3250 throw setError(VBOX_E_NOT_SUPPORTED,
3251 tr("Medium variant 'formatted' applies to floppy images only"));
3252 }
3253
3254 if (pTarget->m->state != MediumState_NotCreated || aLogicalSize == 0)
3255 {
3256 /* setup task object to carry out the operation asynchronously */
3257 pTask = new Medium::CloneTask(this, pProgress, pTarget,
3258 (MediumVariant_T)mediumVariantFlags,
3259 pParent, UINT32_MAX, UINT32_MAX,
3260 pSourceMediumLockList, pTargetMediumLockList,
3261 false, false, true, 0);
3262 }
3263 else
3264 {
3265 /* setup task object to carry out the operation asynchronously */
3266 pTask = new Medium::CloneTask(this, pProgress, pTarget,
3267 (MediumVariant_T)mediumVariantFlags,
3268 pParent, UINT32_MAX, UINT32_MAX,
3269 pSourceMediumLockList, pTargetMediumLockList,
3270 false, false, true, aLogicalSize);
3271 }
3272
3273 rc = pTask->rc();
3274 AssertComRC(rc);
3275 if (FAILED(rc))
3276 throw rc;
3277
3278 if (pTarget->m->state == MediumState_NotCreated)
3279 pTarget->m->state = MediumState_Creating;
3280 }
3281 catch (HRESULT aRC) { rc = aRC; }
3282
3283 if (SUCCEEDED(rc))
3284 {
3285 rc = pTask->createThread();
3286 pTask = NULL;
3287 if (SUCCEEDED(rc))
3288 pProgress.queryInterfaceTo(aProgress.asOutParam());
3289 }
3290 else if (pTask != NULL) {
3291 delete pTask;
3292 throw rc;
3293 }
3294
3295 return rc;
3296}
3297
3298HRESULT Medium::moveTo(AutoCaller &autoCaller, const com::Utf8Str &aLocation, ComPtr<IProgress> &aProgress)
3299{
3300 ComObjPtr<Medium> pParent;
3301 ComObjPtr<Progress> pProgress;
3302 HRESULT rc = S_OK;
3303 Medium::Task *pTask = NULL;
3304
3305 try
3306 {
3307 /// @todo NEWMEDIA for file names, add the default extension if no extension
3308 /// is present (using the information from the VD backend which also implies
3309 /// that one more parameter should be passed to moveTo() requesting
3310 /// that functionality since it is only allowed when called from this method
3311
3312 /// @todo NEWMEDIA rename the file and set m->location on success, then save
3313 /// the global registry (and local registries of portable VMs referring to
3314 /// this medium), this will also require to add the mRegistered flag to data
3315
3316 autoCaller.release();
3317
3318 // locking: we need the tree lock first because we access parent pointers
3319 // and we need to write-lock the media involved
3320 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3321
3322 autoCaller.add();
3323 AssertComRCThrowRC(autoCaller.rc());
3324
3325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3326
3327 /* play with locations */
3328 {
3329 /* get source path and filename */
3330 Utf8Str sourcePath = i_getLocationFull();
3331 Utf8Str sourceFName = i_getName();
3332
3333 if (aLocation.isEmpty())
3334 {
3335 rc = setErrorVrc(VERR_PATH_ZERO_LENGTH,
3336 tr("Medium '%s' can't be moved. Destination path is empty."),
3337 i_getLocationFull().c_str());
3338 throw rc;
3339 }
3340
3341 /* extract destination path and filename */
3342 Utf8Str destPath(aLocation);
3343 Utf8Str destFName(destPath);
3344 destFName.stripPath();
3345
3346 if (destFName.isNotEmpty() && !RTPathHasSuffix(destFName.c_str()))
3347 {
3348 /*
3349 * The target path has no filename: Either "/path/to/new/location" or
3350 * just "newname" (no trailing backslash or there is no filename extension).
3351 */
3352 if (destPath.equals(destFName))
3353 {
3354 /* new path contains only "newname", no path, no extension */
3355 destFName.append(RTPathSuffix(sourceFName.c_str()));
3356 destPath = destFName;
3357 }
3358 else
3359 {
3360 /* new path looks like "/path/to/new/location" */
3361 destFName.setNull();
3362 destPath.append(RTPATH_SLASH);
3363 }
3364 }
3365
3366 if (destFName.isEmpty())
3367 {
3368 /* No target name */
3369 destPath.append(sourceFName);
3370 }
3371 else
3372 {
3373 if (destPath.equals(destFName))
3374 {
3375 /*
3376 * The target path contains of only a filename without a directory.
3377 * Move the medium within the source directory to the new name
3378 * (actually rename operation).
3379 * Scratches sourcePath!
3380 */
3381 destPath = sourcePath.stripFilename().append(RTPATH_SLASH).append(destFName);
3382 }
3383
3384 const char *pszSuffix = RTPathSuffix(sourceFName.c_str());
3385
3386 /* Suffix is empty and one is deduced from the medium format */
3387 if (pszSuffix == NULL)
3388 {
3389 Utf8Str strExt = i_getFormat();
3390 if (strExt.compare("RAW", Utf8Str::CaseInsensitive) == 0)
3391 {
3392 DeviceType_T devType = i_getDeviceType();
3393 switch (devType)
3394 {
3395 case DeviceType_DVD:
3396 strExt = "iso";
3397 break;
3398 case DeviceType_Floppy:
3399 strExt = "img";
3400 break;
3401 default:
3402 rc = setErrorVrc(VERR_NOT_A_FILE, /** @todo r=bird: Mixing status codes again. */
3403 tr("Medium '%s' has RAW type. \"Move\" operation isn't supported for this type."),
3404 i_getLocationFull().c_str());
3405 throw rc;
3406 }
3407 }
3408 else if (strExt.compare("Parallels", Utf8Str::CaseInsensitive) == 0)
3409 {
3410 strExt = "hdd";
3411 }
3412
3413 /* Set the target extension like on the source. Any conversions are prohibited */
3414 strExt.toLower();
3415 destPath.stripSuffix().append('.').append(strExt);
3416 }
3417 else
3418 destPath.stripSuffix().append(pszSuffix);
3419 }
3420
3421 /* Simple check for existence */
3422 if (RTFileExists(destPath.c_str()))
3423 {
3424 rc = setError(VBOX_E_FILE_ERROR,
3425 tr("The given path '%s' is an existing file. Delete or rename this file."),
3426 destPath.c_str());
3427 throw rc;
3428 }
3429
3430 if (!i_isMediumFormatFile())
3431 {
3432 rc = setErrorVrc(VERR_NOT_A_FILE,
3433 tr("Medium '%s' isn't a file object. \"Move\" operation isn't supported."),
3434 i_getLocationFull().c_str());
3435 throw rc;
3436 }
3437 /* Path must be absolute */
3438 if (!RTPathStartsWithRoot(destPath.c_str()))
3439 {
3440 rc = setError(VBOX_E_FILE_ERROR,
3441 tr("The given path '%s' is not fully qualified"),
3442 destPath.c_str());
3443 throw rc;
3444 }
3445 /* Check path for a new file object */
3446 rc = VirtualBox::i_ensureFilePathExists(destPath, true);
3447 if (FAILED(rc))
3448 throw rc;
3449
3450 /* Set needed variables for "moving" procedure. It'll be used later in separate thread task */
3451 rc = i_preparationForMoving(destPath);
3452 if (FAILED(rc))
3453 {
3454 rc = setErrorVrc(VERR_NO_CHANGE,
3455 tr("Medium '%s' is already in the correct location"),
3456 i_getLocationFull().c_str());
3457 throw rc;
3458 }
3459 }
3460
3461 /* Check VMs which have this medium attached to*/
3462 std::vector<com::Guid> aMachineIds;
3463 rc = getMachineIds(aMachineIds);
3464 std::vector<com::Guid>::const_iterator currMachineID = aMachineIds.begin();
3465 std::vector<com::Guid>::const_iterator lastMachineID = aMachineIds.end();
3466
3467 while (currMachineID != lastMachineID)
3468 {
3469 Guid id(*currMachineID);
3470 ComObjPtr<Machine> aMachine;
3471
3472 alock.release();
3473 autoCaller.release();
3474 treeLock.release();
3475 rc = m->pVirtualBox->i_findMachine(id, false, true, &aMachine);
3476 treeLock.acquire();
3477 autoCaller.add();
3478 AssertComRCThrowRC(autoCaller.rc());
3479 alock.acquire();
3480
3481 if (SUCCEEDED(rc))
3482 {
3483 ComObjPtr<SessionMachine> sm;
3484 ComPtr<IInternalSessionControl> ctl;
3485
3486 alock.release();
3487 autoCaller.release();
3488 treeLock.release();
3489 bool ses = aMachine->i_isSessionOpenVM(sm, &ctl);
3490 treeLock.acquire();
3491 autoCaller.add();
3492 AssertComRCThrowRC(autoCaller.rc());
3493 alock.acquire();
3494
3495 if (ses)
3496 {
3497 rc = setError(VBOX_E_INVALID_VM_STATE,
3498 tr("At least the VM '%s' to whom this medium '%s' attached has currently an opened session. Stop all VMs before relocating this medium"),
3499 id.toString().c_str(),
3500 i_getLocationFull().c_str());
3501 throw rc;
3502 }
3503 }
3504 ++currMachineID;
3505 }
3506
3507 /* Build the source lock list. */
3508 MediumLockList *pMediumLockList(new MediumLockList());
3509 alock.release();
3510 autoCaller.release();
3511 treeLock.release();
3512 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
3513 this /* pToLockWrite */,
3514 true /* fMediumLockWriteAll */,
3515 NULL,
3516 *pMediumLockList);
3517 treeLock.acquire();
3518 autoCaller.add();
3519 AssertComRCThrowRC(autoCaller.rc());
3520 alock.acquire();
3521 if (FAILED(rc))
3522 {
3523 delete pMediumLockList;
3524 throw setError(rc,
3525 tr("Failed to create medium lock list for '%s'"),
3526 i_getLocationFull().c_str());
3527 }
3528 alock.release();
3529 autoCaller.release();
3530 treeLock.release();
3531 rc = pMediumLockList->Lock();
3532 treeLock.acquire();
3533 autoCaller.add();
3534 AssertComRCThrowRC(autoCaller.rc());
3535 alock.acquire();
3536 if (FAILED(rc))
3537 {
3538 delete pMediumLockList;
3539 throw setError(rc,
3540 tr("Failed to lock media '%s'"),
3541 i_getLocationFull().c_str());
3542 }
3543
3544 pProgress.createObject();
3545 rc = pProgress->init(m->pVirtualBox,
3546 static_cast <IMedium *>(this),
3547 BstrFmt(tr("Moving medium '%s'"), m->strLocationFull.c_str()).raw(),
3548 TRUE /* aCancelable */);
3549
3550 /* Do the disk moving. */
3551 if (SUCCEEDED(rc))
3552 {
3553 ULONG mediumVariantFlags = i_getVariant();
3554
3555 /* setup task object to carry out the operation asynchronously */
3556 pTask = new Medium::MoveTask(this, pProgress,
3557 (MediumVariant_T)mediumVariantFlags,
3558 pMediumLockList);
3559 rc = pTask->rc();
3560 AssertComRC(rc);
3561 if (FAILED(rc))
3562 throw rc;
3563 }
3564
3565 }
3566 catch (HRESULT aRC) { rc = aRC; }
3567
3568 if (SUCCEEDED(rc))
3569 {
3570 rc = pTask->createThread();
3571 pTask = NULL;
3572 if (SUCCEEDED(rc))
3573 pProgress.queryInterfaceTo(aProgress.asOutParam());
3574 }
3575 else
3576 {
3577 if (pTask)
3578 delete pTask;
3579 }
3580
3581 return rc;
3582}
3583
3584HRESULT Medium::setLocation(const com::Utf8Str &aLocation)
3585{
3586 HRESULT rc = S_OK;
3587
3588 try
3589 {
3590 // locking: we need the tree lock first because we access parent pointers
3591 // and we need to write-lock the media involved
3592 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3593
3594 AutoCaller autoCaller(this);
3595 AssertComRCThrowRC(autoCaller.rc());
3596
3597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3598
3599 Utf8Str destPath(aLocation);
3600
3601 // some check for file based medium
3602 if (i_isMediumFormatFile())
3603 {
3604 /* Path must be absolute */
3605 if (!RTPathStartsWithRoot(destPath.c_str()))
3606 {
3607 rc = setError(VBOX_E_FILE_ERROR,
3608 tr("The given path '%s' is not fully qualified"),
3609 destPath.c_str());
3610 throw rc;
3611 }
3612
3613 /* Simple check for existence */
3614 if (!RTFileExists(destPath.c_str()))
3615 {
3616 rc = setError(VBOX_E_FILE_ERROR,
3617 tr("The given path '%s' is not an existing file. New location is invalid."),
3618 destPath.c_str());
3619 throw rc;
3620 }
3621 }
3622
3623 /* Check VMs which have this medium attached to*/
3624 std::vector<com::Guid> aMachineIds;
3625 rc = getMachineIds(aMachineIds);
3626
3627 // switch locks only if there are machines with this medium attached
3628 if (!aMachineIds.empty())
3629 {
3630 std::vector<com::Guid>::const_iterator currMachineID = aMachineIds.begin();
3631 std::vector<com::Guid>::const_iterator lastMachineID = aMachineIds.end();
3632
3633 alock.release();
3634 autoCaller.release();
3635 treeLock.release();
3636
3637 while (currMachineID != lastMachineID)
3638 {
3639 Guid id(*currMachineID);
3640 ComObjPtr<Machine> aMachine;
3641 rc = m->pVirtualBox->i_findMachine(id, false, true, &aMachine);
3642 if (SUCCEEDED(rc))
3643 {
3644 ComObjPtr<SessionMachine> sm;
3645 ComPtr<IInternalSessionControl> ctl;
3646
3647 bool ses = aMachine->i_isSessionOpenVM(sm, &ctl);
3648 if (ses)
3649 {
3650 treeLock.acquire();
3651 autoCaller.add();
3652 AssertComRCThrowRC(autoCaller.rc());
3653 alock.acquire();
3654
3655 rc = setError(VBOX_E_INVALID_VM_STATE,
3656 tr("At least the VM '%s' to whom this medium '%s' attached has currently an opened session. Stop all VMs before set location for this medium"),
3657 id.toString().c_str(),
3658 i_getLocationFull().c_str());
3659 throw rc;
3660 }
3661 }
3662 ++currMachineID;
3663 }
3664
3665 treeLock.acquire();
3666 autoCaller.add();
3667 AssertComRCThrowRC(autoCaller.rc());
3668 alock.acquire();
3669 }
3670
3671 m->strLocationFull = destPath;
3672
3673 // save the settings
3674 alock.release();
3675 autoCaller.release();
3676 treeLock.release();
3677
3678 i_markRegistriesModified();
3679 m->pVirtualBox->i_saveModifiedRegistries();
3680
3681 MediumState_T mediumState;
3682 refreshState(autoCaller, &mediumState);
3683 m->pVirtualBox->i_onMediumConfigChanged(this);
3684 }
3685 catch (HRESULT aRC) { rc = aRC; }
3686
3687 return rc;
3688}
3689
3690HRESULT Medium::compact(ComPtr<IProgress> &aProgress)
3691{
3692 HRESULT rc = S_OK;
3693 ComObjPtr<Progress> pProgress;
3694 Medium::Task *pTask = NULL;
3695
3696 try
3697 {
3698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3699
3700 /* Build the medium lock list. */
3701 MediumLockList *pMediumLockList(new MediumLockList());
3702 alock.release();
3703 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3704 this /* pToLockWrite */,
3705 false /* fMediumLockWriteAll */,
3706 NULL,
3707 *pMediumLockList);
3708 alock.acquire();
3709 if (FAILED(rc))
3710 {
3711 delete pMediumLockList;
3712 throw rc;
3713 }
3714
3715 alock.release();
3716 rc = pMediumLockList->Lock();
3717 alock.acquire();
3718 if (FAILED(rc))
3719 {
3720 delete pMediumLockList;
3721 throw setError(rc,
3722 tr("Failed to lock media when compacting '%s'"),
3723 i_getLocationFull().c_str());
3724 }
3725
3726 pProgress.createObject();
3727 rc = pProgress->init(m->pVirtualBox,
3728 static_cast <IMedium *>(this),
3729 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
3730 TRUE /* aCancelable */);
3731 if (FAILED(rc))
3732 {
3733 delete pMediumLockList;
3734 throw rc;
3735 }
3736
3737 /* setup task object to carry out the operation asynchronously */
3738 pTask = new Medium::CompactTask(this, pProgress, pMediumLockList);
3739 rc = pTask->rc();
3740 AssertComRC(rc);
3741 if (FAILED(rc))
3742 throw rc;
3743 }
3744 catch (HRESULT aRC) { rc = aRC; }
3745
3746 if (SUCCEEDED(rc))
3747 {
3748 rc = pTask->createThread();
3749 pTask = NULL;
3750 if (SUCCEEDED(rc))
3751 pProgress.queryInterfaceTo(aProgress.asOutParam());
3752 }
3753 else if (pTask != NULL)
3754 delete pTask;
3755
3756 return rc;
3757}
3758
3759HRESULT Medium::resize(LONG64 aLogicalSize,
3760 ComPtr<IProgress> &aProgress)
3761{
3762 CheckComArgExpr(aLogicalSize, aLogicalSize > 0);
3763 HRESULT rc = S_OK;
3764 ComObjPtr<Progress> pProgress;
3765
3766 /* Build the medium lock list. */
3767 MediumLockList *pMediumLockList(new MediumLockList());
3768
3769 try
3770 {
3771 const char *pszError = NULL;
3772
3773 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3774 this /* pToLockWrite */,
3775 false /* fMediumLockWriteAll */,
3776 NULL,
3777 *pMediumLockList);
3778 if (FAILED(rc))
3779 {
3780 pszError = tr("Failed to create medium lock list when resizing '%s'");
3781 }
3782 else
3783 {
3784 rc = pMediumLockList->Lock();
3785 if (FAILED(rc))
3786 pszError = tr("Failed to lock media when resizing '%s'");
3787 }
3788
3789
3790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3791
3792 if (FAILED(rc))
3793 {
3794 throw setError(rc, pszError, i_getLocationFull().c_str());
3795 }
3796
3797 pProgress.createObject();
3798 rc = pProgress->init(m->pVirtualBox,
3799 static_cast <IMedium *>(this),
3800 BstrFmt(tr("Resizing medium '%s'"), m->strLocationFull.c_str()).raw(),
3801 TRUE /* aCancelable */);
3802 if (FAILED(rc))
3803 {
3804 throw rc;
3805 }
3806 }
3807 catch (HRESULT aRC) { rc = aRC; }
3808
3809 if (SUCCEEDED(rc))
3810 rc = i_resize((uint64_t)aLogicalSize, pMediumLockList, &pProgress, false /* aWait */, true /* aNotify */);
3811
3812 if (SUCCEEDED(rc))
3813 pProgress.queryInterfaceTo(aProgress.asOutParam());
3814 else
3815 delete pMediumLockList;
3816
3817 return rc;
3818}
3819
3820HRESULT Medium::reset(AutoCaller &autoCaller, ComPtr<IProgress> &aProgress)
3821{
3822 HRESULT rc = S_OK;
3823 ComObjPtr<Progress> pProgress;
3824 Medium::Task *pTask = NULL;
3825
3826 try
3827 {
3828 autoCaller.release();
3829
3830 /* It is possible that some previous/concurrent uninit has already
3831 * cleared the pVirtualBox reference, see #uninit(). */
3832 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
3833
3834 /* i_canClose() needs the tree lock */
3835 AutoMultiWriteLock2 multilock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL,
3836 this->lockHandle()
3837 COMMA_LOCKVAL_SRC_POS);
3838
3839 autoCaller.add();
3840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3841
3842 LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
3843
3844 if (m->pParent.isNull())
3845 throw setError(VBOX_E_NOT_SUPPORTED,
3846 tr("Medium type of '%s' is not differencing"),
3847 m->strLocationFull.c_str());
3848
3849 rc = i_canClose();
3850 if (FAILED(rc))
3851 throw rc;
3852
3853 /* Build the medium lock list. */
3854 MediumLockList *pMediumLockList(new MediumLockList());
3855 multilock.release();
3856 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
3857 this /* pToLockWrite */,
3858 false /* fMediumLockWriteAll */,
3859 NULL,
3860 *pMediumLockList);
3861 multilock.acquire();
3862 if (FAILED(rc))
3863 {
3864 delete pMediumLockList;
3865 throw rc;
3866 }
3867
3868 multilock.release();
3869 rc = pMediumLockList->Lock();
3870 multilock.acquire();
3871 if (FAILED(rc))
3872 {
3873 delete pMediumLockList;
3874 throw setError(rc,
3875 tr("Failed to lock media when resetting '%s'"),
3876 i_getLocationFull().c_str());
3877 }
3878
3879 pProgress.createObject();
3880 rc = pProgress->init(m->pVirtualBox,
3881 static_cast<IMedium*>(this),
3882 BstrFmt(tr("Resetting differencing medium '%s'"), m->strLocationFull.c_str()).raw(),
3883 FALSE /* aCancelable */);
3884 if (FAILED(rc))
3885 throw rc;
3886
3887 /* setup task object to carry out the operation asynchronously */
3888 pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
3889 rc = pTask->rc();
3890 AssertComRC(rc);
3891 if (FAILED(rc))
3892 throw rc;
3893 }
3894 catch (HRESULT aRC) { rc = aRC; }
3895
3896 if (SUCCEEDED(rc))
3897 {
3898 rc = pTask->createThread();
3899 pTask = NULL;
3900 if (SUCCEEDED(rc))
3901 pProgress.queryInterfaceTo(aProgress.asOutParam());
3902 }
3903 else if (pTask != NULL)
3904 delete pTask;
3905
3906 LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
3907
3908 return rc;
3909}
3910
3911HRESULT Medium::changeEncryption(const com::Utf8Str &aCurrentPassword, const com::Utf8Str &aCipher,
3912 const com::Utf8Str &aNewPassword, const com::Utf8Str &aNewPasswordId,
3913 ComPtr<IProgress> &aProgress)
3914{
3915 HRESULT rc = S_OK;
3916 ComObjPtr<Progress> pProgress;
3917 Medium::Task *pTask = NULL;
3918
3919 try
3920 {
3921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3922
3923 DeviceType_T devType = i_getDeviceType();
3924 /* Cannot encrypt DVD or floppy images so far. */
3925 if ( devType == DeviceType_DVD
3926 || devType == DeviceType_Floppy)
3927 return setError(VBOX_E_INVALID_OBJECT_STATE,
3928 tr("Cannot encrypt DVD or Floppy medium '%s'"),
3929 m->strLocationFull.c_str());
3930
3931 /* Cannot encrypt media which are attached to more than one virtual machine. */
3932 if (m->backRefs.size() > 1)
3933 return setError(VBOX_E_INVALID_OBJECT_STATE,
3934 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", m->backRefs.size()),
3935 m->strLocationFull.c_str(), m->backRefs.size());
3936
3937 if (i_getChildren().size() != 0)
3938 return setError(VBOX_E_INVALID_OBJECT_STATE,
3939 tr("Cannot encrypt medium '%s' because it has %d children", "", i_getChildren().size()),
3940 m->strLocationFull.c_str(), i_getChildren().size());
3941
3942 /* Build the medium lock list. */
3943 MediumLockList *pMediumLockList(new MediumLockList());
3944 alock.release();
3945 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3946 this /* pToLockWrite */,
3947 true /* fMediumLockAllWrite */,
3948 NULL,
3949 *pMediumLockList);
3950 alock.acquire();
3951 if (FAILED(rc))
3952 {
3953 delete pMediumLockList;
3954 throw rc;
3955 }
3956
3957 alock.release();
3958 rc = pMediumLockList->Lock();
3959 alock.acquire();
3960 if (FAILED(rc))
3961 {
3962 delete pMediumLockList;
3963 throw setError(rc,
3964 tr("Failed to lock media for encryption '%s'"),
3965 i_getLocationFull().c_str());
3966 }
3967
3968 /*
3969 * Check all media in the chain to not contain any branches or references to
3970 * other virtual machines, we support encrypting only a list of differencing media at the moment.
3971 */
3972 MediumLockList::Base::const_iterator mediumListBegin = pMediumLockList->GetBegin();
3973 MediumLockList::Base::const_iterator mediumListEnd = pMediumLockList->GetEnd();
3974 for (MediumLockList::Base::const_iterator it = mediumListBegin;
3975 it != mediumListEnd;
3976 ++it)
3977 {
3978 const MediumLock &mediumLock = *it;
3979 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
3980 AutoReadLock mediumReadLock(pMedium COMMA_LOCKVAL_SRC_POS);
3981
3982 Assert(pMedium->m->state == MediumState_LockedWrite);
3983
3984 if (pMedium->m->backRefs.size() > 1)
3985 {
3986 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3987 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "",
3988 pMedium->m->backRefs.size()),
3989 pMedium->m->strLocationFull.c_str(), pMedium->m->backRefs.size());
3990 break;
3991 }
3992 else if (pMedium->i_getChildren().size() > 1)
3993 {
3994 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3995 tr("Cannot encrypt medium '%s' because it has %d children", "", pMedium->i_getChildren().size()),
3996 pMedium->m->strLocationFull.c_str(), pMedium->i_getChildren().size());
3997 break;
3998 }
3999 }
4000
4001 if (FAILED(rc))
4002 {
4003 delete pMediumLockList;
4004 throw rc;
4005 }
4006
4007 const char *pszAction = tr("Encrypting medium");
4008 if ( aCurrentPassword.isNotEmpty()
4009 && aCipher.isEmpty())
4010 pszAction = tr("Decrypting medium");
4011
4012 pProgress.createObject();
4013 rc = pProgress->init(m->pVirtualBox,
4014 static_cast <IMedium *>(this),
4015 BstrFmt("%s '%s'", pszAction, m->strLocationFull.c_str()).raw(),
4016 TRUE /* aCancelable */);
4017 if (FAILED(rc))
4018 {
4019 delete pMediumLockList;
4020 throw rc;
4021 }
4022
4023 /* setup task object to carry out the operation asynchronously */
4024 pTask = new Medium::EncryptTask(this, aNewPassword, aCurrentPassword,
4025 aCipher, aNewPasswordId, pProgress, pMediumLockList);
4026 rc = pTask->rc();
4027 AssertComRC(rc);
4028 if (FAILED(rc))
4029 throw rc;
4030 }
4031 catch (HRESULT aRC) { rc = aRC; }
4032
4033 if (SUCCEEDED(rc))
4034 {
4035 rc = pTask->createThread();
4036 pTask = NULL;
4037 if (SUCCEEDED(rc))
4038 pProgress.queryInterfaceTo(aProgress.asOutParam());
4039 }
4040 else if (pTask != NULL)
4041 delete pTask;
4042
4043 return rc;
4044}
4045
4046HRESULT Medium::getEncryptionSettings(AutoCaller &autoCaller, com::Utf8Str &aCipher, com::Utf8Str &aPasswordId)
4047{
4048#ifndef VBOX_WITH_EXTPACK
4049 RT_NOREF(aCipher, aPasswordId);
4050#endif
4051 HRESULT rc = S_OK;
4052
4053 try
4054 {
4055 autoCaller.release();
4056 ComObjPtr<Medium> pBase = i_getBase();
4057 autoCaller.add();
4058 if (FAILED(autoCaller.rc()))
4059 throw rc;
4060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4061
4062 /* Check whether encryption is configured for this medium. */
4063 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
4064 if (it == pBase->m->mapProperties.end())
4065 throw VBOX_E_NOT_SUPPORTED;
4066
4067# ifdef VBOX_WITH_EXTPACK
4068 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
4069 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
4070 {
4071 /* Load the plugin */
4072 Utf8Str strPlugin;
4073 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
4074 if (SUCCEEDED(rc))
4075 {
4076 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
4077 if (RT_FAILURE(vrc))
4078 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
4079 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
4080 i_vdError(vrc).c_str());
4081 }
4082 else
4083 throw setError(VBOX_E_NOT_SUPPORTED,
4084 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
4085 ORACLE_PUEL_EXTPACK_NAME);
4086 }
4087 else
4088 throw setError(VBOX_E_NOT_SUPPORTED,
4089 tr("Encryption is not supported because the extension pack '%s' is missing"),
4090 ORACLE_PUEL_EXTPACK_NAME);
4091
4092 PVDISK pDisk = NULL;
4093 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
4094 ComAssertRCThrow(vrc, E_FAIL);
4095
4096 MediumCryptoFilterSettings CryptoSettings;
4097
4098 i_taskEncryptSettingsSetup(&CryptoSettings, NULL, it->second.c_str(), NULL, false /* fCreateKeyStore */);
4099 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ | VD_FILTER_FLAGS_INFO, CryptoSettings.vdFilterIfaces);
4100 if (RT_FAILURE(vrc))
4101 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
4102 tr("Failed to load the encryption filter: %s"),
4103 i_vdError(vrc).c_str());
4104
4105 it = pBase->m->mapProperties.find("CRYPT/KeyId");
4106 if (it == pBase->m->mapProperties.end())
4107 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4108 tr("Image is configured for encryption but doesn't has a KeyId set"));
4109
4110 aPasswordId = it->second.c_str();
4111 aCipher = CryptoSettings.pszCipherReturned;
4112 RTStrFree(CryptoSettings.pszCipherReturned);
4113
4114 VDDestroy(pDisk);
4115# else
4116 throw setError(VBOX_E_NOT_SUPPORTED,
4117 tr("Encryption is not supported because extension pack support is not built in"));
4118# endif
4119 }
4120 catch (HRESULT aRC) { rc = aRC; }
4121
4122 return rc;
4123}
4124
4125HRESULT Medium::checkEncryptionPassword(const com::Utf8Str &aPassword)
4126{
4127 HRESULT rc = S_OK;
4128
4129 try
4130 {
4131 ComObjPtr<Medium> pBase = i_getBase();
4132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4133
4134 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
4135 if (it == pBase->m->mapProperties.end())
4136 throw setError(VBOX_E_NOT_SUPPORTED,
4137 tr("The image is not configured for encryption"));
4138
4139 if (aPassword.isEmpty())
4140 throw setError(E_INVALIDARG,
4141 tr("The given password must not be empty"));
4142
4143# ifdef VBOX_WITH_EXTPACK
4144 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
4145 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
4146 {
4147 /* Load the plugin */
4148 Utf8Str strPlugin;
4149 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
4150 if (SUCCEEDED(rc))
4151 {
4152 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
4153 if (RT_FAILURE(vrc))
4154 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
4155 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
4156 i_vdError(vrc).c_str());
4157 }
4158 else
4159 throw setError(VBOX_E_NOT_SUPPORTED,
4160 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
4161 ORACLE_PUEL_EXTPACK_NAME);
4162 }
4163 else
4164 throw setError(VBOX_E_NOT_SUPPORTED,
4165 tr("Encryption is not supported because the extension pack '%s' is missing"),
4166 ORACLE_PUEL_EXTPACK_NAME);
4167
4168 PVDISK pDisk = NULL;
4169 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
4170 ComAssertRCThrow(vrc, E_FAIL);
4171
4172 MediumCryptoFilterSettings CryptoSettings;
4173
4174 i_taskEncryptSettingsSetup(&CryptoSettings, NULL, it->second.c_str(), aPassword.c_str(),
4175 false /* fCreateKeyStore */);
4176 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettings.vdFilterIfaces);
4177 if (vrc == VERR_VD_PASSWORD_INCORRECT)
4178 throw setError(VBOX_E_PASSWORD_INCORRECT,
4179 tr("The given password is incorrect"));
4180 else if (RT_FAILURE(vrc))
4181 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
4182 tr("Failed to load the encryption filter: %s"),
4183 i_vdError(vrc).c_str());
4184
4185 VDDestroy(pDisk);
4186# else
4187 throw setError(VBOX_E_NOT_SUPPORTED,
4188 tr("Encryption is not supported because extension pack support is not built in"));
4189# endif
4190 }
4191 catch (HRESULT aRC) { rc = aRC; }
4192
4193 return rc;
4194}
4195
4196HRESULT Medium::openForIO(BOOL aWritable, com::Utf8Str const &aPassword, ComPtr<IMediumIO> &aMediumIO)
4197{
4198 /*
4199 * Input validation.
4200 */
4201 if (aWritable && i_isReadOnly())
4202 return setError(E_ACCESSDENIED, tr("Write access denied: read-only"));
4203
4204 com::Utf8Str const strKeyId = i_getKeyId();
4205 if (strKeyId.isEmpty() && aPassword.isNotEmpty())
4206 return setError(E_INVALIDARG, tr("Password given for unencrypted medium"));
4207 if (strKeyId.isNotEmpty() && aPassword.isEmpty())
4208 return setError(E_INVALIDARG, tr("Password needed for encrypted medium"));
4209
4210 /*
4211 * Create IO object and return it.
4212 */
4213 ComObjPtr<MediumIO> ptrIO;
4214 HRESULT hrc = ptrIO.createObject();
4215 if (SUCCEEDED(hrc))
4216 {
4217 hrc = ptrIO->initForMedium(this, m->pVirtualBox, aWritable != FALSE, strKeyId, aPassword);
4218 if (SUCCEEDED(hrc))
4219 ptrIO.queryInterfaceTo(aMediumIO.asOutParam());
4220 }
4221 return hrc;
4222}
4223
4224
4225////////////////////////////////////////////////////////////////////////////////
4226//
4227// Medium public internal methods
4228//
4229////////////////////////////////////////////////////////////////////////////////
4230
4231/**
4232 * Internal method to return the medium's parent medium. Must have caller + locking!
4233 * @return
4234 */
4235const ComObjPtr<Medium>& Medium::i_getParent() const
4236{
4237 return m->pParent;
4238}
4239
4240/**
4241 * Internal method to return the medium's list of child media. Must have caller + locking!
4242 * @return
4243 */
4244const MediaList& Medium::i_getChildren() const
4245{
4246 return m->llChildren;
4247}
4248
4249/**
4250 * Internal method to return the medium's GUID. Must have caller + locking!
4251 * @return
4252 */
4253const Guid& Medium::i_getId() const
4254{
4255 return m->id;
4256}
4257
4258/**
4259 * Internal method to return the medium's state. Must have caller + locking!
4260 * @return
4261 */
4262MediumState_T Medium::i_getState() const
4263{
4264 return m->state;
4265}
4266
4267/**
4268 * Internal method to return the medium's variant. Must have caller + locking!
4269 * @return
4270 */
4271MediumVariant_T Medium::i_getVariant() const
4272{
4273 return m->variant;
4274}
4275
4276/**
4277 * Internal method which returns true if this medium represents a host drive.
4278 * @return
4279 */
4280bool Medium::i_isHostDrive() const
4281{
4282 return m->hostDrive;
4283}
4284
4285/**
4286 * Internal method to return the medium's full location. Must have caller + locking!
4287 * @return
4288 */
4289const Utf8Str& Medium::i_getLocationFull() const
4290{
4291 return m->strLocationFull;
4292}
4293
4294/**
4295 * Internal method to return the medium's format string. Must have caller + locking!
4296 * @return
4297 */
4298const Utf8Str& Medium::i_getFormat() const
4299{
4300 return m->strFormat;
4301}
4302
4303/**
4304 * Internal method to return the medium's format object. Must have caller + locking!
4305 * @return
4306 */
4307const ComObjPtr<MediumFormat>& Medium::i_getMediumFormat() const
4308{
4309 return m->formatObj;
4310}
4311
4312/**
4313 * Internal method that returns true if the medium is represented by a file on the host disk
4314 * (and not iSCSI or something).
4315 * @return
4316 */
4317bool Medium::i_isMediumFormatFile() const
4318{
4319 if ( m->formatObj
4320 && (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
4321 )
4322 return true;
4323 return false;
4324}
4325
4326/**
4327 * Internal method to return the medium's size. Must have caller + locking!
4328 * @return
4329 */
4330uint64_t Medium::i_getSize() const
4331{
4332 return m->size;
4333}
4334
4335/**
4336 * Internal method to return the medium's size. Must have caller + locking!
4337 * @return
4338 */
4339uint64_t Medium::i_getLogicalSize() const
4340{
4341 return m->logicalSize;
4342}
4343
4344/**
4345 * Returns the medium device type. Must have caller + locking!
4346 * @return
4347 */
4348DeviceType_T Medium::i_getDeviceType() const
4349{
4350 return m->devType;
4351}
4352
4353/**
4354 * Returns the medium type. Must have caller + locking!
4355 * @return
4356 */
4357MediumType_T Medium::i_getType() const
4358{
4359 return m->type;
4360}
4361
4362/**
4363 * Returns a short version of the location attribute.
4364 *
4365 * @note Must be called from under this object's read or write lock.
4366 */
4367Utf8Str Medium::i_getName()
4368{
4369 Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
4370 return name;
4371}
4372
4373/**
4374 * Same as i_addRegistry() except that we don't check the object state, making
4375 * it safe to call with initFromSettings() on the call stack.
4376 */
4377bool Medium::i_addRegistryNoCallerCheck(const Guid &id)
4378{
4379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4380
4381 bool fAdd = true;
4382
4383 // hard disks cannot be in more than one registry
4384 if ( m->devType == DeviceType_HardDisk
4385 && m->llRegistryIDs.size() > 0)
4386 fAdd = false;
4387
4388 // no need to add the UUID twice
4389 if (fAdd)
4390 {
4391 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
4392 it != m->llRegistryIDs.end();
4393 ++it)
4394 {
4395 if ((*it) == id)
4396 {
4397 fAdd = false;
4398 break;
4399 }
4400 }
4401 }
4402
4403 if (fAdd)
4404 m->llRegistryIDs.push_back(id);
4405
4406 return fAdd;
4407}
4408
4409/**
4410 * This adds the given UUID to the list of media registries in which this
4411 * medium should be registered. The UUID can either be a machine UUID,
4412 * to add a machine registry, or the global registry UUID as returned by
4413 * VirtualBox::getGlobalRegistryId().
4414 *
4415 * Note that for hard disks, this method does nothing if the medium is
4416 * already in another registry to avoid having hard disks in more than
4417 * one registry, which causes trouble with keeping diff images in sync.
4418 * See getFirstRegistryMachineId() for details.
4419 *
4420 * @param id
4421 * @return true if the registry was added; false if the given id was already on the list.
4422 */
4423bool Medium::i_addRegistry(const Guid &id)
4424{
4425 AutoCaller autoCaller(this);
4426 if (FAILED(autoCaller.rc()))
4427 return false;
4428 return i_addRegistryNoCallerCheck(id);
4429}
4430
4431/**
4432 * This adds the given UUID to the list of media registries in which this
4433 * medium should be registered. The UUID can either be a machine UUID,
4434 * to add a machine registry, or the global registry UUID as returned by
4435 * VirtualBox::getGlobalRegistryId(). Thisis applied to all children.
4436 *
4437 * Note that for hard disks, this method does nothing if the medium is
4438 * already in another registry to avoid having hard disks in more than
4439 * one registry, which causes trouble with keeping diff images in sync.
4440 * See getFirstRegistryMachineId() for details.
4441 *
4442 * @note the caller must hold the media tree lock for reading.
4443 *
4444 * @param id
4445 * @return true if the registry was added; false if the given id was already on the list.
4446 */
4447bool Medium::i_addRegistryAll(const Guid &id)
4448{
4449 MediaList llMediaTodo;
4450 llMediaTodo.push_back(this);
4451
4452 bool fAdd = false;
4453
4454 while (!llMediaTodo.empty())
4455 {
4456 ComObjPtr<Medium> pMedium = llMediaTodo.front();
4457 llMediaTodo.pop_front();
4458
4459 AutoCaller mediumCaller(pMedium);
4460 if (FAILED(mediumCaller.rc())) continue;
4461
4462 fAdd |= pMedium->i_addRegistryNoCallerCheck(id);
4463
4464 // protected by the medium tree lock held by our original caller
4465 MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
4466 MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
4467 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
4468 llMediaTodo.push_back(*it);
4469 }
4470
4471 return fAdd;
4472}
4473
4474/**
4475 * Removes the given UUID from the list of media registry UUIDs of this medium.
4476 *
4477 * @param id
4478 * @return true if the UUID was found or false if not.
4479 */
4480bool Medium::i_removeRegistry(const Guid &id)
4481{
4482 AutoCaller autoCaller(this);
4483 if (FAILED(autoCaller.rc()))
4484 return false;
4485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4486
4487 bool fRemove = false;
4488
4489 /// @todo r=klaus eliminate this code, replace it by using find.
4490 for (GuidList::iterator it = m->llRegistryIDs.begin();
4491 it != m->llRegistryIDs.end();
4492 ++it)
4493 {
4494 if ((*it) == id)
4495 {
4496 // getting away with this as the iterator isn't used after
4497 m->llRegistryIDs.erase(it);
4498 fRemove = true;
4499 break;
4500 }
4501 }
4502
4503 return fRemove;
4504}
4505
4506/**
4507 * Removes the given UUID from the list of media registry UUIDs, for this
4508 * medium and all its children.
4509 *
4510 * @note the caller must hold the media tree lock for reading.
4511 *
4512 * @param id
4513 * @return true if the UUID was found or false if not.
4514 */
4515bool Medium::i_removeRegistryAll(const Guid &id)
4516{
4517 MediaList llMediaTodo;
4518 llMediaTodo.push_back(this);
4519
4520 bool fRemove = false;
4521
4522 while (!llMediaTodo.empty())
4523 {
4524 ComObjPtr<Medium> pMedium = llMediaTodo.front();
4525 llMediaTodo.pop_front();
4526
4527 AutoCaller mediumCaller(pMedium);
4528 if (FAILED(mediumCaller.rc())) continue;
4529
4530 fRemove |= pMedium->i_removeRegistry(id);
4531
4532 // protected by the medium tree lock held by our original caller
4533 MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
4534 MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
4535 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
4536 llMediaTodo.push_back(*it);
4537 }
4538
4539 return fRemove;
4540}
4541
4542/**
4543 * Returns true if id is in the list of media registries for this medium.
4544 *
4545 * Must have caller + read locking!
4546 *
4547 * @param id
4548 * @return
4549 */
4550bool Medium::i_isInRegistry(const Guid &id)
4551{
4552 /// @todo r=klaus eliminate this code, replace it by using find.
4553 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
4554 it != m->llRegistryIDs.end();
4555 ++it)
4556 {
4557 if (*it == id)
4558 return true;
4559 }
4560
4561 return false;
4562}
4563
4564/**
4565 * Internal method to return the medium's first registry machine (i.e. the machine in whose
4566 * machine XML this medium is listed).
4567 *
4568 * Every attached medium must now (4.0) reside in at least one media registry, which is identified
4569 * by a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
4570 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
4571 * object if the machine is old and still needs the global registry in VirtualBox.xml.
4572 *
4573 * By definition, hard disks may only be in one media registry, in which all its children
4574 * will be stored as well. Otherwise we run into problems with having keep multiple registries
4575 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
4576 * case, only VM2's registry is used for the disk in question.)
4577 *
4578 * If there is no medium registry, particularly if the medium has not been attached yet, this
4579 * does not modify uuid and returns false.
4580 *
4581 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
4582 * the user.
4583 *
4584 * Must have caller + locking!
4585 *
4586 * @param uuid Receives first registry machine UUID, if available.
4587 * @return true if uuid was set.
4588 */
4589bool Medium::i_getFirstRegistryMachineId(Guid &uuid) const
4590{
4591 if (m->llRegistryIDs.size())
4592 {
4593 uuid = m->llRegistryIDs.front();
4594 return true;
4595 }
4596 return false;
4597}
4598
4599/**
4600 * Marks all the registries in which this medium is registered as modified.
4601 */
4602void Medium::i_markRegistriesModified()
4603{
4604 AutoCaller autoCaller(this);
4605 if (FAILED(autoCaller.rc())) return;
4606
4607 // Get local copy, as keeping the lock over VirtualBox::markRegistryModified
4608 // causes trouble with the lock order
4609 GuidList llRegistryIDs;
4610 {
4611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4612 llRegistryIDs = m->llRegistryIDs;
4613 }
4614
4615 autoCaller.release();
4616
4617 /* Save the error information now, the implicit restore when this goes
4618 * out of scope will throw away spurious additional errors created below. */
4619 ErrorInfoKeeper eik;
4620 for (GuidList::const_iterator it = llRegistryIDs.begin();
4621 it != llRegistryIDs.end();
4622 ++it)
4623 {
4624 m->pVirtualBox->i_markRegistryModified(*it);
4625 }
4626}
4627
4628/**
4629 * Adds the given machine and optionally the snapshot to the list of the objects
4630 * this medium is attached to.
4631 *
4632 * @param aMachineId Machine ID.
4633 * @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
4634 */
4635HRESULT Medium::i_addBackReference(const Guid &aMachineId,
4636 const Guid &aSnapshotId /*= Guid::Empty*/)
4637{
4638 AssertReturn(aMachineId.isValid(), E_FAIL);
4639
4640 LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
4641
4642 AutoCaller autoCaller(this);
4643 AssertComRCReturnRC(autoCaller.rc());
4644
4645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4646
4647 switch (m->state)
4648 {
4649 case MediumState_Created:
4650 case MediumState_Inaccessible:
4651 case MediumState_LockedRead:
4652 case MediumState_LockedWrite:
4653 break;
4654
4655 default:
4656 return i_setStateError();
4657 }
4658
4659 if (m->numCreateDiffTasks > 0)
4660 return setError(VBOX_E_OBJECT_IN_USE,
4661 tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created", "",
4662 m->numCreateDiffTasks),
4663 m->strLocationFull.c_str(),
4664 m->id.raw(),
4665 m->numCreateDiffTasks);
4666
4667 BackRefList::iterator it = std::find_if(m->backRefs.begin(),
4668 m->backRefs.end(),
4669 BackRef::EqualsTo(aMachineId));
4670 if (it == m->backRefs.end())
4671 {
4672 BackRef ref(aMachineId, aSnapshotId);
4673 m->backRefs.push_back(ref);
4674
4675 return S_OK;
4676 }
4677 bool fDvd = false;
4678 {
4679 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
4680 /*
4681 * Check the medium is DVD and readonly. It's for the case if DVD
4682 * will be able to be writable sometime in the future.
4683 */
4684 fDvd = m->type == MediumType_Readonly && m->devType == DeviceType_DVD;
4685 }
4686
4687 // if the caller has not supplied a snapshot ID, then we're attaching
4688 // to a machine a medium which represents the machine's current state,
4689 // so set the flag
4690
4691 if (aSnapshotId.isZero())
4692 {
4693 // Allow DVD having MediumType_Readonly to be attached twice.
4694 // (the medium already had been added to back reference)
4695 if (fDvd)
4696 {
4697 it->iRefCnt++;
4698 return S_OK;
4699 }
4700
4701 /* sanity: no duplicate attachments */
4702 if (it->fInCurState)
4703 return setError(VBOX_E_OBJECT_IN_USE,
4704 tr("Cannot attach medium '%s' {%RTuuid}: medium is already associated with the current state of machine uuid {%RTuuid}!"),
4705 m->strLocationFull.c_str(),
4706 m->id.raw(),
4707 aMachineId.raw());
4708 it->fInCurState = true;
4709
4710 return S_OK;
4711 }
4712
4713 // otherwise: a snapshot medium is being attached
4714
4715 /* sanity: no duplicate attachments */
4716 for (std::list<SnapshotRef>::iterator jt = it->llSnapshotIds.begin();
4717 jt != it->llSnapshotIds.end();
4718 ++jt)
4719 {
4720 const Guid &idOldSnapshot = jt->snapshotId;
4721
4722 if (idOldSnapshot == aSnapshotId)
4723 {
4724 if (fDvd)
4725 {
4726 jt->iRefCnt++;
4727 return S_OK;
4728 }
4729#ifdef DEBUG
4730 i_dumpBackRefs();
4731#endif
4732 return setError(VBOX_E_OBJECT_IN_USE,
4733 tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
4734 m->strLocationFull.c_str(),
4735 m->id.raw(),
4736 aSnapshotId.raw());
4737 }
4738 }
4739
4740 it->llSnapshotIds.push_back(SnapshotRef(aSnapshotId));
4741 // Do not touch fInCurState, as the image may be attached to the current
4742 // state *and* a snapshot, otherwise we lose the current state association!
4743
4744 LogFlowThisFuncLeave();
4745
4746 return S_OK;
4747}
4748
4749/**
4750 * Removes the given machine and optionally the snapshot from the list of the
4751 * objects this medium is attached to.
4752 *
4753 * @param aMachineId Machine ID.
4754 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
4755 * attachment.
4756 */
4757HRESULT Medium::i_removeBackReference(const Guid &aMachineId,
4758 const Guid &aSnapshotId /*= Guid::Empty*/)
4759{
4760 AssertReturn(aMachineId.isValid(), E_FAIL);
4761
4762 AutoCaller autoCaller(this);
4763 AssertComRCReturnRC(autoCaller.rc());
4764
4765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4766
4767 BackRefList::iterator it =
4768 std::find_if(m->backRefs.begin(), m->backRefs.end(),
4769 BackRef::EqualsTo(aMachineId));
4770 AssertReturn(it != m->backRefs.end(), E_FAIL);
4771
4772 if (aSnapshotId.isZero())
4773 {
4774 it->iRefCnt--;
4775 if (it->iRefCnt > 0)
4776 return S_OK;
4777
4778 /* remove the current state attachment */
4779 it->fInCurState = false;
4780 }
4781 else
4782 {
4783 /* remove the snapshot attachment */
4784 std::list<SnapshotRef>::iterator jt =
4785 std::find_if(it->llSnapshotIds.begin(),
4786 it->llSnapshotIds.end(),
4787 SnapshotRef::EqualsTo(aSnapshotId));
4788
4789 AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
4790
4791 jt->iRefCnt--;
4792 if (jt->iRefCnt > 0)
4793 return S_OK;
4794
4795 it->llSnapshotIds.erase(jt);
4796 }
4797
4798 /* if the backref becomes empty, remove it */
4799 if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
4800 m->backRefs.erase(it);
4801
4802 return S_OK;
4803}
4804
4805/**
4806 * Internal method to return the medium's list of backrefs. Must have caller + locking!
4807 * @return
4808 */
4809const Guid* Medium::i_getFirstMachineBackrefId() const
4810{
4811 if (!m->backRefs.size())
4812 return NULL;
4813
4814 return &m->backRefs.front().machineId;
4815}
4816
4817/**
4818 * Internal method which returns a machine that either this medium or one of its children
4819 * is attached to. This is used for finding a replacement media registry when an existing
4820 * media registry is about to be deleted in VirtualBox::unregisterMachine().
4821 *
4822 * Must have caller + locking, *and* caller must hold the media tree lock!
4823 * @param aId Id to ignore when looking for backrefs.
4824 * @return
4825 */
4826const Guid* Medium::i_getAnyMachineBackref(const Guid &aId) const
4827{
4828 std::list<const Medium *> llMediaTodo;
4829 llMediaTodo.push_back(this);
4830
4831 while (!llMediaTodo.empty())
4832 {
4833 const Medium *pMedium = llMediaTodo.front();
4834 llMediaTodo.pop_front();
4835
4836 if (pMedium->m->backRefs.size())
4837 {
4838 if (pMedium->m->backRefs.front().machineId != aId)
4839 return &pMedium->m->backRefs.front().machineId;
4840 if (pMedium->m->backRefs.size() > 1)
4841 {
4842 BackRefList::const_iterator it = pMedium->m->backRefs.begin();
4843 ++it;
4844 return &it->machineId;
4845 }
4846 }
4847
4848 MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
4849 MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
4850 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
4851 llMediaTodo.push_back(*it);
4852 }
4853
4854 return NULL;
4855}
4856
4857const Guid* Medium::i_getFirstMachineBackrefSnapshotId() const
4858{
4859 if (!m->backRefs.size())
4860 return NULL;
4861
4862 const BackRef &ref = m->backRefs.front();
4863 if (ref.llSnapshotIds.empty())
4864 return NULL;
4865
4866 return &ref.llSnapshotIds.front().snapshotId;
4867}
4868
4869size_t Medium::i_getMachineBackRefCount() const
4870{
4871 return m->backRefs.size();
4872}
4873
4874#ifdef DEBUG
4875/**
4876 * Debugging helper that gets called after VirtualBox initialization that writes all
4877 * machine backreferences to the debug log.
4878 */
4879void Medium::i_dumpBackRefs()
4880{
4881 AutoCaller autoCaller(this);
4882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4883
4884 LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
4885
4886 for (BackRefList::iterator it2 = m->backRefs.begin();
4887 it2 != m->backRefs.end();
4888 ++it2)
4889 {
4890 const BackRef &ref = *it2;
4891 LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d, iRefCnt: %d)\n", ref.machineId.raw(), ref.fInCurState, ref.iRefCnt));
4892
4893 for (std::list<SnapshotRef>::const_iterator jt2 = it2->llSnapshotIds.begin();
4894 jt2 != it2->llSnapshotIds.end();
4895 ++jt2)
4896 {
4897 const Guid &id = jt2->snapshotId;
4898 LogFlowThisFunc((" Backref from snapshot {%RTuuid} (iRefCnt = %d)\n", id.raw(), jt2->iRefCnt));
4899 }
4900 }
4901}
4902#endif
4903
4904/**
4905 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
4906 * of this media and updates it if necessary to reflect the new location.
4907 *
4908 * @param strOldPath Old path (full).
4909 * @param strNewPath New path (full).
4910 *
4911 * @note Locks this object for writing.
4912 */
4913HRESULT Medium::i_updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
4914{
4915 AssertReturn(!strOldPath.isEmpty(), E_FAIL);
4916 AssertReturn(!strNewPath.isEmpty(), E_FAIL);
4917
4918 AutoCaller autoCaller(this);
4919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4920
4921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4922
4923 LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
4924
4925 const char *pcszMediumPath = m->strLocationFull.c_str();
4926
4927 if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
4928 {
4929 Utf8Str newPath(strNewPath);
4930 newPath.append(pcszMediumPath + strOldPath.length());
4931 unconst(m->strLocationFull) = newPath;
4932
4933 m->pVirtualBox->i_onMediumConfigChanged(this);
4934
4935 LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
4936 // we changed something
4937 return S_OK;
4938 }
4939
4940 // no change was necessary, signal error which the caller needs to interpret
4941 return VBOX_E_FILE_ERROR;
4942}
4943
4944/**
4945 * Returns the base medium of the media chain this medium is part of.
4946 *
4947 * The base medium is found by walking up the parent-child relationship axis.
4948 * If the medium doesn't have a parent (i.e. it's a base medium), it
4949 * returns itself in response to this method.
4950 *
4951 * @param aLevel Where to store the number of ancestors of this medium
4952 * (zero for the base), may be @c NULL.
4953 *
4954 * @note Locks medium tree for reading.
4955 */
4956ComObjPtr<Medium> Medium::i_getBase(uint32_t *aLevel /*= NULL*/)
4957{
4958 ComObjPtr<Medium> pBase;
4959
4960 /* it is possible that some previous/concurrent uninit has already cleared
4961 * the pVirtualBox reference, and in this case we don't need to continue */
4962 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4963 if (!pVirtualBox)
4964 return pBase;
4965
4966 /* we access m->pParent */
4967 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4968
4969 AutoCaller autoCaller(this);
4970 AssertReturn(autoCaller.isOk(), pBase);
4971
4972 pBase = this;
4973 uint32_t level = 0;
4974
4975 if (m->pParent)
4976 {
4977 for (;;)
4978 {
4979 AutoCaller baseCaller(pBase);
4980 AssertReturn(baseCaller.isOk(), pBase);
4981
4982 if (pBase->m->pParent.isNull())
4983 break;
4984
4985 pBase = pBase->m->pParent;
4986 ++level;
4987 }
4988 }
4989
4990 if (aLevel != NULL)
4991 *aLevel = level;
4992
4993 return pBase;
4994}
4995
4996/**
4997 * Returns the depth of this medium in the media chain.
4998 *
4999 * @note Locks medium tree for reading.
5000 */
5001uint32_t Medium::i_getDepth()
5002{
5003 /* it is possible that some previous/concurrent uninit has already cleared
5004 * the pVirtualBox reference, and in this case we don't need to continue */
5005 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
5006 if (!pVirtualBox)
5007 return 1;
5008
5009 /* we access m->pParent */
5010 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5011
5012 uint32_t cDepth = 0;
5013 ComObjPtr<Medium> pMedium(this);
5014 while (!pMedium.isNull())
5015 {
5016 AutoCaller autoCaller(this);
5017 AssertReturn(autoCaller.isOk(), cDepth + 1);
5018
5019 pMedium = pMedium->m->pParent;
5020 cDepth++;
5021 }
5022
5023 return cDepth;
5024}
5025
5026/**
5027 * Returns @c true if this medium cannot be modified because it has
5028 * dependents (children) or is part of the snapshot. Related to the medium
5029 * type and posterity, not to the current media state.
5030 *
5031 * @note Locks this object and medium tree for reading.
5032 */
5033bool Medium::i_isReadOnly()
5034{
5035 /* it is possible that some previous/concurrent uninit has already cleared
5036 * the pVirtualBox reference, and in this case we don't need to continue */
5037 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
5038 if (!pVirtualBox)
5039 return false;
5040
5041 /* we access children */
5042 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5043
5044 AutoCaller autoCaller(this);
5045 AssertComRCReturn(autoCaller.rc(), false);
5046
5047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5048
5049 switch (m->type)
5050 {
5051 case MediumType_Normal:
5052 {
5053 if (i_getChildren().size() != 0)
5054 return true;
5055
5056 for (BackRefList::const_iterator it = m->backRefs.begin();
5057 it != m->backRefs.end(); ++it)
5058 if (it->llSnapshotIds.size() != 0)
5059 return true;
5060
5061 if (m->variant & MediumVariant_VmdkStreamOptimized)
5062 return true;
5063
5064 return false;
5065 }
5066 case MediumType_Immutable:
5067 case MediumType_MultiAttach:
5068 return true;
5069 case MediumType_Writethrough:
5070 case MediumType_Shareable:
5071 case MediumType_Readonly: /* explicit readonly media has no diffs */
5072 return false;
5073 default:
5074 break;
5075 }
5076
5077 AssertFailedReturn(false);
5078}
5079
5080/**
5081 * Internal method to update the medium's id. Must have caller + locking!
5082 * @return
5083 */
5084void Medium::i_updateId(const Guid &id)
5085{
5086 unconst(m->id) = id;
5087}
5088
5089/**
5090 * Saves the settings of one medium.
5091 *
5092 * @note Caller MUST take care of the medium tree lock and caller.
5093 *
5094 * @param data Settings struct to be updated.
5095 * @param strHardDiskFolder Folder for which paths should be relative.
5096 */
5097void Medium::i_saveSettingsOne(settings::Medium &data, const Utf8Str &strHardDiskFolder)
5098{
5099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5100
5101 data.uuid = m->id;
5102
5103 // make path relative if needed
5104 if ( !strHardDiskFolder.isEmpty()
5105 && RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
5106 )
5107 data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
5108 else
5109 data.strLocation = m->strLocationFull;
5110 data.strFormat = m->strFormat;
5111
5112 /* optional, only for diffs, default is false */
5113 if (m->pParent)
5114 data.fAutoReset = m->autoReset;
5115 else
5116 data.fAutoReset = false;
5117
5118 /* optional */
5119 data.strDescription = m->strDescription;
5120
5121 /* optional properties */
5122 data.properties.clear();
5123
5124 /* handle iSCSI initiator secrets transparently */
5125 bool fHaveInitiatorSecretEncrypted = false;
5126 Utf8Str strCiphertext;
5127 settings::StringsMap::const_iterator itPln = m->mapProperties.find("InitiatorSecret");
5128 if ( itPln != m->mapProperties.end()
5129 && !itPln->second.isEmpty())
5130 {
5131 /* Encrypt the plain secret. If that does not work (i.e. no or wrong settings key
5132 * specified), just use the encrypted secret (if there is any). */
5133 int rc = m->pVirtualBox->i_encryptSetting(itPln->second, &strCiphertext);
5134 if (RT_SUCCESS(rc))
5135 fHaveInitiatorSecretEncrypted = true;
5136 }
5137 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
5138 it != m->mapProperties.end();
5139 ++it)
5140 {
5141 /* only save properties that have non-default values */
5142 if (!it->second.isEmpty())
5143 {
5144 const Utf8Str &name = it->first;
5145 const Utf8Str &value = it->second;
5146 bool fCreateOnly = false;
5147 for (MediumFormat::PropertyArray::const_iterator itf = m->formatObj->i_getProperties().begin();
5148 itf != m->formatObj->i_getProperties().end();
5149 ++itf)
5150 {
5151 if ( itf->strName.equals(name)
5152 && (itf->flags & VD_CFGKEY_CREATEONLY))
5153 {
5154 fCreateOnly = true;
5155 break;
5156 }
5157 }
5158 if (!fCreateOnly)
5159 /* do NOT store the plain InitiatorSecret */
5160 if ( !fHaveInitiatorSecretEncrypted
5161 || !name.equals("InitiatorSecret"))
5162 data.properties[name] = value;
5163 }
5164 }
5165 if (fHaveInitiatorSecretEncrypted)
5166 data.properties["InitiatorSecretEncrypted"] = strCiphertext;
5167
5168 /* only for base media */
5169 if (m->pParent.isNull())
5170 data.hdType = m->type;
5171}
5172
5173/**
5174 * Saves medium data by putting it into the provided data structure.
5175 * The settings of all children is saved, too.
5176 *
5177 * @param data Settings struct to be updated.
5178 * @param strHardDiskFolder Folder for which paths should be relative.
5179 *
5180 * @note Locks this object, medium tree and children for reading.
5181 */
5182HRESULT Medium::i_saveSettings(settings::Medium &data,
5183 const Utf8Str &strHardDiskFolder)
5184{
5185 /* we access m->pParent */
5186 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5187
5188 AutoCaller autoCaller(this);
5189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5190
5191 MediaList llMediaTodo;
5192 llMediaTodo.push_back(this);
5193 std::list<settings::Medium *> llSettingsTodo;
5194 llSettingsTodo.push_back(&data);
5195
5196 while (!llMediaTodo.empty())
5197 {
5198 ComObjPtr<Medium> pMedium = llMediaTodo.front();
5199 llMediaTodo.pop_front();
5200 settings::Medium *current = llSettingsTodo.front();
5201 llSettingsTodo.pop_front();
5202
5203 AutoCaller mediumCaller(pMedium);
5204 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5205
5206 pMedium->i_saveSettingsOne(*current, strHardDiskFolder);
5207
5208 /* save all children */
5209 MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
5210 MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
5211 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
5212 {
5213 llMediaTodo.push_back(*it);
5214 current->llChildren.push_back(settings::Medium::Empty);
5215 llSettingsTodo.push_back(&current->llChildren.back());
5216 }
5217 }
5218
5219 return S_OK;
5220}
5221
5222/**
5223 * Constructs a medium lock list for this medium. The lock is not taken.
5224 *
5225 * @note Caller MUST NOT hold the media tree or medium lock.
5226 *
5227 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
5228 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
5229 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
5230 * @param pToLockWrite If not NULL, associate a write lock with this medium object.
5231 * @param fMediumLockWriteAll Whether to associate a write lock to all other media too.
5232 * @param pToBeParent Medium which will become the parent of this medium.
5233 * @param mediumLockList Where to store the resulting list.
5234 */
5235HRESULT Medium::i_createMediumLockList(bool fFailIfInaccessible,
5236 Medium *pToLockWrite,
5237 bool fMediumLockWriteAll,
5238 Medium *pToBeParent,
5239 MediumLockList &mediumLockList)
5240{
5241 /** @todo r=klaus this needs to be reworked, as the code below uses
5242 * i_getParent without holding the tree lock, and changing this is
5243 * a significant amount of effort. */
5244 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5245 Assert(!isWriteLockOnCurrentThread());
5246
5247 AutoCaller autoCaller(this);
5248 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5249
5250 HRESULT rc = S_OK;
5251
5252 /* paranoid sanity checking if the medium has a to-be parent medium */
5253 if (pToBeParent)
5254 {
5255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5256 ComAssertRet(i_getParent().isNull(), E_FAIL);
5257 ComAssertRet(i_getChildren().size() == 0, E_FAIL);
5258 }
5259
5260 ErrorInfoKeeper eik;
5261 MultiResult mrc(S_OK);
5262
5263 ComObjPtr<Medium> pMedium = this;
5264 while (!pMedium.isNull())
5265 {
5266 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5267
5268 /* Accessibility check must be first, otherwise locking interferes
5269 * with getting the medium state. Lock lists are not created for
5270 * fun, and thus getting the medium status is no luxury. */
5271 MediumState_T mediumState = pMedium->i_getState();
5272 if (mediumState == MediumState_Inaccessible)
5273 {
5274 alock.release();
5275 rc = pMedium->i_queryInfo(false /* fSetImageId */, false /* fSetParentId */,
5276 autoCaller);
5277 alock.acquire();
5278 if (FAILED(rc)) return rc;
5279
5280 mediumState = pMedium->i_getState();
5281 if (mediumState == MediumState_Inaccessible)
5282 {
5283 // ignore inaccessible ISO media and silently return S_OK,
5284 // otherwise VM startup (esp. restore) may fail without good reason
5285 if (!fFailIfInaccessible)
5286 return S_OK;
5287
5288 // otherwise report an error
5289 Bstr error;
5290 rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
5291 if (FAILED(rc)) return rc;
5292
5293 /* collect multiple errors */
5294 eik.restore();
5295 Assert(!error.isEmpty());
5296 mrc = setError(E_FAIL,
5297 "%ls",
5298 error.raw());
5299 // error message will be something like
5300 // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
5301 eik.fetch();
5302 }
5303 }
5304
5305 if (pMedium == pToLockWrite)
5306 mediumLockList.Prepend(pMedium, true);
5307 else
5308 mediumLockList.Prepend(pMedium, fMediumLockWriteAll);
5309
5310 pMedium = pMedium->i_getParent();
5311 if (pMedium.isNull() && pToBeParent)
5312 {
5313 pMedium = pToBeParent;
5314 pToBeParent = NULL;
5315 }
5316 }
5317
5318 return mrc;
5319}
5320
5321/**
5322 * Creates a new differencing storage unit using the format of the given target
5323 * medium and the location. Note that @c aTarget must be NotCreated.
5324 *
5325 * The @a aMediumLockList parameter contains the associated medium lock list,
5326 * which must be in locked state. If @a aWait is @c true then the caller is
5327 * responsible for unlocking.
5328 *
5329 * If @a aProgress is not NULL but the object it points to is @c null then a
5330 * new progress object will be created and assigned to @a *aProgress on
5331 * success, otherwise the existing progress object is used. If @a aProgress is
5332 * NULL, then no progress object is created/used at all.
5333 *
5334 * When @a aWait is @c false, this method will create a thread to perform the
5335 * create operation asynchronously and will return immediately. Otherwise, it
5336 * will perform the operation on the calling thread and will not return to the
5337 * caller until the operation is completed. Note that @a aProgress cannot be
5338 * NULL when @a aWait is @c false (this method will assert in this case).
5339 *
5340 * @param aTarget Target medium.
5341 * @param aVariant Precise medium variant to create.
5342 * @param aMediumLockList List of media which should be locked.
5343 * @param aProgress Where to find/store a Progress object to track
5344 * operation completion.
5345 * @param aWait @c true if this method should block instead of
5346 * creating an asynchronous thread.
5347 * @param aNotify Notify about mediums which metadatа are changed
5348 * during execution of the function.
5349 *
5350 * @note Locks this object and @a aTarget for writing.
5351 */
5352HRESULT Medium::i_createDiffStorage(ComObjPtr<Medium> &aTarget,
5353 MediumVariant_T aVariant,
5354 MediumLockList *aMediumLockList,
5355 ComObjPtr<Progress> *aProgress,
5356 bool aWait,
5357 bool aNotify)
5358{
5359 AssertReturn(!aTarget.isNull(), E_FAIL);
5360 AssertReturn(aMediumLockList, E_FAIL);
5361 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5362
5363 AutoCaller autoCaller(this);
5364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5365
5366 AutoCaller targetCaller(aTarget);
5367 if (FAILED(targetCaller.rc())) return targetCaller.rc();
5368
5369 HRESULT rc = S_OK;
5370 ComObjPtr<Progress> pProgress;
5371 Medium::Task *pTask = NULL;
5372
5373 try
5374 {
5375 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
5376
5377 ComAssertThrow( m->type != MediumType_Writethrough
5378 && m->type != MediumType_Shareable
5379 && m->type != MediumType_Readonly, E_FAIL);
5380 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
5381
5382 if (aTarget->m->state != MediumState_NotCreated)
5383 throw aTarget->i_setStateError();
5384
5385 /* Check that the medium is not attached to the current state of
5386 * any VM referring to it. */
5387 for (BackRefList::const_iterator it = m->backRefs.begin();
5388 it != m->backRefs.end();
5389 ++it)
5390 {
5391 if (it->fInCurState)
5392 {
5393 /* Note: when a VM snapshot is being taken, all normal media
5394 * attached to the VM in the current state will be, as an
5395 * exception, also associated with the snapshot which is about
5396 * to create (see SnapshotMachine::init()) before deassociating
5397 * them from the current state (which takes place only on
5398 * success in Machine::fixupHardDisks()), so that the size of
5399 * snapshotIds will be 1 in this case. The extra condition is
5400 * used to filter out this legal situation. */
5401 if (it->llSnapshotIds.size() == 0)
5402 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5403 tr("Medium '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing media based on it may be created until it is detached"),
5404 m->strLocationFull.c_str(), it->machineId.raw());
5405
5406 Assert(it->llSnapshotIds.size() == 1);
5407 }
5408 }
5409
5410 if (aProgress != NULL)
5411 {
5412 /* use the existing progress object... */
5413 pProgress = *aProgress;
5414
5415 /* ...but create a new one if it is null */
5416 if (pProgress.isNull())
5417 {
5418 pProgress.createObject();
5419 rc = pProgress->init(m->pVirtualBox,
5420 static_cast<IMedium*>(this),
5421 BstrFmt(tr("Creating differencing medium storage unit '%s'"),
5422 aTarget->m->strLocationFull.c_str()).raw(),
5423 TRUE /* aCancelable */);
5424 if (FAILED(rc))
5425 throw rc;
5426 }
5427 }
5428
5429 /* setup task object to carry out the operation sync/async */
5430 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
5431 aMediumLockList,
5432 aWait /* fKeepMediumLockList */,
5433 aNotify);
5434 rc = pTask->rc();
5435 AssertComRC(rc);
5436 if (FAILED(rc))
5437 throw rc;
5438
5439 /* register a task (it will deregister itself when done) */
5440 ++m->numCreateDiffTasks;
5441 Assert(m->numCreateDiffTasks != 0); /* overflow? */
5442
5443 aTarget->m->state = MediumState_Creating;
5444 }
5445 catch (HRESULT aRC) { rc = aRC; }
5446
5447 if (SUCCEEDED(rc))
5448 {
5449 if (aWait)
5450 {
5451 rc = pTask->runNow();
5452 delete pTask;
5453 }
5454 else
5455 rc = pTask->createThread();
5456 pTask = NULL;
5457 if (SUCCEEDED(rc) && aProgress != NULL)
5458 *aProgress = pProgress;
5459 }
5460 else if (pTask != NULL)
5461 delete pTask;
5462
5463 return rc;
5464}
5465
5466/**
5467 * Returns a preferred format for differencing media.
5468 */
5469Utf8Str Medium::i_getPreferredDiffFormat()
5470{
5471 AutoCaller autoCaller(this);
5472 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
5473
5474 /* check that our own format supports diffs */
5475 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5476 {
5477 /* use the default format if not */
5478 Utf8Str tmp;
5479 m->pVirtualBox->i_getDefaultHardDiskFormat(tmp);
5480 return tmp;
5481 }
5482
5483 /* m->strFormat is const, no need to lock */
5484 return m->strFormat;
5485}
5486
5487/**
5488 * Returns a preferred variant for differencing media.
5489 */
5490MediumVariant_T Medium::i_getPreferredDiffVariant()
5491{
5492 AutoCaller autoCaller(this);
5493 AssertComRCReturn(autoCaller.rc(), MediumVariant_Standard);
5494
5495 /* check that our own format supports diffs */
5496 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5497 return MediumVariant_Standard;
5498
5499 /* m->variant is const, no need to lock */
5500 ULONG mediumVariantFlags = (ULONG)m->variant;
5501 mediumVariantFlags &= ~(ULONG)(MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized);
5502 mediumVariantFlags |= MediumVariant_Diff;
5503 return (MediumVariant_T)mediumVariantFlags;
5504}
5505
5506/**
5507 * Implementation for the public Medium::Close() with the exception of calling
5508 * VirtualBox::saveRegistries(), in case someone wants to call this for several
5509 * media.
5510 *
5511 * After this returns with success, uninit() has been called on the medium, and
5512 * the object is no longer usable ("not ready" state).
5513 *
5514 * @param autoCaller AutoCaller instance which must have been created on the caller's
5515 * stack for this medium. This gets released hereupon
5516 * which the Medium instance gets uninitialized.
5517 * @return
5518 */
5519HRESULT Medium::i_close(AutoCaller &autoCaller)
5520{
5521 // must temporarily drop the caller, need the tree lock first
5522 autoCaller.release();
5523
5524 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
5525 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
5526 this->lockHandle()
5527 COMMA_LOCKVAL_SRC_POS);
5528
5529 autoCaller.add();
5530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5531
5532 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5533 while (m->queryInfoRunning)
5534 {
5535 autoCaller.release();
5536 multilock.release();
5537 /* Must not hold the media tree lock, as Medium::i_queryInfo needs
5538 * this lock and thus we would run into a deadlock here. */
5539 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5540 /* must not hold the object lock now */
5541 Assert(!isWriteLockOnCurrentThread());
5542 {
5543 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5544 }
5545 multilock.acquire();
5546 autoCaller.add();
5547 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5548 }
5549
5550 LogFlowFunc(("ENTER for %s\n", i_getLocationFull().c_str()));
5551
5552 bool wasCreated = true;
5553
5554 switch (m->state)
5555 {
5556 case MediumState_NotCreated:
5557 wasCreated = false;
5558 break;
5559 case MediumState_Created:
5560 case MediumState_Inaccessible:
5561 break;
5562 default:
5563 return i_setStateError();
5564 }
5565
5566 if (m->backRefs.size() != 0)
5567 return setError(VBOX_E_OBJECT_IN_USE,
5568 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines", "",
5569 m->backRefs.size()),
5570 m->strLocationFull.c_str(), m->backRefs.size());
5571
5572 // perform extra media-dependent close checks
5573 HRESULT rc = i_canClose();
5574 if (FAILED(rc)) return rc;
5575
5576 m->fClosing = true;
5577
5578 if (wasCreated)
5579 {
5580 // remove from the list of known media before performing actual
5581 // uninitialization (to keep the media registry consistent on
5582 // failure to do so)
5583 rc = i_unregisterWithVirtualBox();
5584 if (FAILED(rc)) return rc;
5585
5586 multilock.release();
5587 // Release the AutoCaller now, as otherwise uninit() will simply hang.
5588 // Needs to be done before mark the registries as modified and saving
5589 // the registry, as otherwise there may be a deadlock with someone else
5590 // closing this object while we're in i_saveModifiedRegistries(), which
5591 // needs the media tree lock, which the other thread holds until after
5592 // uninit() below.
5593 autoCaller.release();
5594 i_markRegistriesModified();
5595 m->pVirtualBox->i_saveModifiedRegistries();
5596 }
5597 else
5598 {
5599 multilock.release();
5600 // release the AutoCaller, as otherwise uninit() will simply hang
5601 autoCaller.release();
5602 }
5603
5604 // Keep the locks held until after uninit, as otherwise the consistency
5605 // of the medium tree cannot be guaranteed.
5606 uninit();
5607
5608 LogFlowFuncLeave();
5609
5610 return rc;
5611}
5612
5613/**
5614 * Deletes the medium storage unit.
5615 *
5616 * If @a aProgress is not NULL but the object it points to is @c null then a new
5617 * progress object will be created and assigned to @a *aProgress on success,
5618 * otherwise the existing progress object is used. If Progress is NULL, then no
5619 * progress object is created/used at all.
5620 *
5621 * When @a aWait is @c false, this method will create a thread to perform the
5622 * delete operation asynchronously and will return immediately. Otherwise, it
5623 * will perform the operation on the calling thread and will not return to the
5624 * caller until the operation is completed. Note that @a aProgress cannot be
5625 * NULL when @a aWait is @c false (this method will assert in this case).
5626 *
5627 * @param aProgress Where to find/store a Progress object to track operation
5628 * completion.
5629 * @param aWait @c true if this method should block instead of creating
5630 * an asynchronous thread.
5631 * @param aNotify Notify about mediums which metadatа are changed
5632 * during execution of the function.
5633 *
5634 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
5635 * writing.
5636 */
5637HRESULT Medium::i_deleteStorage(ComObjPtr<Progress> *aProgress,
5638 bool aWait, bool aNotify)
5639{
5640 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5641
5642 HRESULT rc = S_OK;
5643 ComObjPtr<Progress> pProgress;
5644 Medium::Task *pTask = NULL;
5645
5646 try
5647 {
5648 /* we're accessing the media tree, and i_canClose() needs it too */
5649 AutoWriteLock treelock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5650
5651 AutoCaller autoCaller(this);
5652 AssertComRCThrowRC(autoCaller.rc());
5653
5654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5655
5656 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, i_getLocationFull().c_str() ));
5657
5658 if ( !(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
5659 | MediumFormatCapabilities_CreateFixed)))
5660 throw setError(VBOX_E_NOT_SUPPORTED,
5661 tr("Medium format '%s' does not support storage deletion"),
5662 m->strFormat.c_str());
5663
5664 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5665 /** @todo r=klaus would be great if this could be moved to the async
5666 * part of the operation as it can take quite a while */
5667 while (m->queryInfoRunning)
5668 {
5669 alock.release();
5670 autoCaller.release();
5671 treelock.release();
5672 /* Must not hold the media tree lock or the object lock, as
5673 * Medium::i_queryInfo needs this lock and thus we would run
5674 * into a deadlock here. */
5675 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5676 Assert(!isWriteLockOnCurrentThread());
5677 {
5678 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5679 }
5680 treelock.acquire();
5681 autoCaller.add();
5682 AssertComRCThrowRC(autoCaller.rc());
5683 alock.acquire();
5684 }
5685
5686 /* Note that we are fine with Inaccessible state too: a) for symmetry
5687 * with create calls and b) because it doesn't really harm to try, if
5688 * it is really inaccessible, the delete operation will fail anyway.
5689 * Accepting Inaccessible state is especially important because all
5690 * registered media are initially Inaccessible upon VBoxSVC startup
5691 * until COMGETTER(RefreshState) is called. Accept Deleting state
5692 * because some callers need to put the medium in this state early
5693 * to prevent races. */
5694 switch (m->state)
5695 {
5696 case MediumState_Created:
5697 case MediumState_Deleting:
5698 case MediumState_Inaccessible:
5699 break;
5700 default:
5701 throw i_setStateError();
5702 }
5703
5704 if (m->backRefs.size() != 0)
5705 {
5706 Utf8Str strMachines;
5707 for (BackRefList::const_iterator it = m->backRefs.begin();
5708 it != m->backRefs.end();
5709 ++it)
5710 {
5711 const BackRef &b = *it;
5712 if (strMachines.length())
5713 strMachines.append(", ");
5714 strMachines.append(b.machineId.toString().c_str());
5715 }
5716#ifdef DEBUG
5717 i_dumpBackRefs();
5718#endif
5719 throw setError(VBOX_E_OBJECT_IN_USE,
5720 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s",
5721 "", m->backRefs.size()),
5722 m->strLocationFull.c_str(),
5723 m->backRefs.size(),
5724 strMachines.c_str());
5725 }
5726
5727 rc = i_canClose();
5728 if (FAILED(rc))
5729 throw rc;
5730
5731 /* go to Deleting state, so that the medium is not actually locked */
5732 if (m->state != MediumState_Deleting)
5733 {
5734 rc = i_markForDeletion();
5735 if (FAILED(rc))
5736 throw rc;
5737 }
5738
5739 /* Build the medium lock list. */
5740 MediumLockList *pMediumLockList(new MediumLockList());
5741 alock.release();
5742 autoCaller.release();
5743 treelock.release();
5744 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5745 this /* pToLockWrite */,
5746 false /* fMediumLockWriteAll */,
5747 NULL,
5748 *pMediumLockList);
5749 treelock.acquire();
5750 autoCaller.add();
5751 AssertComRCThrowRC(autoCaller.rc());
5752 alock.acquire();
5753 if (FAILED(rc))
5754 {
5755 delete pMediumLockList;
5756 throw rc;
5757 }
5758
5759 alock.release();
5760 autoCaller.release();
5761 treelock.release();
5762 rc = pMediumLockList->Lock();
5763 treelock.acquire();
5764 autoCaller.add();
5765 AssertComRCThrowRC(autoCaller.rc());
5766 alock.acquire();
5767 if (FAILED(rc))
5768 {
5769 delete pMediumLockList;
5770 throw setError(rc,
5771 tr("Failed to lock media when deleting '%s'"),
5772 i_getLocationFull().c_str());
5773 }
5774
5775 /* try to remove from the list of known media before performing
5776 * actual deletion (we favor the consistency of the media registry
5777 * which would have been broken if unregisterWithVirtualBox() failed
5778 * after we successfully deleted the storage) */
5779 rc = i_unregisterWithVirtualBox();
5780 if (FAILED(rc))
5781 throw rc;
5782 // no longer need lock
5783 alock.release();
5784 autoCaller.release();
5785 treelock.release();
5786 i_markRegistriesModified();
5787
5788 if (aProgress != NULL)
5789 {
5790 /* use the existing progress object... */
5791 pProgress = *aProgress;
5792
5793 /* ...but create a new one if it is null */
5794 if (pProgress.isNull())
5795 {
5796 pProgress.createObject();
5797 rc = pProgress->init(m->pVirtualBox,
5798 static_cast<IMedium*>(this),
5799 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
5800 FALSE /* aCancelable */);
5801 if (FAILED(rc))
5802 throw rc;
5803 }
5804 }
5805
5806 /* setup task object to carry out the operation sync/async */
5807 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList, false, aNotify);
5808 rc = pTask->rc();
5809 AssertComRC(rc);
5810 if (FAILED(rc))
5811 throw rc;
5812 }
5813 catch (HRESULT aRC) { rc = aRC; }
5814
5815 if (SUCCEEDED(rc))
5816 {
5817 if (aWait)
5818 {
5819 rc = pTask->runNow();
5820 delete pTask;
5821 }
5822 else
5823 rc = pTask->createThread();
5824 pTask = NULL;
5825 if (SUCCEEDED(rc) && aProgress != NULL)
5826 *aProgress = pProgress;
5827 }
5828 else
5829 {
5830 if (pTask)
5831 delete pTask;
5832
5833 /* Undo deleting state if necessary. */
5834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5835 /* Make sure that any error signalled by unmarkForDeletion() is not
5836 * ending up in the error list (if the caller uses MultiResult). It
5837 * usually is spurious, as in most cases the medium hasn't been marked
5838 * for deletion when the error was thrown above. */
5839 ErrorInfoKeeper eik;
5840 i_unmarkForDeletion();
5841 }
5842
5843 return rc;
5844}
5845
5846/**
5847 * Mark a medium for deletion.
5848 *
5849 * @note Caller must hold the write lock on this medium!
5850 */
5851HRESULT Medium::i_markForDeletion()
5852{
5853 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5854 switch (m->state)
5855 {
5856 case MediumState_Created:
5857 case MediumState_Inaccessible:
5858 m->preLockState = m->state;
5859 m->state = MediumState_Deleting;
5860 return S_OK;
5861 default:
5862 return i_setStateError();
5863 }
5864}
5865
5866/**
5867 * Removes the "mark for deletion".
5868 *
5869 * @note Caller must hold the write lock on this medium!
5870 */
5871HRESULT Medium::i_unmarkForDeletion()
5872{
5873 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5874 switch (m->state)
5875 {
5876 case MediumState_Deleting:
5877 m->state = m->preLockState;
5878 return S_OK;
5879 default:
5880 return i_setStateError();
5881 }
5882}
5883
5884/**
5885 * Mark a medium for deletion which is in locked state.
5886 *
5887 * @note Caller must hold the write lock on this medium!
5888 */
5889HRESULT Medium::i_markLockedForDeletion()
5890{
5891 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5892 if ( ( m->state == MediumState_LockedRead
5893 || m->state == MediumState_LockedWrite)
5894 && m->preLockState == MediumState_Created)
5895 {
5896 m->preLockState = MediumState_Deleting;
5897 return S_OK;
5898 }
5899 else
5900 return i_setStateError();
5901}
5902
5903/**
5904 * Removes the "mark for deletion" for a medium in locked state.
5905 *
5906 * @note Caller must hold the write lock on this medium!
5907 */
5908HRESULT Medium::i_unmarkLockedForDeletion()
5909{
5910 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5911 if ( ( m->state == MediumState_LockedRead
5912 || m->state == MediumState_LockedWrite)
5913 && m->preLockState == MediumState_Deleting)
5914 {
5915 m->preLockState = MediumState_Created;
5916 return S_OK;
5917 }
5918 else
5919 return i_setStateError();
5920}
5921
5922/**
5923 * Queries the preferred merge direction from this to the other medium, i.e.
5924 * the one which requires the least amount of I/O and therefore time and
5925 * disk consumption.
5926 *
5927 * @returns Status code.
5928 * @retval E_FAIL in case determining the merge direction fails for some reason,
5929 * for example if getting the size of the media fails. There is no
5930 * error set though and the caller is free to continue to find out
5931 * what was going wrong later. Leaves fMergeForward unset.
5932 * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
5933 * An error is set.
5934 * @param pOther The other medium to merge with.
5935 * @param fMergeForward Resulting preferred merge direction (out).
5936 */
5937HRESULT Medium::i_queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
5938 bool &fMergeForward)
5939{
5940 AssertReturn(pOther != NULL, E_FAIL);
5941 AssertReturn(pOther != this, E_FAIL);
5942
5943 HRESULT rc = S_OK;
5944 bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
5945
5946 try
5947 {
5948 // locking: we need the tree lock first because we access parent pointers
5949 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5950
5951 AutoCaller autoCaller(this);
5952 AssertComRCThrowRC(autoCaller.rc());
5953
5954 AutoCaller otherCaller(pOther);
5955 AssertComRCThrowRC(otherCaller.rc());
5956
5957 /* more sanity checking and figuring out the current merge direction */
5958 ComObjPtr<Medium> pMedium = i_getParent();
5959 while (!pMedium.isNull() && pMedium != pOther)
5960 pMedium = pMedium->i_getParent();
5961 if (pMedium == pOther)
5962 fThisParent = false;
5963 else
5964 {
5965 pMedium = pOther->i_getParent();
5966 while (!pMedium.isNull() && pMedium != this)
5967 pMedium = pMedium->i_getParent();
5968 if (pMedium == this)
5969 fThisParent = true;
5970 else
5971 {
5972 Utf8Str tgtLoc;
5973 {
5974 AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
5975 tgtLoc = pOther->i_getLocationFull();
5976 }
5977
5978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5979 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5980 tr("Media '%s' and '%s' are unrelated"),
5981 m->strLocationFull.c_str(), tgtLoc.c_str());
5982 }
5983 }
5984
5985 /*
5986 * Figure out the preferred merge direction. The current way is to
5987 * get the current sizes of file based images and select the merge
5988 * direction depending on the size.
5989 *
5990 * Can't use the VD API to get current size here as the media might
5991 * be write locked by a running VM. Resort to RTFileQuerySize().
5992 */
5993 int vrc = VINF_SUCCESS;
5994 uint64_t cbMediumThis = 0;
5995 uint64_t cbMediumOther = 0;
5996
5997 if (i_isMediumFormatFile() && pOther->i_isMediumFormatFile())
5998 {
5999 vrc = RTFileQuerySizeByPath(this->i_getLocationFull().c_str(), &cbMediumThis);
6000 if (RT_SUCCESS(vrc))
6001 {
6002 vrc = RTFileQuerySizeByPath(pOther->i_getLocationFull().c_str(),
6003 &cbMediumOther);
6004 }
6005
6006 if (RT_FAILURE(vrc))
6007 rc = E_FAIL;
6008 else
6009 {
6010 /*
6011 * Check which merge direction might be more optimal.
6012 * This method is not bullet proof of course as there might
6013 * be overlapping blocks in the images so the file size is
6014 * not the best indicator but it is good enough for our purpose
6015 * and everything else is too complicated, especially when the
6016 * media are used by a running VM.
6017 */
6018
6019 uint32_t mediumVariants = MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized;
6020 uint32_t mediumCaps = MediumFormatCapabilities_CreateDynamic | MediumFormatCapabilities_File;
6021
6022 bool fDynamicOther = pOther->i_getMediumFormat()->i_getCapabilities() & mediumCaps
6023 && pOther->i_getVariant() & ~mediumVariants;
6024 bool fDynamicThis = i_getMediumFormat()->i_getCapabilities() & mediumCaps
6025 && i_getVariant() & ~mediumVariants;
6026 bool fMergeIntoThis = (fDynamicThis && !fDynamicOther)
6027 || (fDynamicThis == fDynamicOther && cbMediumThis > cbMediumOther);
6028 fMergeForward = fMergeIntoThis != fThisParent;
6029 }
6030 }
6031 }
6032 catch (HRESULT aRC) { rc = aRC; }
6033
6034 return rc;
6035}
6036
6037/**
6038 * Prepares this (source) medium, target medium and all intermediate media
6039 * for the merge operation.
6040 *
6041 * This method is to be called prior to calling the #mergeTo() to perform
6042 * necessary consistency checks and place involved media to appropriate
6043 * states. If #mergeTo() is not called or fails, the state modifications
6044 * performed by this method must be undone by #i_cancelMergeTo().
6045 *
6046 * See #mergeTo() for more information about merging.
6047 *
6048 * @param pTarget Target medium.
6049 * @param aMachineId Allowed machine attachment. NULL means do not check.
6050 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
6051 * do not check.
6052 * @param fLockMedia Flag whether to lock the medium lock list or not.
6053 * If set to false and the medium lock list locking fails
6054 * later you must call #i_cancelMergeTo().
6055 * @param fMergeForward Resulting merge direction (out).
6056 * @param pParentForTarget New parent for target medium after merge (out).
6057 * @param aChildrenToReparent Medium lock list containing all children of the
6058 * source which will have to be reparented to the target
6059 * after merge (out).
6060 * @param aMediumLockList Medium locking information (out).
6061 *
6062 * @note Locks medium tree for reading. Locks this object, aTarget and all
6063 * intermediate media for writing.
6064 */
6065HRESULT Medium::i_prepareMergeTo(const ComObjPtr<Medium> &pTarget,
6066 const Guid *aMachineId,
6067 const Guid *aSnapshotId,
6068 bool fLockMedia,
6069 bool &fMergeForward,
6070 ComObjPtr<Medium> &pParentForTarget,
6071 MediumLockList * &aChildrenToReparent,
6072 MediumLockList * &aMediumLockList)
6073{
6074 AssertReturn(pTarget != NULL, E_FAIL);
6075 AssertReturn(pTarget != this, E_FAIL);
6076
6077 HRESULT rc = S_OK;
6078 fMergeForward = false;
6079 pParentForTarget.setNull();
6080 Assert(aChildrenToReparent == NULL);
6081 aChildrenToReparent = NULL;
6082 Assert(aMediumLockList == NULL);
6083 aMediumLockList = NULL;
6084
6085 try
6086 {
6087 // locking: we need the tree lock first because we access parent pointers
6088 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6089
6090 AutoCaller autoCaller(this);
6091 AssertComRCThrowRC(autoCaller.rc());
6092
6093 AutoCaller targetCaller(pTarget);
6094 AssertComRCThrowRC(targetCaller.rc());
6095
6096 /* more sanity checking and figuring out the merge direction */
6097 ComObjPtr<Medium> pMedium = i_getParent();
6098 while (!pMedium.isNull() && pMedium != pTarget)
6099 pMedium = pMedium->i_getParent();
6100 if (pMedium == pTarget)
6101 fMergeForward = false;
6102 else
6103 {
6104 pMedium = pTarget->i_getParent();
6105 while (!pMedium.isNull() && pMedium != this)
6106 pMedium = pMedium->i_getParent();
6107 if (pMedium == this)
6108 fMergeForward = true;
6109 else
6110 {
6111 Utf8Str tgtLoc;
6112 {
6113 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6114 tgtLoc = pTarget->i_getLocationFull();
6115 }
6116
6117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6118 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6119 tr("Media '%s' and '%s' are unrelated"),
6120 m->strLocationFull.c_str(), tgtLoc.c_str());
6121 }
6122 }
6123
6124 /* Build the lock list. */
6125 aMediumLockList = new MediumLockList();
6126 targetCaller.release();
6127 autoCaller.release();
6128 treeLock.release();
6129 if (fMergeForward)
6130 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
6131 pTarget /* pToLockWrite */,
6132 false /* fMediumLockWriteAll */,
6133 NULL,
6134 *aMediumLockList);
6135 else
6136 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6137 pTarget /* pToLockWrite */,
6138 false /* fMediumLockWriteAll */,
6139 NULL,
6140 *aMediumLockList);
6141 treeLock.acquire();
6142 autoCaller.add();
6143 AssertComRCThrowRC(autoCaller.rc());
6144 targetCaller.add();
6145 AssertComRCThrowRC(targetCaller.rc());
6146 if (FAILED(rc))
6147 throw rc;
6148
6149 /* Sanity checking, must be after lock list creation as it depends on
6150 * valid medium states. The medium objects must be accessible. Only
6151 * do this if immediate locking is requested, otherwise it fails when
6152 * we construct a medium lock list for an already running VM. Snapshot
6153 * deletion uses this to simplify its life. */
6154 if (fLockMedia)
6155 {
6156 {
6157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6158 if (m->state != MediumState_Created)
6159 throw i_setStateError();
6160 }
6161 {
6162 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6163 if (pTarget->m->state != MediumState_Created)
6164 throw pTarget->i_setStateError();
6165 }
6166 }
6167
6168 /* check medium attachment and other sanity conditions */
6169 if (fMergeForward)
6170 {
6171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6172 if (i_getChildren().size() > 1)
6173 {
6174 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6175 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6176 m->strLocationFull.c_str(), i_getChildren().size());
6177 }
6178 /* One backreference is only allowed if the machine ID is not empty
6179 * and it matches the machine the medium is attached to (including
6180 * the snapshot ID if not empty). */
6181 if ( m->backRefs.size() != 0
6182 && ( !aMachineId
6183 || m->backRefs.size() != 1
6184 || aMachineId->isZero()
6185 || *i_getFirstMachineBackrefId() != *aMachineId
6186 || ( (!aSnapshotId || !aSnapshotId->isZero())
6187 && *i_getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
6188 throw setError(VBOX_E_OBJECT_IN_USE,
6189 tr("Medium '%s' is attached to %d virtual machines", "", m->backRefs.size()),
6190 m->strLocationFull.c_str(), m->backRefs.size());
6191 if (m->type == MediumType_Immutable)
6192 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6193 tr("Medium '%s' is immutable"),
6194 m->strLocationFull.c_str());
6195 if (m->type == MediumType_MultiAttach)
6196 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6197 tr("Medium '%s' is multi-attach"),
6198 m->strLocationFull.c_str());
6199 }
6200 else
6201 {
6202 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6203 if (pTarget->i_getChildren().size() > 1)
6204 {
6205 throw setError(VBOX_E_OBJECT_IN_USE,
6206 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6207 pTarget->m->strLocationFull.c_str(),
6208 pTarget->i_getChildren().size());
6209 }
6210 if (pTarget->m->type == MediumType_Immutable)
6211 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6212 tr("Medium '%s' is immutable"),
6213 pTarget->m->strLocationFull.c_str());
6214 if (pTarget->m->type == MediumType_MultiAttach)
6215 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6216 tr("Medium '%s' is multi-attach"),
6217 pTarget->m->strLocationFull.c_str());
6218 }
6219 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
6220 ComObjPtr<Medium> pLastIntermediate = pLast->i_getParent();
6221 for (pLast = pLastIntermediate;
6222 !pLast.isNull() && pLast != pTarget && pLast != this;
6223 pLast = pLast->i_getParent())
6224 {
6225 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
6226 if (pLast->i_getChildren().size() > 1)
6227 {
6228 throw setError(VBOX_E_OBJECT_IN_USE,
6229 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6230 pLast->m->strLocationFull.c_str(),
6231 pLast->i_getChildren().size());
6232 }
6233 if (pLast->m->backRefs.size() != 0)
6234 throw setError(VBOX_E_OBJECT_IN_USE,
6235 tr("Medium '%s' is attached to %d virtual machines", "", pLast->m->backRefs.size()),
6236 pLast->m->strLocationFull.c_str(),
6237 pLast->m->backRefs.size());
6238
6239 }
6240
6241 /* Update medium states appropriately */
6242 {
6243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6244
6245 if (m->state == MediumState_Created)
6246 {
6247 rc = i_markForDeletion();
6248 if (FAILED(rc))
6249 throw rc;
6250 }
6251 else
6252 {
6253 if (fLockMedia)
6254 throw i_setStateError();
6255 else if ( m->state == MediumState_LockedWrite
6256 || m->state == MediumState_LockedRead)
6257 {
6258 /* Either mark it for deletion in locked state or allow
6259 * others to have done so. */
6260 if (m->preLockState == MediumState_Created)
6261 i_markLockedForDeletion();
6262 else if (m->preLockState != MediumState_Deleting)
6263 throw i_setStateError();
6264 }
6265 else
6266 throw i_setStateError();
6267 }
6268 }
6269
6270 if (fMergeForward)
6271 {
6272 /* we will need parent to reparent target */
6273 pParentForTarget = i_getParent();
6274 }
6275 else
6276 {
6277 /* we will need to reparent children of the source */
6278 aChildrenToReparent = new MediumLockList();
6279 for (MediaList::const_iterator it = i_getChildren().begin();
6280 it != i_getChildren().end();
6281 ++it)
6282 {
6283 pMedium = *it;
6284 aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
6285 }
6286 if (fLockMedia && aChildrenToReparent)
6287 {
6288 targetCaller.release();
6289 autoCaller.release();
6290 treeLock.release();
6291 rc = aChildrenToReparent->Lock();
6292 treeLock.acquire();
6293 autoCaller.add();
6294 AssertComRCThrowRC(autoCaller.rc());
6295 targetCaller.add();
6296 AssertComRCThrowRC(targetCaller.rc());
6297 if (FAILED(rc))
6298 throw rc;
6299 }
6300 }
6301 for (pLast = pLastIntermediate;
6302 !pLast.isNull() && pLast != pTarget && pLast != this;
6303 pLast = pLast->i_getParent())
6304 {
6305 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
6306 if (pLast->m->state == MediumState_Created)
6307 {
6308 rc = pLast->i_markForDeletion();
6309 if (FAILED(rc))
6310 throw rc;
6311 }
6312 else
6313 throw pLast->i_setStateError();
6314 }
6315
6316 /* Tweak the lock list in the backward merge case, as the target
6317 * isn't marked to be locked for writing yet. */
6318 if (!fMergeForward)
6319 {
6320 MediumLockList::Base::iterator lockListBegin =
6321 aMediumLockList->GetBegin();
6322 MediumLockList::Base::iterator lockListEnd =
6323 aMediumLockList->GetEnd();
6324 ++lockListEnd;
6325 for (MediumLockList::Base::iterator it = lockListBegin;
6326 it != lockListEnd;
6327 ++it)
6328 {
6329 MediumLock &mediumLock = *it;
6330 if (mediumLock.GetMedium() == pTarget)
6331 {
6332 HRESULT rc2 = mediumLock.UpdateLock(true);
6333 AssertComRC(rc2);
6334 break;
6335 }
6336 }
6337 }
6338
6339 if (fLockMedia)
6340 {
6341 targetCaller.release();
6342 autoCaller.release();
6343 treeLock.release();
6344 rc = aMediumLockList->Lock();
6345 treeLock.acquire();
6346 autoCaller.add();
6347 AssertComRCThrowRC(autoCaller.rc());
6348 targetCaller.add();
6349 AssertComRCThrowRC(targetCaller.rc());
6350 if (FAILED(rc))
6351 {
6352 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6353 throw setError(rc,
6354 tr("Failed to lock media when merging to '%s'"),
6355 pTarget->i_getLocationFull().c_str());
6356 }
6357 }
6358 }
6359 catch (HRESULT aRC) { rc = aRC; }
6360
6361 if (FAILED(rc))
6362 {
6363 if (aMediumLockList)
6364 {
6365 delete aMediumLockList;
6366 aMediumLockList = NULL;
6367 }
6368 if (aChildrenToReparent)
6369 {
6370 delete aChildrenToReparent;
6371 aChildrenToReparent = NULL;
6372 }
6373 }
6374
6375 return rc;
6376}
6377
6378/**
6379 * Merges this medium to the specified medium which must be either its
6380 * direct ancestor or descendant.
6381 *
6382 * Given this medium is SOURCE and the specified medium is TARGET, we will
6383 * get two variants of the merge operation:
6384 *
6385 * forward merge
6386 * ------------------------->
6387 * [Extra] <- SOURCE <- Intermediate <- TARGET
6388 * Any Del Del LockWr
6389 *
6390 *
6391 * backward merge
6392 * <-------------------------
6393 * TARGET <- Intermediate <- SOURCE <- [Extra]
6394 * LockWr Del Del LockWr
6395 *
6396 * Each diagram shows the involved media on the media chain where
6397 * SOURCE and TARGET belong. Under each medium there is a state value which
6398 * the medium must have at a time of the mergeTo() call.
6399 *
6400 * The media in the square braces may be absent (e.g. when the forward
6401 * operation takes place and SOURCE is the base medium, or when the backward
6402 * merge operation takes place and TARGET is the last child in the chain) but if
6403 * they present they are involved too as shown.
6404 *
6405 * Neither the source medium nor intermediate media may be attached to
6406 * any VM directly or in the snapshot, otherwise this method will assert.
6407 *
6408 * The #i_prepareMergeTo() method must be called prior to this method to place
6409 * all involved to necessary states and perform other consistency checks.
6410 *
6411 * If @a aWait is @c true then this method will perform the operation on the
6412 * calling thread and will not return to the caller until the operation is
6413 * completed. When this method succeeds, all intermediate medium objects in
6414 * the chain will be uninitialized, the state of the target medium (and all
6415 * involved extra media) will be restored. @a aMediumLockList will not be
6416 * deleted, whether the operation is successful or not. The caller has to do
6417 * this if appropriate. Note that this (source) medium is not uninitialized
6418 * because of possible AutoCaller instances held by the caller of this method
6419 * on the current thread. It's therefore the responsibility of the caller to
6420 * call Medium::uninit() after releasing all callers.
6421 *
6422 * If @a aWait is @c false then this method will create a thread to perform the
6423 * operation asynchronously and will return immediately. If the operation
6424 * succeeds, the thread will uninitialize the source medium object and all
6425 * intermediate medium objects in the chain, reset the state of the target
6426 * medium (and all involved extra media) and delete @a aMediumLockList.
6427 * If the operation fails, the thread will only reset the states of all
6428 * involved media and delete @a aMediumLockList.
6429 *
6430 * When this method fails (regardless of the @a aWait mode), it is a caller's
6431 * responsibility to undo state changes and delete @a aMediumLockList using
6432 * #i_cancelMergeTo().
6433 *
6434 * If @a aProgress is not NULL but the object it points to is @c null then a new
6435 * progress object will be created and assigned to @a *aProgress on success,
6436 * otherwise the existing progress object is used. If Progress is NULL, then no
6437 * progress object is created/used at all. Note that @a aProgress cannot be
6438 * NULL when @a aWait is @c false (this method will assert in this case).
6439 *
6440 * @param pTarget Target medium.
6441 * @param fMergeForward Merge direction.
6442 * @param pParentForTarget New parent for target medium after merge.
6443 * @param aChildrenToReparent List of children of the source which will have
6444 * to be reparented to the target after merge.
6445 * @param aMediumLockList Medium locking information.
6446 * @param aProgress Where to find/store a Progress object to track operation
6447 * completion.
6448 * @param aWait @c true if this method should block instead of creating
6449 * an asynchronous thread.
6450 * @param aNotify Notify about mediums which metadatа are changed
6451 * during execution of the function.
6452 *
6453 * @note Locks the tree lock for writing. Locks the media from the chain
6454 * for writing.
6455 */
6456HRESULT Medium::i_mergeTo(const ComObjPtr<Medium> &pTarget,
6457 bool fMergeForward,
6458 const ComObjPtr<Medium> &pParentForTarget,
6459 MediumLockList *aChildrenToReparent,
6460 MediumLockList *aMediumLockList,
6461 ComObjPtr<Progress> *aProgress,
6462 bool aWait, bool aNotify)
6463{
6464 AssertReturn(pTarget != NULL, E_FAIL);
6465 AssertReturn(pTarget != this, E_FAIL);
6466 AssertReturn(aMediumLockList != NULL, E_FAIL);
6467 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6468
6469 AutoCaller autoCaller(this);
6470 AssertComRCReturnRC(autoCaller.rc());
6471
6472 AutoCaller targetCaller(pTarget);
6473 AssertComRCReturnRC(targetCaller.rc());
6474
6475 HRESULT rc = S_OK;
6476 ComObjPtr<Progress> pProgress;
6477 Medium::Task *pTask = NULL;
6478
6479 try
6480 {
6481 if (aProgress != NULL)
6482 {
6483 /* use the existing progress object... */
6484 pProgress = *aProgress;
6485
6486 /* ...but create a new one if it is null */
6487 if (pProgress.isNull())
6488 {
6489 Utf8Str tgtName;
6490 {
6491 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6492 tgtName = pTarget->i_getName();
6493 }
6494
6495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6496
6497 pProgress.createObject();
6498 rc = pProgress->init(m->pVirtualBox,
6499 static_cast<IMedium*>(this),
6500 BstrFmt(tr("Merging medium '%s' to '%s'"),
6501 i_getName().c_str(),
6502 tgtName.c_str()).raw(),
6503 TRUE, /* aCancelable */
6504 2, /* Number of opearations */
6505 BstrFmt(tr("Resizing medium '%s' before merge"),
6506 tgtName.c_str()).raw()
6507 );
6508 if (FAILED(rc))
6509 throw rc;
6510 }
6511 }
6512
6513 /* setup task object to carry out the operation sync/async */
6514 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
6515 pParentForTarget, aChildrenToReparent,
6516 pProgress, aMediumLockList,
6517 aWait /* fKeepMediumLockList */,
6518 aNotify);
6519 rc = pTask->rc();
6520 AssertComRC(rc);
6521 if (FAILED(rc))
6522 throw rc;
6523 }
6524 catch (HRESULT aRC) { rc = aRC; }
6525
6526 if (SUCCEEDED(rc))
6527 {
6528 if (aWait)
6529 {
6530 rc = pTask->runNow();
6531 delete pTask;
6532 }
6533 else
6534 rc = pTask->createThread();
6535 pTask = NULL;
6536 if (SUCCEEDED(rc) && aProgress != NULL)
6537 *aProgress = pProgress;
6538 }
6539 else if (pTask != NULL)
6540 delete pTask;
6541
6542 return rc;
6543}
6544
6545/**
6546 * Undoes what #i_prepareMergeTo() did. Must be called if #mergeTo() is not
6547 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
6548 * the medium objects in @a aChildrenToReparent.
6549 *
6550 * @param aChildrenToReparent List of children of the source which will have
6551 * to be reparented to the target after merge.
6552 * @param aMediumLockList Medium locking information.
6553 *
6554 * @note Locks the tree lock for writing. Locks the media from the chain
6555 * for writing.
6556 */
6557void Medium::i_cancelMergeTo(MediumLockList *aChildrenToReparent,
6558 MediumLockList *aMediumLockList)
6559{
6560 AutoCaller autoCaller(this);
6561 AssertComRCReturnVoid(autoCaller.rc());
6562
6563 AssertReturnVoid(aMediumLockList != NULL);
6564
6565 /* Revert media marked for deletion to previous state. */
6566 HRESULT rc;
6567 MediumLockList::Base::const_iterator mediumListBegin =
6568 aMediumLockList->GetBegin();
6569 MediumLockList::Base::const_iterator mediumListEnd =
6570 aMediumLockList->GetEnd();
6571 for (MediumLockList::Base::const_iterator it = mediumListBegin;
6572 it != mediumListEnd;
6573 ++it)
6574 {
6575 const MediumLock &mediumLock = *it;
6576 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6577 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6578
6579 if (pMedium->m->state == MediumState_Deleting)
6580 {
6581 rc = pMedium->i_unmarkForDeletion();
6582 AssertComRC(rc);
6583 }
6584 else if ( ( pMedium->m->state == MediumState_LockedWrite
6585 || pMedium->m->state == MediumState_LockedRead)
6586 && pMedium->m->preLockState == MediumState_Deleting)
6587 {
6588 rc = pMedium->i_unmarkLockedForDeletion();
6589 AssertComRC(rc);
6590 }
6591 }
6592
6593 /* the destructor will do the work */
6594 delete aMediumLockList;
6595
6596 /* unlock the children which had to be reparented, the destructor will do
6597 * the work */
6598 if (aChildrenToReparent)
6599 delete aChildrenToReparent;
6600}
6601
6602/**
6603 * Resizes the media.
6604 *
6605 * If @a aWait is @c true then this method will perform the operation on the
6606 * calling thread and will not return to the caller until the operation is
6607 * completed. When this method succeeds, the state of the target medium (and all
6608 * involved extra media) will be restored. @a aMediumLockList will not be
6609 * deleted, whether the operation is successful or not. The caller has to do
6610 * this if appropriate.
6611 *
6612 * If @a aWait is @c false then this method will create a thread to perform the
6613 * operation asynchronously and will return immediately. The thread will reset
6614 * the state of the target medium (and all involved extra media) and delete
6615 * @a aMediumLockList.
6616 *
6617 * When this method fails (regardless of the @a aWait mode), it is a caller's
6618 * responsibility to undo state changes and delete @a aMediumLockList.
6619 *
6620 * If @a aProgress is not NULL but the object it points to is @c null then a new
6621 * progress object will be created and assigned to @a *aProgress on success,
6622 * otherwise the existing progress object is used. If Progress is NULL, then no
6623 * progress object is created/used at all. Note that @a aProgress cannot be
6624 * NULL when @a aWait is @c false (this method will assert in this case).
6625 *
6626 * @param aLogicalSize New nominal capacity of the medium in bytes.
6627 * @param aMediumLockList Medium locking information.
6628 * @param aProgress Where to find/store a Progress object to track operation
6629 * completion.
6630 * @param aWait @c true if this method should block instead of creating
6631 * an asynchronous thread.
6632 * @param aNotify Notify about mediums which metadatа are changed
6633 * during execution of the function.
6634 *
6635 * @note Locks the media from the chain for writing.
6636 */
6637
6638HRESULT Medium::i_resize(uint64_t aLogicalSize,
6639 MediumLockList *aMediumLockList,
6640 ComObjPtr<Progress> *aProgress,
6641 bool aWait,
6642 bool aNotify)
6643{
6644 AssertReturn(aMediumLockList != NULL, E_FAIL);
6645 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6646
6647 AutoCaller autoCaller(this);
6648 AssertComRCReturnRC(autoCaller.rc());
6649
6650 HRESULT rc = S_OK;
6651 ComObjPtr<Progress> pProgress;
6652 Medium::Task *pTask = NULL;
6653
6654 try
6655 {
6656 if (aProgress != NULL)
6657 {
6658 /* use the existing progress object... */
6659 pProgress = *aProgress;
6660
6661 /* ...but create a new one if it is null */
6662 if (pProgress.isNull())
6663 {
6664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6665
6666 pProgress.createObject();
6667 rc = pProgress->init(m->pVirtualBox,
6668 static_cast <IMedium *>(this),
6669 BstrFmt(tr("Resizing medium '%s'"), m->strLocationFull.c_str()).raw(),
6670 TRUE /* aCancelable */);
6671 if (FAILED(rc))
6672 throw rc;
6673 }
6674 }
6675
6676 /* setup task object to carry out the operation asynchronously */
6677 pTask = new Medium::ResizeTask(this,
6678 aLogicalSize,
6679 pProgress,
6680 aMediumLockList,
6681 aWait /* fKeepMediumLockList */,
6682 aNotify);
6683 rc = pTask->rc();
6684 AssertComRC(rc);
6685 if (FAILED(rc))
6686 throw rc;
6687 }
6688 catch (HRESULT aRC) { rc = aRC; }
6689
6690 if (SUCCEEDED(rc))
6691 {
6692 if (aWait)
6693 {
6694 rc = pTask->runNow();
6695 delete pTask;
6696 }
6697 else
6698 rc = pTask->createThread();
6699 pTask = NULL;
6700 if (SUCCEEDED(rc) && aProgress != NULL)
6701 *aProgress = pProgress;
6702 }
6703 else if (pTask != NULL)
6704 delete pTask;
6705
6706 return rc;
6707}
6708
6709/**
6710 * Fix the parent UUID of all children to point to this medium as their
6711 * parent.
6712 */
6713HRESULT Medium::i_fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
6714{
6715 /** @todo r=klaus The code below needs to be double checked with regard
6716 * to lock order violations, it probably causes lock order issues related
6717 * to the AutoCaller usage. Likewise the code using this method seems
6718 * problematic. */
6719 Assert(!isWriteLockOnCurrentThread());
6720 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6721 MediumLockList mediumLockList;
6722 HRESULT rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6723 NULL /* pToLockWrite */,
6724 false /* fMediumLockWriteAll */,
6725 this,
6726 mediumLockList);
6727 AssertComRCReturnRC(rc);
6728
6729 try
6730 {
6731 PVDISK hdd;
6732 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6733 ComAssertRCThrow(vrc, E_FAIL);
6734
6735 try
6736 {
6737 MediumLockList::Base::iterator lockListBegin =
6738 mediumLockList.GetBegin();
6739 MediumLockList::Base::iterator lockListEnd =
6740 mediumLockList.GetEnd();
6741 for (MediumLockList::Base::iterator it = lockListBegin;
6742 it != lockListEnd;
6743 ++it)
6744 {
6745 MediumLock &mediumLock = *it;
6746 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6747 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6748
6749 // open the medium
6750 vrc = VDOpen(hdd,
6751 pMedium->m->strFormat.c_str(),
6752 pMedium->m->strLocationFull.c_str(),
6753 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
6754 pMedium->m->vdImageIfaces);
6755 if (RT_FAILURE(vrc))
6756 throw vrc;
6757 }
6758
6759 MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
6760 MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
6761 for (MediumLockList::Base::iterator it = childrenBegin;
6762 it != childrenEnd;
6763 ++it)
6764 {
6765 Medium *pMedium = it->GetMedium();
6766 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
6767 vrc = VDOpen(hdd,
6768 pMedium->m->strFormat.c_str(),
6769 pMedium->m->strLocationFull.c_str(),
6770 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
6771 pMedium->m->vdImageIfaces);
6772 if (RT_FAILURE(vrc))
6773 throw vrc;
6774
6775 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
6776 if (RT_FAILURE(vrc))
6777 throw vrc;
6778
6779 vrc = VDClose(hdd, false /* fDelete */);
6780 if (RT_FAILURE(vrc))
6781 throw vrc;
6782 }
6783 }
6784 catch (HRESULT aRC) { rc = aRC; }
6785 catch (int aVRC)
6786 {
6787 rc = setErrorBoth(E_FAIL, aVRC,
6788 tr("Could not update medium UUID references to parent '%s' (%s)"),
6789 m->strLocationFull.c_str(),
6790 i_vdError(aVRC).c_str());
6791 }
6792
6793 VDDestroy(hdd);
6794 }
6795 catch (HRESULT aRC) { rc = aRC; }
6796
6797 return rc;
6798}
6799
6800/**
6801 *
6802 * @note Similar code exists in i_taskExportHandler.
6803 */
6804HRESULT Medium::i_addRawToFss(const char *aFilename, SecretKeyStore *pKeyStore, RTVFSFSSTREAM hVfsFssDst,
6805 const ComObjPtr<Progress> &aProgress, bool fSparse)
6806{
6807 AutoCaller autoCaller(this);
6808 HRESULT hrc = autoCaller.rc();
6809 if (SUCCEEDED(hrc))
6810 {
6811 /*
6812 * Get a readonly hdd for this medium.
6813 */
6814 MediumCryptoFilterSettings CryptoSettingsRead;
6815 MediumLockList SourceMediumLockList;
6816 PVDISK pHdd;
6817 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pHdd, &SourceMediumLockList, &CryptoSettingsRead);
6818 if (SUCCEEDED(hrc))
6819 {
6820 /*
6821 * Create a VFS file interface to the HDD and attach a progress wrapper
6822 * that monitors the progress reading of the raw image. The image will
6823 * be read twice if hVfsFssDst does sparse processing.
6824 */
6825 RTVFSFILE hVfsFileDisk = NIL_RTVFSFILE;
6826 int vrc = VDCreateVfsFileFromDisk(pHdd, 0 /*fFlags*/, &hVfsFileDisk);
6827 if (RT_SUCCESS(vrc))
6828 {
6829 RTVFSFILE hVfsFileProgress = NIL_RTVFSFILE;
6830 vrc = RTVfsCreateProgressForFile(hVfsFileDisk, aProgress->i_iprtProgressCallback, &*aProgress,
6831 RTVFSPROGRESS_F_CANCELABLE | RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ,
6832 VDGetSize(pHdd, VD_LAST_IMAGE) * (fSparse ? 2 : 1) /*cbExpectedRead*/,
6833 0 /*cbExpectedWritten*/, &hVfsFileProgress);
6834 RTVfsFileRelease(hVfsFileDisk);
6835 if (RT_SUCCESS(vrc))
6836 {
6837 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFileProgress);
6838 RTVfsFileRelease(hVfsFileProgress);
6839
6840 vrc = RTVfsFsStrmAdd(hVfsFssDst, aFilename, hVfsObj, 0 /*fFlags*/);
6841 RTVfsObjRelease(hVfsObj);
6842 if (RT_FAILURE(vrc))
6843 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to add '%s' to output (%Rrc)"), aFilename, vrc);
6844 }
6845 else
6846 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
6847 tr("RTVfsCreateProgressForFile failed when processing '%s' (%Rrc)"), aFilename, vrc);
6848 }
6849 else
6850 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("VDCreateVfsFileFromDisk failed for '%s' (%Rrc)"), aFilename, vrc);
6851 VDDestroy(pHdd);
6852 }
6853 }
6854 return hrc;
6855}
6856
6857/**
6858 * Used by IAppliance to export disk images.
6859 *
6860 * @param aFilename Filename to create (UTF8).
6861 * @param aFormat Medium format for creating @a aFilename.
6862 * @param aVariant Which exact image format variant to use for the
6863 * destination image.
6864 * @param pKeyStore The optional key store for decrypting the data for
6865 * encrypted media during the export.
6866 * @param hVfsIosDst The destination I/O stream object.
6867 * @param aProgress Progress object to use.
6868 * @return
6869 *
6870 * @note The source format is defined by the Medium instance.
6871 */
6872HRESULT Medium::i_exportFile(const char *aFilename,
6873 const ComObjPtr<MediumFormat> &aFormat,
6874 MediumVariant_T aVariant,
6875 SecretKeyStore *pKeyStore,
6876 RTVFSIOSTREAM hVfsIosDst,
6877 const ComObjPtr<Progress> &aProgress)
6878{
6879 AssertPtrReturn(aFilename, E_INVALIDARG);
6880 AssertReturn(aFormat.isNotNull(), E_INVALIDARG);
6881 AssertReturn(aProgress.isNotNull(), E_INVALIDARG);
6882
6883 AutoCaller autoCaller(this);
6884 HRESULT hrc = autoCaller.rc();
6885 if (SUCCEEDED(hrc))
6886 {
6887 /*
6888 * Setup VD interfaces.
6889 */
6890 PVDINTERFACE pVDImageIfaces = m->vdImageIfaces;
6891 PVDINTERFACEIO pVfsIoIf;
6892 int vrc = VDIfCreateFromVfsStream(hVfsIosDst, RTFILE_O_WRITE, &pVfsIoIf);
6893 if (RT_SUCCESS(vrc))
6894 {
6895 vrc = VDInterfaceAdd(&pVfsIoIf->Core, "Medium::ExportTaskVfsIos", VDINTERFACETYPE_IO,
6896 pVfsIoIf, sizeof(VDINTERFACEIO), &pVDImageIfaces);
6897 if (RT_SUCCESS(vrc))
6898 {
6899 /*
6900 * Get a readonly hdd for this medium (source).
6901 */
6902 MediumCryptoFilterSettings CryptoSettingsRead;
6903 MediumLockList SourceMediumLockList;
6904 PVDISK pSrcHdd;
6905 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pSrcHdd, &SourceMediumLockList, &CryptoSettingsRead);
6906 if (SUCCEEDED(hrc))
6907 {
6908 /*
6909 * Create the target medium.
6910 */
6911 Utf8Str strDstFormat(aFormat->i_getId());
6912
6913 /* ensure the target directory exists */
6914 uint64_t fDstCapabilities = aFormat->i_getCapabilities();
6915 if (fDstCapabilities & MediumFormatCapabilities_File)
6916 {
6917 Utf8Str strDstLocation(aFilename);
6918 hrc = VirtualBox::i_ensureFilePathExists(strDstLocation.c_str(),
6919 !(aVariant & MediumVariant_NoCreateDir) /* fCreate */);
6920 }
6921 if (SUCCEEDED(hrc))
6922 {
6923 PVDISK pDstHdd;
6924 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDstHdd);
6925 if (RT_SUCCESS(vrc))
6926 {
6927 /*
6928 * Create an interface for getting progress callbacks.
6929 */
6930 VDINTERFACEPROGRESS ProgressIf = VDINTERFACEPROGRESS_INITALIZER(aProgress->i_vdProgressCallback);
6931 PVDINTERFACE pProgress = NULL;
6932 vrc = VDInterfaceAdd(&ProgressIf.Core, "export-progress", VDINTERFACETYPE_PROGRESS,
6933 &*aProgress, sizeof(ProgressIf), &pProgress);
6934 AssertRC(vrc);
6935
6936 /*
6937 * Do the exporting.
6938 */
6939 vrc = VDCopy(pSrcHdd,
6940 VD_LAST_IMAGE,
6941 pDstHdd,
6942 strDstFormat.c_str(),
6943 aFilename,
6944 false /* fMoveByRename */,
6945 0 /* cbSize */,
6946 aVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
6947 NULL /* pDstUuid */,
6948 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
6949 pProgress,
6950 pVDImageIfaces,
6951 NULL);
6952 if (RT_SUCCESS(vrc))
6953 hrc = S_OK;
6954 else
6955 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Could not create the exported medium '%s'%s"),
6956 aFilename, i_vdError(vrc).c_str());
6957 VDDestroy(pDstHdd);
6958 }
6959 else
6960 hrc = setErrorVrc(vrc);
6961 }
6962 }
6963 VDDestroy(pSrcHdd);
6964 }
6965 else
6966 hrc = setErrorVrc(vrc, "VDInterfaceAdd -> %Rrc", vrc);
6967 VDIfDestroyFromVfsStream(pVfsIoIf);
6968 }
6969 else
6970 hrc = setErrorVrc(vrc, "VDIfCreateFromVfsStream -> %Rrc", vrc);
6971 }
6972 return hrc;
6973}
6974
6975/**
6976 * Used by IAppliance to import disk images.
6977 *
6978 * @param aFilename Filename to read (UTF8).
6979 * @param aFormat Medium format for reading @a aFilename.
6980 * @param aVariant Which exact image format variant to use
6981 * for the destination image.
6982 * @param aVfsIosSrc Handle to the source I/O stream.
6983 * @param aParent Parent medium. May be NULL.
6984 * @param aProgress Progress object to use.
6985 * @param aNotify Notify about mediums which metadatа are changed
6986 * during execution of the function.
6987 * @return
6988 * @note The destination format is defined by the Medium instance.
6989 *
6990 * @todo The only consumer of this method (Appliance::i_importOneDiskImage) is
6991 * already on a worker thread, so perhaps consider bypassing the thread
6992 * here and run in the task synchronously? VBoxSVC has enough threads as
6993 * it is...
6994 */
6995HRESULT Medium::i_importFile(const char *aFilename,
6996 const ComObjPtr<MediumFormat> &aFormat,
6997 MediumVariant_T aVariant,
6998 RTVFSIOSTREAM aVfsIosSrc,
6999 const ComObjPtr<Medium> &aParent,
7000 const ComObjPtr<Progress> &aProgress,
7001 bool aNotify)
7002{
7003 /** @todo r=klaus The code below needs to be double checked with regard
7004 * to lock order violations, it probably causes lock order issues related
7005 * to the AutoCaller usage. */
7006 AssertPtrReturn(aFilename, E_INVALIDARG);
7007 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
7008 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
7009
7010 AutoCaller autoCaller(this);
7011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7012
7013 HRESULT rc = S_OK;
7014 Medium::Task *pTask = NULL;
7015
7016 try
7017 {
7018 // locking: we need the tree lock first because we access parent pointers
7019 // and we need to write-lock the media involved
7020 uint32_t cHandles = 2;
7021 LockHandle* pHandles[3] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
7022 this->lockHandle() };
7023 /* Only add parent to the lock if it is not null */
7024 if (!aParent.isNull())
7025 pHandles[cHandles++] = aParent->lockHandle();
7026 AutoWriteLock alock(cHandles,
7027 pHandles
7028 COMMA_LOCKVAL_SRC_POS);
7029
7030 if ( m->state != MediumState_NotCreated
7031 && m->state != MediumState_Created)
7032 throw i_setStateError();
7033
7034 /* Build the target lock list. */
7035 MediumLockList *pTargetMediumLockList(new MediumLockList());
7036 alock.release();
7037 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
7038 this /* pToLockWrite */,
7039 false /* fMediumLockWriteAll */,
7040 aParent,
7041 *pTargetMediumLockList);
7042 alock.acquire();
7043 if (FAILED(rc))
7044 {
7045 delete pTargetMediumLockList;
7046 throw rc;
7047 }
7048
7049 alock.release();
7050 rc = pTargetMediumLockList->Lock();
7051 alock.acquire();
7052 if (FAILED(rc))
7053 {
7054 delete pTargetMediumLockList;
7055 throw setError(rc,
7056 tr("Failed to lock target media '%s'"),
7057 i_getLocationFull().c_str());
7058 }
7059
7060 /* setup task object to carry out the operation asynchronously */
7061 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat, aVariant,
7062 aVfsIosSrc, aParent, pTargetMediumLockList, false, aNotify);
7063 rc = pTask->rc();
7064 AssertComRC(rc);
7065 if (FAILED(rc))
7066 throw rc;
7067
7068 if (m->state == MediumState_NotCreated)
7069 m->state = MediumState_Creating;
7070 }
7071 catch (HRESULT aRC) { rc = aRC; }
7072
7073 if (SUCCEEDED(rc))
7074 {
7075 rc = pTask->createThread();
7076 pTask = NULL;
7077 }
7078 else if (pTask != NULL)
7079 delete pTask;
7080
7081 return rc;
7082}
7083
7084/**
7085 * Internal version of the public CloneTo API which allows to enable certain
7086 * optimizations to improve speed during VM cloning.
7087 *
7088 * @param aTarget Target medium
7089 * @param aVariant Which exact image format variant to use
7090 * for the destination image.
7091 * @param aParent Parent medium. May be NULL.
7092 * @param aProgress Progress object to use.
7093 * @param idxSrcImageSame The last image in the source chain which has the
7094 * same content as the given image in the destination
7095 * chain. Use UINT32_MAX to disable this optimization.
7096 * @param idxDstImageSame The last image in the destination chain which has the
7097 * same content as the given image in the source chain.
7098 * Use UINT32_MAX to disable this optimization.
7099 * @param aNotify Notify about mediums which metadatа are changed
7100 * during execution of the function.
7101 * @return
7102 */
7103HRESULT Medium::i_cloneToEx(const ComObjPtr<Medium> &aTarget, MediumVariant_T aVariant,
7104 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
7105 uint32_t idxSrcImageSame, uint32_t idxDstImageSame, bool aNotify)
7106{
7107 /** @todo r=klaus The code below needs to be double checked with regard
7108 * to lock order violations, it probably causes lock order issues related
7109 * to the AutoCaller usage. */
7110 CheckComArgNotNull(aTarget);
7111 CheckComArgOutPointerValid(aProgress);
7112 ComAssertRet(aTarget != this, E_INVALIDARG);
7113
7114 AutoCaller autoCaller(this);
7115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7116
7117 HRESULT rc = S_OK;
7118 ComObjPtr<Progress> pProgress;
7119 Medium::Task *pTask = NULL;
7120
7121 try
7122 {
7123 // locking: we need the tree lock first because we access parent pointers
7124 // and we need to write-lock the media involved
7125 uint32_t cHandles = 3;
7126 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
7127 this->lockHandle(),
7128 aTarget->lockHandle() };
7129 /* Only add parent to the lock if it is not null */
7130 if (!aParent.isNull())
7131 pHandles[cHandles++] = aParent->lockHandle();
7132 AutoWriteLock alock(cHandles,
7133 pHandles
7134 COMMA_LOCKVAL_SRC_POS);
7135
7136 if ( aTarget->m->state != MediumState_NotCreated
7137 && aTarget->m->state != MediumState_Created)
7138 throw aTarget->i_setStateError();
7139
7140 /* Build the source lock list. */
7141 MediumLockList *pSourceMediumLockList(new MediumLockList());
7142 alock.release();
7143 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
7144 NULL /* pToLockWrite */,
7145 false /* fMediumLockWriteAll */,
7146 NULL,
7147 *pSourceMediumLockList);
7148 alock.acquire();
7149 if (FAILED(rc))
7150 {
7151 delete pSourceMediumLockList;
7152 throw rc;
7153 }
7154
7155 /* Build the target lock list (including the to-be parent chain). */
7156 MediumLockList *pTargetMediumLockList(new MediumLockList());
7157 alock.release();
7158 rc = aTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
7159 aTarget /* pToLockWrite */,
7160 false /* fMediumLockWriteAll */,
7161 aParent,
7162 *pTargetMediumLockList);
7163 alock.acquire();
7164 if (FAILED(rc))
7165 {
7166 delete pSourceMediumLockList;
7167 delete pTargetMediumLockList;
7168 throw rc;
7169 }
7170
7171 alock.release();
7172 rc = pSourceMediumLockList->Lock();
7173 alock.acquire();
7174 if (FAILED(rc))
7175 {
7176 delete pSourceMediumLockList;
7177 delete pTargetMediumLockList;
7178 throw setError(rc,
7179 tr("Failed to lock source media '%s'"),
7180 i_getLocationFull().c_str());
7181 }
7182 alock.release();
7183 rc = pTargetMediumLockList->Lock();
7184 alock.acquire();
7185 if (FAILED(rc))
7186 {
7187 delete pSourceMediumLockList;
7188 delete pTargetMediumLockList;
7189 throw setError(rc,
7190 tr("Failed to lock target media '%s'"),
7191 aTarget->i_getLocationFull().c_str());
7192 }
7193
7194 pProgress.createObject();
7195 rc = pProgress->init(m->pVirtualBox,
7196 static_cast <IMedium *>(this),
7197 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
7198 TRUE /* aCancelable */);
7199 if (FAILED(rc))
7200 {
7201 delete pSourceMediumLockList;
7202 delete pTargetMediumLockList;
7203 throw rc;
7204 }
7205
7206 /* setup task object to carry out the operation asynchronously */
7207 pTask = new Medium::CloneTask(this, pProgress, aTarget, aVariant,
7208 aParent, idxSrcImageSame,
7209 idxDstImageSame, pSourceMediumLockList,
7210 pTargetMediumLockList, false, false, aNotify);
7211 rc = pTask->rc();
7212 AssertComRC(rc);
7213 if (FAILED(rc))
7214 throw rc;
7215
7216 if (aTarget->m->state == MediumState_NotCreated)
7217 aTarget->m->state = MediumState_Creating;
7218 }
7219 catch (HRESULT aRC) { rc = aRC; }
7220
7221 if (SUCCEEDED(rc))
7222 {
7223 rc = pTask->createThread();
7224 pTask = NULL;
7225 if (SUCCEEDED(rc))
7226 pProgress.queryInterfaceTo(aProgress);
7227 }
7228 else if (pTask != NULL)
7229 delete pTask;
7230
7231 return rc;
7232}
7233
7234/**
7235 * Returns the key identifier for this medium if encryption is configured.
7236 *
7237 * @returns Key identifier or empty string if no encryption is configured.
7238 */
7239const Utf8Str& Medium::i_getKeyId()
7240{
7241 ComObjPtr<Medium> pBase = i_getBase();
7242
7243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7244
7245 settings::StringsMap::const_iterator it = pBase->m->mapProperties.find("CRYPT/KeyId");
7246 if (it == pBase->m->mapProperties.end())
7247 return Utf8Str::Empty;
7248
7249 return it->second;
7250}
7251
7252
7253/**
7254 * Returns all filter related properties.
7255 *
7256 * @returns COM status code.
7257 * @param aReturnNames Where to store the properties names on success.
7258 * @param aReturnValues Where to store the properties values on success.
7259 */
7260HRESULT Medium::i_getFilterProperties(std::vector<com::Utf8Str> &aReturnNames,
7261 std::vector<com::Utf8Str> &aReturnValues)
7262{
7263 std::vector<com::Utf8Str> aPropNames;
7264 std::vector<com::Utf8Str> aPropValues;
7265 HRESULT hrc = getProperties(Utf8Str(""), aPropNames, aPropValues);
7266
7267 if (SUCCEEDED(hrc))
7268 {
7269 unsigned cReturnSize = 0;
7270 aReturnNames.resize(0);
7271 aReturnValues.resize(0);
7272 for (unsigned idx = 0; idx < aPropNames.size(); idx++)
7273 {
7274 if (i_isPropertyForFilter(aPropNames[idx]))
7275 {
7276 aReturnNames.resize(cReturnSize + 1);
7277 aReturnValues.resize(cReturnSize + 1);
7278 aReturnNames[cReturnSize] = aPropNames[idx];
7279 aReturnValues[cReturnSize] = aPropValues[idx];
7280 cReturnSize++;
7281 }
7282 }
7283 }
7284
7285 return hrc;
7286}
7287
7288/**
7289 * Preparation to move this medium to a new location
7290 *
7291 * @param aLocation Location of the storage unit. If the location is a FS-path,
7292 * then it can be relative to the VirtualBox home directory.
7293 *
7294 * @note Must be called from under this object's write lock.
7295 */
7296HRESULT Medium::i_preparationForMoving(const Utf8Str &aLocation)
7297{
7298 HRESULT rc = E_FAIL;
7299
7300 if (i_getLocationFull() != aLocation)
7301 {
7302 m->strNewLocationFull = aLocation;
7303 m->fMoveThisMedium = true;
7304 rc = S_OK;
7305 }
7306
7307 return rc;
7308}
7309
7310/**
7311 * Checking whether current operation "moving" or not
7312 */
7313bool Medium::i_isMoveOperation(const ComObjPtr<Medium> &aTarget) const
7314{
7315 RT_NOREF(aTarget);
7316 return m->fMoveThisMedium;
7317}
7318
7319bool Medium::i_resetMoveOperationData()
7320{
7321 m->strNewLocationFull.setNull();
7322 m->fMoveThisMedium = false;
7323 return true;
7324}
7325
7326Utf8Str Medium::i_getNewLocationForMoving() const
7327{
7328 if (m->fMoveThisMedium == true)
7329 return m->strNewLocationFull;
7330 else
7331 return Utf8Str();
7332}
7333////////////////////////////////////////////////////////////////////////////////
7334//
7335// Private methods
7336//
7337////////////////////////////////////////////////////////////////////////////////
7338
7339/**
7340 * Queries information from the medium.
7341 *
7342 * As a result of this call, the accessibility state and data members such as
7343 * size and description will be updated with the current information.
7344 *
7345 * @note This method may block during a system I/O call that checks storage
7346 * accessibility.
7347 *
7348 * @note Caller MUST NOT hold the media tree or medium lock.
7349 *
7350 * @note Locks m->pParent for reading. Locks this object for writing.
7351 *
7352 * @param fSetImageId Whether to reset the UUID contained in the image file
7353 * to the UUID in the medium instance data (see SetIDs())
7354 * @param fSetParentId Whether to reset the parent UUID contained in the image
7355 * file to the parent UUID in the medium instance data (see
7356 * SetIDs())
7357 * @param autoCaller
7358 * @return
7359 */
7360HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
7361{
7362 Assert(!isWriteLockOnCurrentThread());
7363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7364
7365 if ( ( m->state != MediumState_Created
7366 && m->state != MediumState_Inaccessible
7367 && m->state != MediumState_LockedRead)
7368 || m->fClosing)
7369 return E_FAIL;
7370
7371 HRESULT rc = S_OK;
7372
7373 int vrc = VINF_SUCCESS;
7374
7375 /* check if a blocking i_queryInfo() call is in progress on some other thread,
7376 * and wait for it to finish if so instead of querying data ourselves */
7377 if (m->queryInfoRunning)
7378 {
7379 Assert( m->state == MediumState_LockedRead
7380 || m->state == MediumState_LockedWrite);
7381
7382 while (m->queryInfoRunning)
7383 {
7384 alock.release();
7385 /* must not hold the object lock now */
7386 Assert(!isWriteLockOnCurrentThread());
7387 {
7388 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
7389 }
7390 alock.acquire();
7391 }
7392
7393 return S_OK;
7394 }
7395
7396 bool success = false;
7397 Utf8Str lastAccessError;
7398
7399 /* are we dealing with a new medium constructed using the existing
7400 * location? */
7401 bool isImport = m->id.isZero();
7402 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
7403
7404 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
7405 * media because that would prevent necessary modifications
7406 * when opening media of some third-party formats for the first
7407 * time in VirtualBox (such as VMDK for which VDOpen() needs to
7408 * generate an UUID if it is missing) */
7409 if ( m->hddOpenMode == OpenReadOnly
7410 || m->type == MediumType_Readonly
7411 || (!isImport && !fSetImageId && !fSetParentId)
7412 )
7413 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7414
7415 /* Open shareable medium with the appropriate flags */
7416 if (m->type == MediumType_Shareable)
7417 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7418
7419 /* Lock the medium, which makes the behavior much more consistent, must be
7420 * done before dropping the object lock and setting queryInfoRunning. */
7421 ComPtr<IToken> pToken;
7422 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
7423 rc = LockRead(pToken.asOutParam());
7424 else
7425 rc = LockWrite(pToken.asOutParam());
7426 if (FAILED(rc)) return rc;
7427
7428 /* Copies of the input state fields which are not read-only,
7429 * as we're dropping the lock. CAUTION: be extremely careful what
7430 * you do with the contents of this medium object, as you will
7431 * create races if there are concurrent changes. */
7432 Utf8Str format(m->strFormat);
7433 Utf8Str location(m->strLocationFull);
7434 ComObjPtr<MediumFormat> formatObj = m->formatObj;
7435
7436 /* "Output" values which can't be set because the lock isn't held
7437 * at the time the values are determined. */
7438 Guid mediumId = m->id;
7439 uint64_t mediumSize = 0;
7440 uint64_t mediumLogicalSize = 0;
7441
7442 /* Flag whether a base image has a non-zero parent UUID and thus
7443 * need repairing after it was closed again. */
7444 bool fRepairImageZeroParentUuid = false;
7445
7446 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
7447
7448 /* must be set before leaving the object lock the first time */
7449 m->queryInfoRunning = true;
7450
7451 /* must leave object lock now, because a lock from a higher lock class
7452 * is needed and also a lengthy operation is coming */
7453 alock.release();
7454 autoCaller.release();
7455
7456 /* Note that taking the queryInfoSem after leaving the object lock above
7457 * can lead to short spinning of the loops waiting for i_queryInfo() to
7458 * complete. This is unavoidable since the other order causes a lock order
7459 * violation: here it would be requesting the object lock (at the beginning
7460 * of the method), then queryInfoSem, and below the other way round. */
7461 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
7462
7463 /* take the opportunity to have a media tree lock, released initially */
7464 Assert(!isWriteLockOnCurrentThread());
7465 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7466 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7467 treeLock.release();
7468
7469 /* re-take the caller, but not the object lock, to keep uninit away */
7470 autoCaller.add();
7471 if (FAILED(autoCaller.rc()))
7472 {
7473 m->queryInfoRunning = false;
7474 return autoCaller.rc();
7475 }
7476
7477 try
7478 {
7479 /* skip accessibility checks for host drives */
7480 if (m->hostDrive)
7481 {
7482 success = true;
7483 throw S_OK;
7484 }
7485
7486 PVDISK hdd;
7487 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7488 ComAssertRCThrow(vrc, E_FAIL);
7489
7490 try
7491 {
7492 /** @todo This kind of opening of media is assuming that diff
7493 * media can be opened as base media. Should be documented that
7494 * it must work for all medium format backends. */
7495 vrc = VDOpen(hdd,
7496 format.c_str(),
7497 location.c_str(),
7498 uOpenFlags | m->uOpenFlagsDef,
7499 m->vdImageIfaces);
7500 if (RT_FAILURE(vrc))
7501 {
7502 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
7503 location.c_str(), i_vdError(vrc).c_str());
7504 throw S_OK;
7505 }
7506
7507 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
7508 {
7509 /* Modify the UUIDs if necessary. The associated fields are
7510 * not modified by other code, so no need to copy. */
7511 if (fSetImageId)
7512 {
7513 alock.acquire();
7514 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
7515 alock.release();
7516 if (RT_FAILURE(vrc))
7517 {
7518 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
7519 location.c_str(), i_vdError(vrc).c_str());
7520 throw S_OK;
7521 }
7522 mediumId = m->uuidImage;
7523 }
7524 if (fSetParentId)
7525 {
7526 alock.acquire();
7527 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
7528 alock.release();
7529 if (RT_FAILURE(vrc))
7530 {
7531 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
7532 location.c_str(), i_vdError(vrc).c_str());
7533 throw S_OK;
7534 }
7535 }
7536 /* zap the information, these are no long-term members */
7537 alock.acquire();
7538 unconst(m->uuidImage).clear();
7539 unconst(m->uuidParentImage).clear();
7540 alock.release();
7541
7542 /* check the UUID */
7543 RTUUID uuid;
7544 vrc = VDGetUuid(hdd, 0, &uuid);
7545 ComAssertRCThrow(vrc, E_FAIL);
7546
7547 if (isImport)
7548 {
7549 mediumId = uuid;
7550
7551 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
7552 // only when importing a VDMK that has no UUID, create one in memory
7553 mediumId.create();
7554 }
7555 else
7556 {
7557 Assert(!mediumId.isZero());
7558
7559 if (mediumId != uuid)
7560 {
7561 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7562 lastAccessError = Utf8StrFmt(
7563 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
7564 &uuid,
7565 location.c_str(),
7566 mediumId.raw(),
7567 pVirtualBox->i_settingsFilePath().c_str());
7568 throw S_OK;
7569 }
7570 }
7571 }
7572 else
7573 {
7574 /* the backend does not support storing UUIDs within the
7575 * underlying storage so use what we store in XML */
7576
7577 if (fSetImageId)
7578 {
7579 /* set the UUID if an API client wants to change it */
7580 alock.acquire();
7581 mediumId = m->uuidImage;
7582 alock.release();
7583 }
7584 else if (isImport)
7585 {
7586 /* generate an UUID for an imported UUID-less medium */
7587 mediumId.create();
7588 }
7589 }
7590
7591 /* set the image uuid before the below parent uuid handling code
7592 * might place it somewhere in the media tree, so that the medium
7593 * UUID is valid at this point */
7594 alock.acquire();
7595 if (isImport || fSetImageId)
7596 unconst(m->id) = mediumId;
7597 alock.release();
7598
7599 /* get the medium variant */
7600 unsigned uImageFlags;
7601 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7602 ComAssertRCThrow(vrc, E_FAIL);
7603 alock.acquire();
7604 m->variant = (MediumVariant_T)uImageFlags;
7605 alock.release();
7606
7607 /* check/get the parent uuid and update corresponding state */
7608 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
7609 {
7610 RTUUID parentId;
7611 vrc = VDGetParentUuid(hdd, 0, &parentId);
7612 ComAssertRCThrow(vrc, E_FAIL);
7613
7614 /* streamOptimized VMDK images are only accepted as base
7615 * images, as this allows automatic repair of OVF appliances.
7616 * Since such images don't support random writes they will not
7617 * be created for diff images. Only an overly smart user might
7618 * manually create this case. Too bad for him. */
7619 if ( (isImport || fSetParentId)
7620 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
7621 {
7622 /* the parent must be known to us. Note that we freely
7623 * call locking methods of mVirtualBox and parent, as all
7624 * relevant locks must be already held. There may be no
7625 * concurrent access to the just opened medium on other
7626 * threads yet (and init() will fail if this method reports
7627 * MediumState_Inaccessible) */
7628
7629 ComObjPtr<Medium> pParent;
7630 if (RTUuidIsNull(&parentId))
7631 rc = VBOX_E_OBJECT_NOT_FOUND;
7632 else
7633 rc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
7634 if (FAILED(rc))
7635 {
7636 if (fSetImageId && !fSetParentId)
7637 {
7638 /* If the image UUID gets changed for an existing
7639 * image then the parent UUID can be stale. In such
7640 * cases clear the parent information. The parent
7641 * information may/will be re-set later if the
7642 * API client wants to adjust a complete medium
7643 * hierarchy one by one. */
7644 rc = S_OK;
7645 alock.acquire();
7646 RTUuidClear(&parentId);
7647 vrc = VDSetParentUuid(hdd, 0, &parentId);
7648 alock.release();
7649 ComAssertRCThrow(vrc, E_FAIL);
7650 }
7651 else
7652 {
7653 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
7654 &parentId, location.c_str(),
7655 pVirtualBox->i_settingsFilePath().c_str());
7656 throw S_OK;
7657 }
7658 }
7659
7660 /* must drop the caller before taking the tree lock */
7661 autoCaller.release();
7662 /* we set m->pParent & children() */
7663 treeLock.acquire();
7664 autoCaller.add();
7665 if (FAILED(autoCaller.rc()))
7666 throw autoCaller.rc();
7667
7668 if (m->pParent)
7669 i_deparent();
7670
7671 if (!pParent.isNull())
7672 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7673 {
7674 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
7675 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7676 tr("Cannot open differencing image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
7677 pParent->m->strLocationFull.c_str());
7678 }
7679 i_setParent(pParent);
7680
7681 treeLock.release();
7682 }
7683 else
7684 {
7685 /* must drop the caller before taking the tree lock */
7686 autoCaller.release();
7687 /* we access m->pParent */
7688 treeLock.acquire();
7689 autoCaller.add();
7690 if (FAILED(autoCaller.rc()))
7691 throw autoCaller.rc();
7692
7693 /* check that parent UUIDs match. Note that there's no need
7694 * for the parent's AutoCaller (our lifetime is bound to
7695 * it) */
7696
7697 if (m->pParent.isNull())
7698 {
7699 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
7700 * and 3.1.0-3.1.8 there are base images out there
7701 * which have a non-zero parent UUID. No point in
7702 * complaining about them, instead automatically
7703 * repair the problem. Later we can bring back the
7704 * error message, but we should wait until really
7705 * most users have repaired their images, either with
7706 * VBoxFixHdd or this way. */
7707#if 1
7708 fRepairImageZeroParentUuid = true;
7709#else /* 0 */
7710 lastAccessError = Utf8StrFmt(
7711 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
7712 location.c_str(),
7713 pVirtualBox->settingsFilePath().c_str());
7714 treeLock.release();
7715 throw S_OK;
7716#endif /* 0 */
7717 }
7718
7719 {
7720 autoCaller.release();
7721 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
7722 autoCaller.add();
7723 if (FAILED(autoCaller.rc()))
7724 throw autoCaller.rc();
7725
7726 if ( !fRepairImageZeroParentUuid
7727 && m->pParent->i_getState() != MediumState_Inaccessible
7728 && m->pParent->i_getId() != parentId)
7729 {
7730 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7731 lastAccessError = Utf8StrFmt(
7732 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
7733 &parentId, location.c_str(),
7734 m->pParent->i_getId().raw(),
7735 pVirtualBox->i_settingsFilePath().c_str());
7736 parentLock.release();
7737 treeLock.release();
7738 throw S_OK;
7739 }
7740 }
7741
7742 /// @todo NEWMEDIA what to do if the parent is not
7743 /// accessible while the diff is? Probably nothing. The
7744 /// real code will detect the mismatch anyway.
7745
7746 treeLock.release();
7747 }
7748 }
7749
7750 mediumSize = VDGetFileSize(hdd, 0);
7751 mediumLogicalSize = VDGetSize(hdd, 0);
7752
7753 success = true;
7754 }
7755 catch (HRESULT aRC)
7756 {
7757 rc = aRC;
7758 }
7759
7760 vrc = VDDestroy(hdd);
7761 if (RT_FAILURE(vrc))
7762 {
7763 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
7764 location.c_str(), i_vdError(vrc).c_str());
7765 success = false;
7766 throw S_OK;
7767 }
7768 }
7769 catch (HRESULT aRC)
7770 {
7771 rc = aRC;
7772 }
7773
7774 autoCaller.release();
7775 treeLock.acquire();
7776 autoCaller.add();
7777 if (FAILED(autoCaller.rc()))
7778 {
7779 m->queryInfoRunning = false;
7780 return autoCaller.rc();
7781 }
7782 alock.acquire();
7783
7784 if (success)
7785 {
7786 m->size = mediumSize;
7787 m->logicalSize = mediumLogicalSize;
7788 m->strLastAccessError.setNull();
7789 }
7790 else
7791 {
7792 m->strLastAccessError = lastAccessError;
7793 Log1WarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
7794 location.c_str(), m->strLastAccessError.c_str(), rc, vrc));
7795 }
7796
7797 /* Set the proper state according to the result of the check */
7798 if (success)
7799 m->preLockState = MediumState_Created;
7800 else
7801 m->preLockState = MediumState_Inaccessible;
7802
7803 /* unblock anyone waiting for the i_queryInfo results */
7804 qlock.release();
7805 m->queryInfoRunning = false;
7806
7807 pToken->Abandon();
7808 pToken.setNull();
7809
7810 if (FAILED(rc))
7811 return rc;
7812
7813 /* If this is a base image which incorrectly has a parent UUID set,
7814 * repair the image now by zeroing the parent UUID. This is only done
7815 * when we have structural information from a config file, on import
7816 * this is not possible. If someone would accidentally call openMedium
7817 * with a diff image before the base is registered this would destroy
7818 * the diff. Not acceptable. */
7819 do
7820 {
7821 if (fRepairImageZeroParentUuid)
7822 {
7823 rc = LockWrite(pToken.asOutParam());
7824 if (FAILED(rc))
7825 break;
7826
7827 alock.release();
7828
7829 try
7830 {
7831 PVDISK hdd;
7832 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7833 ComAssertRCThrow(vrc, E_FAIL);
7834
7835 try
7836 {
7837 vrc = VDOpen(hdd,
7838 format.c_str(),
7839 location.c_str(),
7840 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
7841 m->vdImageIfaces);
7842 if (RT_FAILURE(vrc))
7843 throw S_OK;
7844
7845 RTUUID zeroParentUuid;
7846 RTUuidClear(&zeroParentUuid);
7847 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
7848 ComAssertRCThrow(vrc, E_FAIL);
7849 }
7850 catch (HRESULT aRC)
7851 {
7852 rc = aRC;
7853 }
7854
7855 VDDestroy(hdd);
7856 }
7857 catch (HRESULT aRC)
7858 {
7859 rc = aRC;
7860 }
7861
7862 pToken->Abandon();
7863 pToken.setNull();
7864 if (FAILED(rc))
7865 break;
7866 }
7867 } while(0);
7868
7869 return rc;
7870}
7871
7872/**
7873 * Performs extra checks if the medium can be closed and returns S_OK in
7874 * this case. Otherwise, returns a respective error message. Called by
7875 * Close() under the medium tree lock and the medium lock.
7876 *
7877 * @note Also reused by Medium::Reset().
7878 *
7879 * @note Caller must hold the media tree write lock!
7880 */
7881HRESULT Medium::i_canClose()
7882{
7883 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7884
7885 if (i_getChildren().size() != 0)
7886 return setError(VBOX_E_OBJECT_IN_USE,
7887 tr("Cannot close medium '%s' because it has %d child media", "", i_getChildren().size()),
7888 m->strLocationFull.c_str(), i_getChildren().size());
7889
7890 return S_OK;
7891}
7892
7893/**
7894 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
7895 *
7896 * @note Caller must have locked the media tree lock for writing!
7897 */
7898HRESULT Medium::i_unregisterWithVirtualBox()
7899{
7900 /* Note that we need to de-associate ourselves from the parent to let
7901 * VirtualBox::i_unregisterMedium() properly save the registry */
7902
7903 /* we modify m->pParent and access children */
7904 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7905
7906 Medium *pParentBackup = m->pParent;
7907 AssertReturn(i_getChildren().size() == 0, E_FAIL);
7908 if (m->pParent)
7909 i_deparent();
7910
7911 HRESULT rc = m->pVirtualBox->i_unregisterMedium(this);
7912 if (FAILED(rc))
7913 {
7914 if (pParentBackup)
7915 {
7916 // re-associate with the parent as we are still relatives in the registry
7917 i_setParent(pParentBackup);
7918 }
7919 }
7920
7921 return rc;
7922}
7923
7924/**
7925 * Like SetProperty but do not trigger a settings store. Only for internal use!
7926 */
7927HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
7928{
7929 AutoCaller autoCaller(this);
7930 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7931
7932 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
7933
7934 switch (m->state)
7935 {
7936 case MediumState_Created:
7937 case MediumState_Inaccessible:
7938 break;
7939 default:
7940 return i_setStateError();
7941 }
7942
7943 m->mapProperties[aName] = aValue;
7944
7945 return S_OK;
7946}
7947
7948/**
7949 * Sets the extended error info according to the current media state.
7950 *
7951 * @note Must be called from under this object's write or read lock.
7952 */
7953HRESULT Medium::i_setStateError()
7954{
7955 HRESULT rc = E_FAIL;
7956
7957 switch (m->state)
7958 {
7959 case MediumState_NotCreated:
7960 {
7961 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7962 tr("Storage for the medium '%s' is not created"),
7963 m->strLocationFull.c_str());
7964 break;
7965 }
7966 case MediumState_Created:
7967 {
7968 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7969 tr("Storage for the medium '%s' is already created"),
7970 m->strLocationFull.c_str());
7971 break;
7972 }
7973 case MediumState_LockedRead:
7974 {
7975 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7976 tr("Medium '%s' is locked for reading by another task"),
7977 m->strLocationFull.c_str());
7978 break;
7979 }
7980 case MediumState_LockedWrite:
7981 {
7982 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7983 tr("Medium '%s' is locked for writing by another task"),
7984 m->strLocationFull.c_str());
7985 break;
7986 }
7987 case MediumState_Inaccessible:
7988 {
7989 /* be in sync with Console::powerUpThread() */
7990 if (!m->strLastAccessError.isEmpty())
7991 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7992 tr("Medium '%s' is not accessible. %s"),
7993 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
7994 else
7995 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7996 tr("Medium '%s' is not accessible"),
7997 m->strLocationFull.c_str());
7998 break;
7999 }
8000 case MediumState_Creating:
8001 {
8002 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
8003 tr("Storage for the medium '%s' is being created"),
8004 m->strLocationFull.c_str());
8005 break;
8006 }
8007 case MediumState_Deleting:
8008 {
8009 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
8010 tr("Storage for the medium '%s' is being deleted"),
8011 m->strLocationFull.c_str());
8012 break;
8013 }
8014 default:
8015 {
8016 AssertFailed();
8017 break;
8018 }
8019 }
8020
8021 return rc;
8022}
8023
8024/**
8025 * Sets the value of m->strLocationFull. The given location must be a fully
8026 * qualified path; relative paths are not supported here.
8027 *
8028 * As a special exception, if the specified location is a file path that ends with '/'
8029 * then the file name part will be generated by this method automatically in the format
8030 * '{\<uuid\>}.\<ext\>' where \<uuid\> is a fresh UUID that this method will generate
8031 * and assign to this medium, and \<ext\> is the default extension for this
8032 * medium's storage format. Note that this procedure requires the media state to
8033 * be NotCreated and will return a failure otherwise.
8034 *
8035 * @param aLocation Location of the storage unit. If the location is a FS-path,
8036 * then it can be relative to the VirtualBox home directory.
8037 * @param aFormat Optional fallback format if it is an import and the format
8038 * cannot be determined.
8039 *
8040 * @note Must be called from under this object's write lock.
8041 */
8042HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
8043 const Utf8Str &aFormat /* = Utf8Str::Empty */)
8044{
8045 AssertReturn(!aLocation.isEmpty(), E_FAIL);
8046
8047 AutoCaller autoCaller(this);
8048 AssertComRCReturnRC(autoCaller.rc());
8049
8050 /* formatObj may be null only when initializing from an existing path and
8051 * no format is known yet */
8052 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
8053 || ( getObjectState().getState() == ObjectState::InInit
8054 && m->state != MediumState_NotCreated
8055 && m->id.isZero()
8056 && m->strFormat.isEmpty()
8057 && m->formatObj.isNull()),
8058 E_FAIL);
8059
8060 /* are we dealing with a new medium constructed using the existing
8061 * location? */
8062 bool isImport = m->strFormat.isEmpty();
8063
8064 if ( isImport
8065 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
8066 && !m->hostDrive))
8067 {
8068 Guid id;
8069
8070 Utf8Str locationFull(aLocation);
8071
8072 if (m->state == MediumState_NotCreated)
8073 {
8074 /* must be a file (formatObj must be already known) */
8075 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
8076
8077 if (RTPathFilename(aLocation.c_str()) == NULL)
8078 {
8079 /* no file name is given (either an empty string or ends with a
8080 * slash), generate a new UUID + file name if the state allows
8081 * this */
8082
8083 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
8084 (tr("Must be at least one extension if it is MediumFormatCapabilities_File\n")),
8085 E_FAIL);
8086
8087 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
8088 ComAssertMsgRet(!strExt.isEmpty(),
8089 (tr("Default extension must not be empty\n")),
8090 E_FAIL);
8091
8092 id.create();
8093
8094 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
8095 aLocation.c_str(), id.raw(), strExt.c_str());
8096 }
8097 }
8098
8099 // we must always have full paths now (if it refers to a file)
8100 if ( ( m->formatObj.isNull()
8101 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
8102 && !RTPathStartsWithRoot(locationFull.c_str()))
8103 return setError(VBOX_E_FILE_ERROR,
8104 tr("The given path '%s' is not fully qualified"),
8105 locationFull.c_str());
8106
8107 /* detect the backend from the storage unit if importing */
8108 if (isImport)
8109 {
8110 VDTYPE const enmDesiredType = i_convertDeviceType();
8111 VDTYPE enmType = VDTYPE_INVALID;
8112 char *backendName = NULL;
8113
8114 /* is it a file? */
8115 RTFILE hFile;
8116 int vrc = RTFileOpen(&hFile, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
8117 if (RT_SUCCESS(vrc))
8118 {
8119 RTFileClose(hFile);
8120 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
8121 locationFull.c_str(), enmDesiredType, &backendName, &enmType);
8122 }
8123 else if ( vrc != VERR_FILE_NOT_FOUND
8124 && vrc != VERR_PATH_NOT_FOUND
8125 && vrc != VERR_ACCESS_DENIED
8126 && locationFull != aLocation)
8127 {
8128 /* assume it's not a file, restore the original location */
8129 locationFull = aLocation;
8130 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
8131 locationFull.c_str(), enmDesiredType, &backendName, &enmType);
8132 }
8133
8134 if (RT_FAILURE(vrc))
8135 {
8136 if (vrc == VERR_ACCESS_DENIED)
8137 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8138 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
8139 locationFull.c_str(), vrc);
8140 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
8141 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8142 tr("Could not find file for the medium '%s' (%Rrc)"),
8143 locationFull.c_str(), vrc);
8144 if (aFormat.isEmpty())
8145 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8146 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
8147 locationFull.c_str(), vrc);
8148 HRESULT rc = i_setFormat(aFormat);
8149 /* setFormat() must not fail since we've just used the backend so
8150 * the format object must be there */
8151 AssertComRCReturnRC(rc);
8152 }
8153 else if ( enmType == VDTYPE_INVALID
8154 || m->devType != i_convertToDeviceType(enmType))
8155 {
8156 /*
8157 * The user tried to use a image as a device which is not supported
8158 * by the backend.
8159 */
8160 RTStrFree(backendName);
8161 return setError(E_FAIL,
8162 tr("The medium '%s' can't be used as the requested device type (%s, detected %s)"),
8163 locationFull.c_str(), getDeviceTypeName(m->devType), getVDTypeName(enmType));
8164 }
8165 else
8166 {
8167 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
8168
8169 HRESULT rc = i_setFormat(backendName);
8170 RTStrFree(backendName);
8171
8172 /* setFormat() must not fail since we've just used the backend so
8173 * the format object must be there */
8174 AssertComRCReturnRC(rc);
8175 }
8176 }
8177
8178 m->strLocationFull = locationFull;
8179
8180 /* is it still a file? */
8181 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
8182 && (m->state == MediumState_NotCreated)
8183 )
8184 /* assign a new UUID (this UUID will be used when calling
8185 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
8186 * also do that if we didn't generate it to make sure it is
8187 * either generated by us or reset to null */
8188 unconst(m->id) = id;
8189 }
8190 else
8191 m->strLocationFull = aLocation;
8192
8193 return S_OK;
8194}
8195
8196/**
8197 * Checks that the format ID is valid and sets it on success.
8198 *
8199 * Note that this method will caller-reference the format object on success!
8200 * This reference must be released somewhere to let the MediumFormat object be
8201 * uninitialized.
8202 *
8203 * @note Must be called from under this object's write lock.
8204 */
8205HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
8206{
8207 /* get the format object first */
8208 {
8209 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
8210 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
8211
8212 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
8213 if (m->formatObj.isNull())
8214 return setError(E_INVALIDARG,
8215 tr("Invalid medium storage format '%s'"),
8216 aFormat.c_str());
8217
8218 /* get properties (preinsert them as keys in the map). Note that the
8219 * map doesn't grow over the object life time since the set of
8220 * properties is meant to be constant. */
8221
8222 Assert(m->mapProperties.empty());
8223
8224 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
8225 it != m->formatObj->i_getProperties().end();
8226 ++it)
8227 {
8228 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
8229 }
8230 }
8231
8232 unconst(m->strFormat) = aFormat;
8233
8234 return S_OK;
8235}
8236
8237/**
8238 * Converts the Medium device type to the VD type.
8239 */
8240VDTYPE Medium::i_convertDeviceType()
8241{
8242 VDTYPE enmType;
8243
8244 switch (m->devType)
8245 {
8246 case DeviceType_HardDisk:
8247 enmType = VDTYPE_HDD;
8248 break;
8249 case DeviceType_DVD:
8250 enmType = VDTYPE_OPTICAL_DISC;
8251 break;
8252 case DeviceType_Floppy:
8253 enmType = VDTYPE_FLOPPY;
8254 break;
8255 default:
8256 ComAssertFailedRet(VDTYPE_INVALID);
8257 }
8258
8259 return enmType;
8260}
8261
8262/**
8263 * Converts from the VD type to the medium type.
8264 */
8265DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
8266{
8267 DeviceType_T devType;
8268
8269 switch (enmType)
8270 {
8271 case VDTYPE_HDD:
8272 devType = DeviceType_HardDisk;
8273 break;
8274 case VDTYPE_OPTICAL_DISC:
8275 devType = DeviceType_DVD;
8276 break;
8277 case VDTYPE_FLOPPY:
8278 devType = DeviceType_Floppy;
8279 break;
8280 default:
8281 ComAssertFailedRet(DeviceType_Null);
8282 }
8283
8284 return devType;
8285}
8286
8287/**
8288 * Internal method which checks whether a property name is for a filter plugin.
8289 */
8290bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
8291{
8292 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
8293 size_t offSlash;
8294 if ((offSlash = aName.find("/", 0)) != aName.npos)
8295 {
8296 com::Utf8Str strFilter;
8297 com::Utf8Str strKey;
8298
8299 HRESULT rc = strFilter.assignEx(aName, 0, offSlash);
8300 if (FAILED(rc))
8301 return false;
8302
8303 rc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
8304 if (FAILED(rc))
8305 return false;
8306
8307 VDFILTERINFO FilterInfo;
8308 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
8309 if (RT_SUCCESS(vrc))
8310 {
8311 /* Check that the property exists. */
8312 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
8313 while (paConfig->pszKey)
8314 {
8315 if (strKey.equals(paConfig->pszKey))
8316 return true;
8317 paConfig++;
8318 }
8319 }
8320 }
8321
8322 return false;
8323}
8324
8325/**
8326 * Returns the last error message collected by the i_vdErrorCall callback and
8327 * resets it.
8328 *
8329 * The error message is returned prepended with a dot and a space, like this:
8330 * <code>
8331 * ". <error_text> (%Rrc)"
8332 * </code>
8333 * to make it easily appendable to a more general error message. The @c %Rrc
8334 * format string is given @a aVRC as an argument.
8335 *
8336 * If there is no last error message collected by i_vdErrorCall or if it is a
8337 * null or empty string, then this function returns the following text:
8338 * <code>
8339 * " (%Rrc)"
8340 * </code>
8341 *
8342 * @note Doesn't do any object locking; it is assumed that the caller makes sure
8343 * the callback isn't called by more than one thread at a time.
8344 *
8345 * @param aVRC VBox error code to use when no error message is provided.
8346 */
8347Utf8Str Medium::i_vdError(int aVRC)
8348{
8349 Utf8Str error;
8350
8351 if (m->vdError.isEmpty())
8352 error = Utf8StrFmt(" (%Rrc)", aVRC);
8353 else
8354 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
8355
8356 m->vdError.setNull();
8357
8358 return error;
8359}
8360
8361/**
8362 * Error message callback.
8363 *
8364 * Puts the reported error message to the m->vdError field.
8365 *
8366 * @note Doesn't do any object locking; it is assumed that the caller makes sure
8367 * the callback isn't called by more than one thread at a time.
8368 *
8369 * @param pvUser The opaque data passed on container creation.
8370 * @param rc The VBox error code.
8371 * @param SRC_POS Use RT_SRC_POS.
8372 * @param pszFormat Error message format string.
8373 * @param va Error message arguments.
8374 */
8375/*static*/
8376DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
8377 const char *pszFormat, va_list va)
8378{
8379 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
8380
8381 Medium *that = static_cast<Medium*>(pvUser);
8382 AssertReturnVoid(that != NULL);
8383
8384 if (that->m->vdError.isEmpty())
8385 that->m->vdError =
8386 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
8387 else
8388 that->m->vdError =
8389 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
8390 Utf8Str(pszFormat, va).c_str(), rc);
8391}
8392
8393/* static */
8394DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
8395 const char * /* pszzValid */)
8396{
8397 Medium *that = static_cast<Medium*>(pvUser);
8398 AssertReturn(that != NULL, false);
8399
8400 /* we always return true since the only keys we have are those found in
8401 * VDBACKENDINFO */
8402 return true;
8403}
8404
8405/* static */
8406DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
8407 const char *pszName,
8408 size_t *pcbValue)
8409{
8410 AssertPtrReturn(pcbValue, VERR_INVALID_POINTER);
8411
8412 Medium *that = static_cast<Medium*>(pvUser);
8413 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
8414
8415 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
8416 if (it == that->m->mapProperties.end())
8417 return VERR_CFGM_VALUE_NOT_FOUND;
8418
8419 /* we interpret null values as "no value" in Medium */
8420 if (it->second.isEmpty())
8421 return VERR_CFGM_VALUE_NOT_FOUND;
8422
8423 *pcbValue = it->second.length() + 1 /* include terminator */;
8424
8425 return VINF_SUCCESS;
8426}
8427
8428/* static */
8429DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
8430 const char *pszName,
8431 char *pszValue,
8432 size_t cchValue)
8433{
8434 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
8435
8436 Medium *that = static_cast<Medium*>(pvUser);
8437 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
8438
8439 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
8440 if (it == that->m->mapProperties.end())
8441 return VERR_CFGM_VALUE_NOT_FOUND;
8442
8443 /* we interpret null values as "no value" in Medium */
8444 if (it->second.isEmpty())
8445 return VERR_CFGM_VALUE_NOT_FOUND;
8446
8447 const Utf8Str &value = it->second;
8448 if (value.length() >= cchValue)
8449 return VERR_CFGM_NOT_ENOUGH_SPACE;
8450
8451 memcpy(pszValue, value.c_str(), value.length() + 1);
8452
8453 return VINF_SUCCESS;
8454}
8455
8456DECLCALLBACK(bool) Medium::i_vdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid)
8457{
8458 /* Just return always true here. */
8459 NOREF(pvUser);
8460 NOREF(pszzValid);
8461 return true;
8462}
8463
8464DECLCALLBACK(int) Medium::i_vdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
8465{
8466 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8467 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8468 AssertPtrReturn(pcbValue, VERR_INVALID_POINTER);
8469
8470 size_t cbValue = 0;
8471 if (!strcmp(pszName, "Algorithm"))
8472 cbValue = strlen(pSettings->pszCipher) + 1;
8473 else if (!strcmp(pszName, "KeyId"))
8474 cbValue = sizeof("irrelevant");
8475 else if (!strcmp(pszName, "KeyStore"))
8476 {
8477 if (!pSettings->pszKeyStoreLoad)
8478 return VERR_CFGM_VALUE_NOT_FOUND;
8479 cbValue = strlen(pSettings->pszKeyStoreLoad) + 1;
8480 }
8481 else if (!strcmp(pszName, "CreateKeyStore"))
8482 cbValue = 2; /* Single digit + terminator. */
8483 else
8484 return VERR_CFGM_VALUE_NOT_FOUND;
8485
8486 *pcbValue = cbValue + 1 /* include terminator */;
8487
8488 return VINF_SUCCESS;
8489}
8490
8491DECLCALLBACK(int) Medium::i_vdCryptoConfigQuery(void *pvUser, const char *pszName,
8492 char *pszValue, size_t cchValue)
8493{
8494 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8495 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8496 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
8497
8498 const char *psz = NULL;
8499 if (!strcmp(pszName, "Algorithm"))
8500 psz = pSettings->pszCipher;
8501 else if (!strcmp(pszName, "KeyId"))
8502 psz = "irrelevant";
8503 else if (!strcmp(pszName, "KeyStore"))
8504 psz = pSettings->pszKeyStoreLoad;
8505 else if (!strcmp(pszName, "CreateKeyStore"))
8506 {
8507 if (pSettings->fCreateKeyStore)
8508 psz = "1";
8509 else
8510 psz = "0";
8511 }
8512 else
8513 return VERR_CFGM_VALUE_NOT_FOUND;
8514
8515 size_t cch = strlen(psz);
8516 if (cch >= cchValue)
8517 return VERR_CFGM_NOT_ENOUGH_SPACE;
8518
8519 memcpy(pszValue, psz, cch + 1);
8520 return VINF_SUCCESS;
8521}
8522
8523DECLCALLBACK(int) Medium::i_vdConfigUpdate(void *pvUser,
8524 bool fCreate,
8525 const char *pszName,
8526 const char *pszValue)
8527{
8528 Medium *that = (Medium *)pvUser;
8529
8530 // Detect if this runs inside i_queryInfo() on the current thread.
8531 // Skip if not. Check does not need synchronization.
8532 if (!that->m || !that->m->queryInfoRunning || !that->m->queryInfoSem.isWriteLockOnCurrentThread())
8533 return VINF_SUCCESS;
8534
8535 // It's guaranteed that this code is executing inside Medium::i_queryInfo,
8536 // can assume it took care of synchronization.
8537 int rv = VINF_SUCCESS;
8538 Utf8Str strName(pszName);
8539 settings::StringsMap::const_iterator it = that->m->mapProperties.find(strName);
8540 if (it == that->m->mapProperties.end() && !fCreate)
8541 rv = VERR_CFGM_VALUE_NOT_FOUND;
8542 else
8543 that->m->mapProperties[strName] = Utf8Str(pszValue);
8544 return rv;
8545}
8546
8547DECLCALLBACK(int) Medium::i_vdCryptoKeyRetain(void *pvUser, const char *pszId,
8548 const uint8_t **ppbKey, size_t *pcbKey)
8549{
8550 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8551 NOREF(pszId);
8552 NOREF(ppbKey);
8553 NOREF(pcbKey);
8554 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8555 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8556}
8557
8558DECLCALLBACK(int) Medium::i_vdCryptoKeyRelease(void *pvUser, const char *pszId)
8559{
8560 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8561 NOREF(pszId);
8562 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8563 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8564}
8565
8566DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
8567{
8568 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8569 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8570
8571 NOREF(pszId);
8572 *ppszPassword = pSettings->pszPassword;
8573 return VINF_SUCCESS;
8574}
8575
8576DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
8577{
8578 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8579 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8580 NOREF(pszId);
8581 return VINF_SUCCESS;
8582}
8583
8584DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore)
8585{
8586 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8587 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8588
8589 pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore);
8590 if (!pSettings->pszKeyStore)
8591 return VERR_NO_MEMORY;
8592
8593 memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore);
8594 return VINF_SUCCESS;
8595}
8596
8597DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher,
8598 const uint8_t *pbDek, size_t cbDek)
8599{
8600 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8601 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8602
8603 pSettings->pszCipherReturned = RTStrDup(pszCipher);
8604 pSettings->pbDek = pbDek;
8605 pSettings->cbDek = cbDek;
8606
8607 return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY;
8608}
8609
8610/**
8611 * Creates a VDISK instance for this medium.
8612 *
8613 * @note Caller should not hold any medium related locks as this method will
8614 * acquire the medium lock for writing and others (VirtualBox).
8615 *
8616 * @returns COM status code.
8617 * @param fWritable Whether to return a writable VDISK instance
8618 * (true) or a read-only one (false).
8619 * @param pKeyStore The key store.
8620 * @param ppHdd Where to return the pointer to the VDISK on
8621 * success.
8622 * @param pMediumLockList The lock list to populate and lock. Caller
8623 * is responsible for calling the destructor or
8624 * MediumLockList::Clear() after destroying
8625 * @a *ppHdd
8626 * @param pCryptoSettings The crypto settings to use for setting up
8627 * decryption/encryption of the VDISK. This object
8628 * must be alive until the VDISK is destroyed!
8629 */
8630HRESULT Medium::i_openForIO(bool fWritable, SecretKeyStore *pKeyStore, PVDISK *ppHdd, MediumLockList *pMediumLockList,
8631 MediumCryptoFilterSettings *pCryptoSettings)
8632{
8633 /*
8634 * Create the media lock list and lock the media.
8635 */
8636 HRESULT hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
8637 fWritable ? this : NULL /* pToLockWrite */,
8638 false /* fMediumLockWriteAll */,
8639 NULL,
8640 *pMediumLockList);
8641 if (SUCCEEDED(hrc))
8642 hrc = pMediumLockList->Lock();
8643 if (FAILED(hrc))
8644 return hrc;
8645
8646 /*
8647 * Get the base medium before write locking this medium.
8648 */
8649 ComObjPtr<Medium> pBase = i_getBase();
8650 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8651
8652 /*
8653 * Create the VDISK instance.
8654 */
8655 PVDISK pHdd;
8656 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pHdd);
8657 AssertRCReturn(vrc, E_FAIL);
8658
8659 /*
8660 * Goto avoidance using try/catch/throw(HRESULT).
8661 */
8662 try
8663 {
8664 settings::StringsMap::iterator itKeyStore = pBase->m->mapProperties.find("CRYPT/KeyStore");
8665 if (itKeyStore != pBase->m->mapProperties.end())
8666 {
8667#ifdef VBOX_WITH_EXTPACK
8668 settings::StringsMap::iterator itKeyId = pBase->m->mapProperties.find("CRYPT/KeyId");
8669
8670 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
8671 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
8672 {
8673 /* Load the plugin */
8674 Utf8Str strPlugin;
8675 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
8676 if (SUCCEEDED(hrc))
8677 {
8678 vrc = VDPluginLoadFromFilename(strPlugin.c_str());
8679 if (RT_FAILURE(vrc))
8680 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
8681 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
8682 i_vdError(vrc).c_str());
8683 }
8684 else
8685 throw setError(VBOX_E_NOT_SUPPORTED,
8686 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
8687 ORACLE_PUEL_EXTPACK_NAME);
8688 }
8689 else
8690 throw setError(VBOX_E_NOT_SUPPORTED,
8691 tr("Encryption is not supported because the extension pack '%s' is missing"),
8692 ORACLE_PUEL_EXTPACK_NAME);
8693
8694 if (itKeyId == pBase->m->mapProperties.end())
8695 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8696 tr("Image '%s' is configured for encryption but doesn't has a key identifier set"),
8697 pBase->m->strLocationFull.c_str());
8698
8699 /* Find the proper secret key in the key store. */
8700 if (!pKeyStore)
8701 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8702 tr("Image '%s' is configured for encryption but there is no key store to retrieve the password from"),
8703 pBase->m->strLocationFull.c_str());
8704
8705 SecretKey *pKey = NULL;
8706 vrc = pKeyStore->retainSecretKey(itKeyId->second, &pKey);
8707 if (RT_FAILURE(vrc))
8708 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
8709 tr("Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)"),
8710 itKeyId->second.c_str(), vrc);
8711
8712 i_taskEncryptSettingsSetup(pCryptoSettings, NULL, itKeyStore->second.c_str(), (const char *)pKey->getKeyBuffer(),
8713 false /* fCreateKeyStore */);
8714 vrc = VDFilterAdd(pHdd, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pCryptoSettings->vdFilterIfaces);
8715 pKeyStore->releaseSecretKey(itKeyId->second);
8716 if (vrc == VERR_VD_PASSWORD_INCORRECT)
8717 throw setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc, tr("The password to decrypt the image is incorrect"));
8718 if (RT_FAILURE(vrc))
8719 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc, tr("Failed to load the decryption filter: %s"),
8720 i_vdError(vrc).c_str());
8721#else
8722 RT_NOREF(pKeyStore, pCryptoSettings);
8723 throw setError(VBOX_E_NOT_SUPPORTED,
8724 tr("Encryption is not supported because extension pack support is not built in"));
8725#endif /* VBOX_WITH_EXTPACK */
8726 }
8727
8728 /*
8729 * Open all media in the source chain.
8730 */
8731 MediumLockList::Base::const_iterator sourceListBegin = pMediumLockList->GetBegin();
8732 MediumLockList::Base::const_iterator sourceListEnd = pMediumLockList->GetEnd();
8733 MediumLockList::Base::const_iterator mediumListLast = sourceListEnd;
8734 --mediumListLast;
8735
8736 for (MediumLockList::Base::const_iterator it = sourceListBegin; it != sourceListEnd; ++it)
8737 {
8738 const MediumLock &mediumLock = *it;
8739 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8740 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8741
8742 /* sanity check */
8743 Assert(pMedium->m->state == (fWritable && it == mediumListLast ? MediumState_LockedWrite : MediumState_LockedRead));
8744
8745 /* Open all media in read-only mode. */
8746 vrc = VDOpen(pHdd,
8747 pMedium->m->strFormat.c_str(),
8748 pMedium->m->strLocationFull.c_str(),
8749 m->uOpenFlagsDef | (fWritable && it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
8750 pMedium->m->vdImageIfaces);
8751 if (RT_FAILURE(vrc))
8752 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8753 tr("Could not open the medium storage unit '%s'%s"),
8754 pMedium->m->strLocationFull.c_str(),
8755 i_vdError(vrc).c_str());
8756 }
8757
8758 Assert(m->state == (fWritable ? MediumState_LockedWrite : MediumState_LockedRead));
8759
8760 /*
8761 * Done!
8762 */
8763 *ppHdd = pHdd;
8764 return S_OK;
8765 }
8766 catch (HRESULT hrc2)
8767 {
8768 hrc = hrc2;
8769 }
8770
8771 VDDestroy(pHdd);
8772 return hrc;
8773
8774}
8775
8776/**
8777 * Implementation code for the "create base" task.
8778 *
8779 * This only gets started from Medium::CreateBaseStorage() and always runs
8780 * asynchronously. As a result, we always save the VirtualBox.xml file when
8781 * we're done here.
8782 *
8783 * @param task
8784 * @return
8785 */
8786HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
8787{
8788 /** @todo r=klaus The code below needs to be double checked with regard
8789 * to lock order violations, it probably causes lock order issues related
8790 * to the AutoCaller usage. */
8791 HRESULT rc = S_OK;
8792
8793 /* these parameters we need after creation */
8794 uint64_t size = 0, logicalSize = 0;
8795 MediumVariant_T variant = MediumVariant_Standard;
8796 bool fGenerateUuid = false;
8797
8798 try
8799 {
8800 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8801
8802 /* The object may request a specific UUID (through a special form of
8803 * the moveTo() argument). Otherwise we have to generate it */
8804 Guid id = m->id;
8805
8806 fGenerateUuid = id.isZero();
8807 if (fGenerateUuid)
8808 {
8809 id.create();
8810 /* VirtualBox::i_registerMedium() will need UUID */
8811 unconst(m->id) = id;
8812 }
8813
8814 Utf8Str format(m->strFormat);
8815 Utf8Str location(m->strLocationFull);
8816 uint64_t capabilities = m->formatObj->i_getCapabilities();
8817 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
8818 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
8819 Assert(m->state == MediumState_Creating);
8820
8821 PVDISK hdd;
8822 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8823 ComAssertRCThrow(vrc, E_FAIL);
8824
8825 /* unlock before the potentially lengthy operation */
8826 thisLock.release();
8827
8828 try
8829 {
8830 /* ensure the directory exists */
8831 if (capabilities & MediumFormatCapabilities_File)
8832 {
8833 rc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8834 if (FAILED(rc))
8835 throw rc;
8836 }
8837
8838 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
8839
8840 vrc = VDCreateBase(hdd,
8841 format.c_str(),
8842 location.c_str(),
8843 task.mSize,
8844 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
8845 NULL,
8846 &geo,
8847 &geo,
8848 id.raw(),
8849 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8850 m->vdImageIfaces,
8851 task.mVDOperationIfaces);
8852 if (RT_FAILURE(vrc))
8853 {
8854 if (vrc == VERR_VD_INVALID_TYPE)
8855 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8856 tr("Parameters for creating the medium storage unit '%s' are invalid%s"),
8857 location.c_str(), i_vdError(vrc).c_str());
8858 else
8859 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8860 tr("Could not create the medium storage unit '%s'%s"),
8861 location.c_str(), i_vdError(vrc).c_str());
8862 }
8863
8864 if (task.mVariant & MediumVariant_Formatted)
8865 {
8866 RTVFSFILE hVfsFile;
8867 vrc = VDCreateVfsFileFromDisk(hdd, 0 /*fFlags*/, &hVfsFile);
8868 if (RT_FAILURE(vrc))
8869 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Opening medium storage unit '%s' failed%s"),
8870 location.c_str(), i_vdError(vrc).c_str());
8871 RTERRINFOSTATIC ErrInfo;
8872 vrc = RTFsFatVolFormat(hVfsFile, 0 /* offVol */, 0 /* cbVol */, RTFSFATVOL_FMT_F_FULL,
8873 0 /* cbSector */, 0 /* cbSectorPerCluster */, RTFSFATTYPE_INVALID,
8874 0 /* cHeads */, 0 /* cSectorsPerTrack*/, 0 /* bMedia */,
8875 0 /* cRootDirEntries */, 0 /* cHiddenSectors */,
8876 RTErrInfoInitStatic(&ErrInfo));
8877 RTVfsFileRelease(hVfsFile);
8878 if (RT_FAILURE(vrc) && RTErrInfoIsSet(&ErrInfo.Core))
8879 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed: %s"),
8880 location.c_str(), ErrInfo.Core.pszMsg);
8881 if (RT_FAILURE(vrc))
8882 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed%s"),
8883 location.c_str(), i_vdError(vrc).c_str());
8884 }
8885
8886 size = VDGetFileSize(hdd, 0);
8887 logicalSize = VDGetSize(hdd, 0);
8888 unsigned uImageFlags;
8889 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8890 if (RT_SUCCESS(vrc))
8891 variant = (MediumVariant_T)uImageFlags;
8892 }
8893 catch (HRESULT aRC) { rc = aRC; }
8894
8895 VDDestroy(hdd);
8896 }
8897 catch (HRESULT aRC) { rc = aRC; }
8898
8899 if (SUCCEEDED(rc))
8900 {
8901 /* register with mVirtualBox as the last step and move to
8902 * Created state only on success (leaving an orphan file is
8903 * better than breaking media registry consistency) */
8904 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8905 ComObjPtr<Medium> pMedium;
8906 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
8907 Assert(pMedium == NULL || this == pMedium);
8908 }
8909
8910 // re-acquire the lock before changing state
8911 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8912
8913 if (SUCCEEDED(rc))
8914 {
8915 m->state = MediumState_Created;
8916
8917 m->size = size;
8918 m->logicalSize = logicalSize;
8919 m->variant = variant;
8920
8921 thisLock.release();
8922 i_markRegistriesModified();
8923 if (task.isAsync())
8924 {
8925 // in asynchronous mode, save settings now
8926 m->pVirtualBox->i_saveModifiedRegistries();
8927 }
8928 }
8929 else
8930 {
8931 /* back to NotCreated on failure */
8932 m->state = MediumState_NotCreated;
8933
8934 /* reset UUID to prevent it from being reused next time */
8935 if (fGenerateUuid)
8936 unconst(m->id).clear();
8937 }
8938
8939 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
8940 {
8941 m->pVirtualBox->i_onMediumConfigChanged(this);
8942 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
8943 }
8944
8945 return rc;
8946}
8947
8948/**
8949 * Implementation code for the "create diff" task.
8950 *
8951 * This task always gets started from Medium::createDiffStorage() and can run
8952 * synchronously or asynchronously depending on the "wait" parameter passed to
8953 * that function. If we run synchronously, the caller expects the medium
8954 * registry modification to be set before returning; otherwise (in asynchronous
8955 * mode), we save the settings ourselves.
8956 *
8957 * @param task
8958 * @return
8959 */
8960HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
8961{
8962 /** @todo r=klaus The code below needs to be double checked with regard
8963 * to lock order violations, it probably causes lock order issues related
8964 * to the AutoCaller usage. */
8965 HRESULT rcTmp = S_OK;
8966
8967 const ComObjPtr<Medium> &pTarget = task.mTarget;
8968
8969 uint64_t size = 0, logicalSize = 0;
8970 MediumVariant_T variant = MediumVariant_Standard;
8971 bool fGenerateUuid = false;
8972
8973 try
8974 {
8975 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8976 {
8977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8978 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8979 tr("Cannot create differencing image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
8980 m->strLocationFull.c_str());
8981 }
8982
8983 /* Lock both in {parent,child} order. */
8984 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8985
8986 /* The object may request a specific UUID (through a special form of
8987 * the moveTo() argument). Otherwise we have to generate it */
8988 Guid targetId = pTarget->m->id;
8989
8990 fGenerateUuid = targetId.isZero();
8991 if (fGenerateUuid)
8992 {
8993 targetId.create();
8994 /* VirtualBox::i_registerMedium() will need UUID */
8995 unconst(pTarget->m->id) = targetId;
8996 }
8997
8998 Guid id = m->id;
8999
9000 Utf8Str targetFormat(pTarget->m->strFormat);
9001 Utf8Str targetLocation(pTarget->m->strLocationFull);
9002 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
9003 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
9004
9005 Assert(pTarget->m->state == MediumState_Creating);
9006 Assert(m->state == MediumState_LockedRead);
9007
9008 PVDISK hdd;
9009 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9010 ComAssertRCThrow(vrc, E_FAIL);
9011
9012 /* the two media are now protected by their non-default states;
9013 * unlock the media before the potentially lengthy operation */
9014 mediaLock.release();
9015
9016 try
9017 {
9018 /* Open all media in the target chain but the last. */
9019 MediumLockList::Base::const_iterator targetListBegin =
9020 task.mpMediumLockList->GetBegin();
9021 MediumLockList::Base::const_iterator targetListEnd =
9022 task.mpMediumLockList->GetEnd();
9023 for (MediumLockList::Base::const_iterator it = targetListBegin;
9024 it != targetListEnd;
9025 ++it)
9026 {
9027 const MediumLock &mediumLock = *it;
9028 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9029
9030 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9031
9032 /* Skip over the target diff medium */
9033 if (pMedium->m->state == MediumState_Creating)
9034 continue;
9035
9036 /* sanity check */
9037 Assert(pMedium->m->state == MediumState_LockedRead);
9038
9039 /* Open all media in appropriate mode. */
9040 vrc = VDOpen(hdd,
9041 pMedium->m->strFormat.c_str(),
9042 pMedium->m->strLocationFull.c_str(),
9043 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9044 pMedium->m->vdImageIfaces);
9045 if (RT_FAILURE(vrc))
9046 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9047 tr("Could not open the medium storage unit '%s'%s"),
9048 pMedium->m->strLocationFull.c_str(),
9049 i_vdError(vrc).c_str());
9050 }
9051
9052 /* ensure the target directory exists */
9053 if (capabilities & MediumFormatCapabilities_File)
9054 {
9055 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9056 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9057 if (FAILED(rc))
9058 throw rc;
9059 }
9060
9061 vrc = VDCreateDiff(hdd,
9062 targetFormat.c_str(),
9063 targetLocation.c_str(),
9064 (task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX))
9065 | VD_IMAGE_FLAGS_DIFF,
9066 NULL,
9067 targetId.raw(),
9068 id.raw(),
9069 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9070 pTarget->m->vdImageIfaces,
9071 task.mVDOperationIfaces);
9072 if (RT_FAILURE(vrc))
9073 {
9074 if (vrc == VERR_VD_INVALID_TYPE)
9075 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9076 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
9077 targetLocation.c_str(), i_vdError(vrc).c_str());
9078 else
9079 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9080 tr("Could not create the differencing medium storage unit '%s'%s"),
9081 targetLocation.c_str(), i_vdError(vrc).c_str());
9082 }
9083
9084 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9085 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9086 unsigned uImageFlags;
9087 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9088 if (RT_SUCCESS(vrc))
9089 variant = (MediumVariant_T)uImageFlags;
9090 }
9091 catch (HRESULT aRC) { rcTmp = aRC; }
9092
9093 VDDestroy(hdd);
9094 }
9095 catch (HRESULT aRC) { rcTmp = aRC; }
9096
9097 MultiResult mrc(rcTmp);
9098
9099 if (SUCCEEDED(mrc))
9100 {
9101 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9102
9103 Assert(pTarget->m->pParent.isNull());
9104
9105 /* associate child with the parent, maximum depth was checked above */
9106 pTarget->i_setParent(this);
9107
9108 /* diffs for immutable media are auto-reset by default */
9109 bool fAutoReset;
9110 {
9111 ComObjPtr<Medium> pBase = i_getBase();
9112 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
9113 fAutoReset = (pBase->m->type == MediumType_Immutable);
9114 }
9115 {
9116 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
9117 pTarget->m->autoReset = fAutoReset;
9118 }
9119
9120 /* register with mVirtualBox as the last step and move to
9121 * Created state only on success (leaving an orphan file is
9122 * better than breaking media registry consistency) */
9123 ComObjPtr<Medium> pMedium;
9124 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
9125 Assert(pTarget == pMedium);
9126
9127 if (FAILED(mrc))
9128 /* break the parent association on failure to register */
9129 i_deparent();
9130 }
9131
9132 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
9133
9134 if (SUCCEEDED(mrc))
9135 {
9136 pTarget->m->state = MediumState_Created;
9137
9138 pTarget->m->size = size;
9139 pTarget->m->logicalSize = logicalSize;
9140 pTarget->m->variant = variant;
9141 }
9142 else
9143 {
9144 /* back to NotCreated on failure */
9145 pTarget->m->state = MediumState_NotCreated;
9146
9147 pTarget->m->autoReset = false;
9148
9149 /* reset UUID to prevent it from being reused next time */
9150 if (fGenerateUuid)
9151 unconst(pTarget->m->id).clear();
9152 }
9153
9154 // deregister the task registered in createDiffStorage()
9155 Assert(m->numCreateDiffTasks != 0);
9156 --m->numCreateDiffTasks;
9157
9158 mediaLock.release();
9159 i_markRegistriesModified();
9160 if (task.isAsync())
9161 {
9162 // in asynchronous mode, save settings now
9163 m->pVirtualBox->i_saveModifiedRegistries();
9164 }
9165
9166 /* Note that in sync mode, it's the caller's responsibility to
9167 * unlock the medium. */
9168
9169 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
9170 {
9171 m->pVirtualBox->i_onMediumConfigChanged(this);
9172 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
9173 }
9174
9175 return mrc;
9176}
9177
9178/**
9179 * Implementation code for the "merge" task.
9180 *
9181 * This task always gets started from Medium::mergeTo() and can run
9182 * synchronously or asynchronously depending on the "wait" parameter passed to
9183 * that function. If we run synchronously, the caller expects the medium
9184 * registry modification to be set before returning; otherwise (in asynchronous
9185 * mode), we save the settings ourselves.
9186 *
9187 * @param task
9188 * @return
9189 */
9190HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
9191{
9192 /** @todo r=klaus The code below needs to be double checked with regard
9193 * to lock order violations, it probably causes lock order issues related
9194 * to the AutoCaller usage. */
9195 HRESULT rcTmp = S_OK;
9196
9197 const ComObjPtr<Medium> &pTarget = task.mTarget;
9198
9199 try
9200 {
9201 if (!task.mParentForTarget.isNull())
9202 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9203 {
9204 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
9205 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9206 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9207 task.mParentForTarget->m->strLocationFull.c_str());
9208 }
9209
9210 // Resize target to source size, if possible. Otherwise throw an error.
9211 // It's offline resizing. Online resizing will be called in the
9212 // SessionMachine::onlineMergeMedium.
9213
9214 uint64_t sourceSize = 0;
9215 Utf8Str sourceName;
9216 {
9217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9218 sourceSize = i_getLogicalSize();
9219 sourceName = i_getName();
9220 }
9221 uint64_t targetSize = 0;
9222 Utf8Str targetName;
9223 {
9224 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
9225 targetSize = pTarget->i_getLogicalSize();
9226 targetName = pTarget->i_getName();
9227 }
9228
9229 //reducing vm disks are not implemented yet
9230 if (sourceSize > targetSize)
9231 {
9232 if (i_isMediumFormatFile())
9233 {
9234 // Have to make own lock list, because "resize" method resizes only last image
9235 // in the lock chain. The lock chain already in the task.mpMediumLockList, so
9236 // just make new lock list based on it. In fact the own lock list neither makes
9237 // double locking of mediums nor unlocks them during delete, because medium
9238 // already locked by task.mpMediumLockList and own list is used just to specify
9239 // what "resize" method should resize.
9240
9241 MediumLockList* pMediumLockListForResize = new MediumLockList();
9242
9243 for (MediumLockList::Base::iterator it = task.mpMediumLockList->GetBegin();
9244 it != task.mpMediumLockList->GetEnd();
9245 ++it)
9246 {
9247 ComObjPtr<Medium> pMedium = it->GetMedium();
9248 pMediumLockListForResize->Append(pMedium, pMedium->m->state == MediumState_LockedWrite);
9249 if (pMedium == pTarget)
9250 break;
9251 }
9252
9253 // just to switch internal state of the lock list to avoid errors during list deletion,
9254 // because all meduims in the list already locked by task.mpMediumLockList
9255 HRESULT rc = pMediumLockListForResize->Lock(true /* fSkipOverLockedMedia */);
9256 if (FAILED(rc))
9257 {
9258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9259 rc = setError(rc,
9260 tr("Failed to lock the medium '%s' to resize before merge"),
9261 targetName.c_str());
9262 delete pMediumLockListForResize;
9263 throw rc;
9264 }
9265
9266 ComObjPtr<Progress> pProgress(task.GetProgressObject());
9267 rc = pTarget->i_resize(sourceSize, pMediumLockListForResize, &pProgress, true, false);
9268 if (FAILED(rc))
9269 {
9270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9271 rc = setError(rc,
9272 tr("Failed to set size of '%s' to size of '%s'"),
9273 targetName.c_str(), sourceName.c_str());
9274 delete pMediumLockListForResize;
9275 throw rc;
9276 }
9277 delete pMediumLockListForResize;
9278 }
9279 else
9280 {
9281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9282 HRESULT rc = setError(VBOX_E_NOT_SUPPORTED,
9283 tr("Sizes of '%s' and '%s' are different and medium format does not support resing"),
9284 sourceName.c_str(), targetName.c_str());
9285 throw rc;
9286 }
9287 }
9288
9289 task.GetProgressObject()->SetNextOperation(BstrFmt(tr("Merging medium '%s' to '%s'"),
9290 i_getName().c_str(),
9291 targetName.c_str()).raw(),
9292 1);
9293
9294 PVDISK hdd;
9295 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9296 ComAssertRCThrow(vrc, E_FAIL);
9297
9298 try
9299 {
9300 // Similar code appears in SessionMachine::onlineMergeMedium, so
9301 // if you make any changes below check whether they are applicable
9302 // in that context as well.
9303
9304 unsigned uTargetIdx = VD_LAST_IMAGE;
9305 unsigned uSourceIdx = VD_LAST_IMAGE;
9306 /* Open all media in the chain. */
9307 MediumLockList::Base::iterator lockListBegin =
9308 task.mpMediumLockList->GetBegin();
9309 MediumLockList::Base::iterator lockListEnd =
9310 task.mpMediumLockList->GetEnd();
9311 unsigned i = 0;
9312 for (MediumLockList::Base::iterator it = lockListBegin;
9313 it != lockListEnd;
9314 ++it)
9315 {
9316 MediumLock &mediumLock = *it;
9317 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9318
9319 if (pMedium == this)
9320 uSourceIdx = i;
9321 else if (pMedium == pTarget)
9322 uTargetIdx = i;
9323
9324 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9325
9326 /*
9327 * complex sanity (sane complexity)
9328 *
9329 * The current medium must be in the Deleting (medium is merged)
9330 * or LockedRead (parent medium) state if it is not the target.
9331 * If it is the target it must be in the LockedWrite state.
9332 */
9333 Assert( ( pMedium != pTarget
9334 && ( pMedium->m->state == MediumState_Deleting
9335 || pMedium->m->state == MediumState_LockedRead))
9336 || ( pMedium == pTarget
9337 && pMedium->m->state == MediumState_LockedWrite));
9338 /*
9339 * Medium must be the target, in the LockedRead state
9340 * or Deleting state where it is not allowed to be attached
9341 * to a virtual machine.
9342 */
9343 Assert( pMedium == pTarget
9344 || pMedium->m->state == MediumState_LockedRead
9345 || ( pMedium->m->backRefs.size() == 0
9346 && pMedium->m->state == MediumState_Deleting));
9347 /* The source medium must be in Deleting state. */
9348 Assert( pMedium != this
9349 || pMedium->m->state == MediumState_Deleting);
9350
9351 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9352
9353 if ( pMedium->m->state == MediumState_LockedRead
9354 || pMedium->m->state == MediumState_Deleting)
9355 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9356 if (pMedium->m->type == MediumType_Shareable)
9357 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9358
9359 /* Open the medium */
9360 vrc = VDOpen(hdd,
9361 pMedium->m->strFormat.c_str(),
9362 pMedium->m->strLocationFull.c_str(),
9363 uOpenFlags | m->uOpenFlagsDef,
9364 pMedium->m->vdImageIfaces);
9365 if (RT_FAILURE(vrc))
9366 throw vrc;
9367
9368 i++;
9369 }
9370
9371 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
9372 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
9373
9374 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
9375 task.mVDOperationIfaces);
9376 if (RT_FAILURE(vrc))
9377 throw vrc;
9378
9379 /* update parent UUIDs */
9380 if (!task.mfMergeForward)
9381 {
9382 /* we need to update UUIDs of all source's children
9383 * which cannot be part of the container at once so
9384 * add each one in there individually */
9385 if (task.mpChildrenToReparent)
9386 {
9387 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9388 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9389 for (MediumLockList::Base::iterator it = childrenBegin;
9390 it != childrenEnd;
9391 ++it)
9392 {
9393 Medium *pMedium = it->GetMedium();
9394 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
9395 vrc = VDOpen(hdd,
9396 pMedium->m->strFormat.c_str(),
9397 pMedium->m->strLocationFull.c_str(),
9398 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9399 pMedium->m->vdImageIfaces);
9400 if (RT_FAILURE(vrc))
9401 throw vrc;
9402
9403 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
9404 pTarget->m->id.raw());
9405 if (RT_FAILURE(vrc))
9406 throw vrc;
9407
9408 vrc = VDClose(hdd, false /* fDelete */);
9409 if (RT_FAILURE(vrc))
9410 throw vrc;
9411 }
9412 }
9413 }
9414 }
9415 catch (HRESULT aRC) { rcTmp = aRC; }
9416 catch (int aVRC)
9417 {
9418 rcTmp = setErrorBoth(VBOX_E_FILE_ERROR, aVRC,
9419 tr("Could not merge the medium '%s' to '%s'%s"),
9420 m->strLocationFull.c_str(),
9421 pTarget->m->strLocationFull.c_str(),
9422 i_vdError(aVRC).c_str());
9423 }
9424
9425 VDDestroy(hdd);
9426 }
9427 catch (HRESULT aRC) { rcTmp = aRC; }
9428
9429 ErrorInfoKeeper eik;
9430 MultiResult mrc(rcTmp);
9431 HRESULT rc2;
9432
9433 std::set<ComObjPtr<Medium> > pMediumsForNotify;
9434 std::map<Guid, DeviceType_T> uIdsForNotify;
9435
9436 if (SUCCEEDED(mrc))
9437 {
9438 /* all media but the target were successfully deleted by
9439 * VDMerge; reparent the last one and uninitialize deleted media. */
9440
9441 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9442
9443 if (task.mfMergeForward)
9444 {
9445 /* first, unregister the target since it may become a base
9446 * medium which needs re-registration */
9447 rc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
9448 AssertComRC(rc2);
9449
9450 /* then, reparent it and disconnect the deleted branch at both ends
9451 * (chain->parent() is source's parent). Depth check above. */
9452 pTarget->i_deparent();
9453 pTarget->i_setParent(task.mParentForTarget);
9454 if (task.mParentForTarget)
9455 {
9456 i_deparent();
9457 if (task.NotifyAboutChanges())
9458 pMediumsForNotify.insert(task.mParentForTarget);
9459 }
9460
9461 /* then, register again */
9462 ComObjPtr<Medium> pMedium;
9463 rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9464 treeLock);
9465 AssertComRC(rc2);
9466 }
9467 else
9468 {
9469 Assert(pTarget->i_getChildren().size() == 1);
9470 Medium *targetChild = pTarget->i_getChildren().front();
9471
9472 /* disconnect the deleted branch at the elder end */
9473 targetChild->i_deparent();
9474
9475 /* reparent source's children and disconnect the deleted
9476 * branch at the younger end */
9477 if (task.mpChildrenToReparent)
9478 {
9479 /* obey {parent,child} lock order */
9480 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
9481
9482 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9483 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9484 for (MediumLockList::Base::iterator it = childrenBegin;
9485 it != childrenEnd;
9486 ++it)
9487 {
9488 Medium *pMedium = it->GetMedium();
9489 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
9490
9491 pMedium->i_deparent(); // removes pMedium from source
9492 // no depth check, reduces depth
9493 pMedium->i_setParent(pTarget);
9494
9495 if (task.NotifyAboutChanges())
9496 pMediumsForNotify.insert(pMedium);
9497 }
9498 }
9499 pMediumsForNotify.insert(pTarget);
9500 }
9501
9502 /* unregister and uninitialize all media removed by the merge */
9503 MediumLockList::Base::iterator lockListBegin =
9504 task.mpMediumLockList->GetBegin();
9505 MediumLockList::Base::iterator lockListEnd =
9506 task.mpMediumLockList->GetEnd();
9507 for (MediumLockList::Base::iterator it = lockListBegin;
9508 it != lockListEnd;
9509 )
9510 {
9511 MediumLock &mediumLock = *it;
9512 /* Create a real copy of the medium pointer, as the medium
9513 * lock deletion below would invalidate the referenced object. */
9514 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
9515
9516 /* The target and all media not merged (readonly) are skipped */
9517 if ( pMedium == pTarget
9518 || pMedium->m->state == MediumState_LockedRead)
9519 {
9520 ++it;
9521 continue;
9522 }
9523
9524 uIdsForNotify[pMedium->i_getId()] = pMedium->i_getDeviceType();
9525 rc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
9526 AssertComRC(rc2);
9527
9528 /* now, uninitialize the deleted medium (note that
9529 * due to the Deleting state, uninit() will not touch
9530 * the parent-child relationship so we need to
9531 * uninitialize each disk individually) */
9532
9533 /* note that the operation initiator medium (which is
9534 * normally also the source medium) is a special case
9535 * -- there is one more caller added by Task to it which
9536 * we must release. Also, if we are in sync mode, the
9537 * caller may still hold an AutoCaller instance for it
9538 * and therefore we cannot uninit() it (it's therefore
9539 * the caller's responsibility) */
9540 if (pMedium == this)
9541 {
9542 Assert(i_getChildren().size() == 0);
9543 Assert(m->backRefs.size() == 0);
9544 task.mMediumCaller.release();
9545 }
9546
9547 /* Delete the medium lock list entry, which also releases the
9548 * caller added by MergeChain before uninit() and updates the
9549 * iterator to point to the right place. */
9550 rc2 = task.mpMediumLockList->RemoveByIterator(it);
9551 AssertComRC(rc2);
9552
9553 if (task.isAsync() || pMedium != this)
9554 {
9555 treeLock.release();
9556 pMedium->uninit();
9557 treeLock.acquire();
9558 }
9559 }
9560 }
9561
9562 i_markRegistriesModified();
9563 if (task.isAsync())
9564 {
9565 // in asynchronous mode, save settings now
9566 eik.restore();
9567 m->pVirtualBox->i_saveModifiedRegistries();
9568 eik.fetch();
9569 }
9570
9571 if (FAILED(mrc))
9572 {
9573 /* Here we come if either VDMerge() failed (in which case we
9574 * assume that it tried to do everything to make a further
9575 * retry possible -- e.g. not deleted intermediate media
9576 * and so on) or VirtualBox::saveRegistries() failed (where we
9577 * should have the original tree but with intermediate storage
9578 * units deleted by VDMerge()). We have to only restore states
9579 * (through the MergeChain dtor) unless we are run synchronously
9580 * in which case it's the responsibility of the caller as stated
9581 * in the mergeTo() docs. The latter also implies that we
9582 * don't own the merge chain, so release it in this case. */
9583 if (task.isAsync())
9584 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
9585 }
9586 else if (task.NotifyAboutChanges())
9587 {
9588 for (std::set<ComObjPtr<Medium> >::const_iterator it = pMediumsForNotify.begin();
9589 it != pMediumsForNotify.end();
9590 ++it)
9591 {
9592 if (it->isNotNull())
9593 m->pVirtualBox->i_onMediumConfigChanged(*it);
9594 }
9595 for (std::map<Guid, DeviceType_T>::const_iterator it = uIdsForNotify.begin();
9596 it != uIdsForNotify.end();
9597 ++it)
9598 {
9599 m->pVirtualBox->i_onMediumRegistered(it->first, it->second, FALSE);
9600 }
9601 }
9602
9603 return mrc;
9604}
9605
9606/**
9607 * Implementation code for the "clone" task.
9608 *
9609 * This only gets started from Medium::CloneTo() and always runs asynchronously.
9610 * As a result, we always save the VirtualBox.xml file when we're done here.
9611 *
9612 * @param task
9613 * @return
9614 */
9615HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
9616{
9617 /** @todo r=klaus The code below needs to be double checked with regard
9618 * to lock order violations, it probably causes lock order issues related
9619 * to the AutoCaller usage. */
9620 HRESULT rcTmp = S_OK;
9621
9622 const ComObjPtr<Medium> &pTarget = task.mTarget;
9623 const ComObjPtr<Medium> &pParent = task.mParent;
9624
9625 bool fCreatingTarget = false;
9626
9627 uint64_t size = 0, logicalSize = 0;
9628 MediumVariant_T variant = MediumVariant_Standard;
9629 bool fGenerateUuid = false;
9630
9631 try
9632 {
9633 if (!pParent.isNull())
9634 {
9635
9636 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9637 {
9638 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
9639 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9640 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9641 pParent->m->strLocationFull.c_str());
9642 }
9643 }
9644
9645 /* Lock all in {parent,child} order. The lock is also used as a
9646 * signal from the task initiator (which releases it only after
9647 * RTThreadCreate()) that we can start the job. */
9648 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
9649
9650 fCreatingTarget = pTarget->m->state == MediumState_Creating;
9651
9652 /* The object may request a specific UUID (through a special form of
9653 * the moveTo() argument). Otherwise we have to generate it */
9654 Guid targetId = pTarget->m->id;
9655
9656 fGenerateUuid = targetId.isZero();
9657 if (fGenerateUuid)
9658 {
9659 targetId.create();
9660 /* VirtualBox::registerMedium() will need UUID */
9661 unconst(pTarget->m->id) = targetId;
9662 }
9663
9664 PVDISK hdd;
9665 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9666 ComAssertRCThrow(vrc, E_FAIL);
9667
9668 try
9669 {
9670 /* Open all media in the source chain. */
9671 MediumLockList::Base::const_iterator sourceListBegin =
9672 task.mpSourceMediumLockList->GetBegin();
9673 MediumLockList::Base::const_iterator sourceListEnd =
9674 task.mpSourceMediumLockList->GetEnd();
9675 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9676 it != sourceListEnd;
9677 ++it)
9678 {
9679 const MediumLock &mediumLock = *it;
9680 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9681 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9682
9683 /* sanity check */
9684 Assert(pMedium->m->state == MediumState_LockedRead);
9685
9686 /** Open all media in read-only mode. */
9687 vrc = VDOpen(hdd,
9688 pMedium->m->strFormat.c_str(),
9689 pMedium->m->strLocationFull.c_str(),
9690 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9691 pMedium->m->vdImageIfaces);
9692 if (RT_FAILURE(vrc))
9693 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9694 tr("Could not open the medium storage unit '%s'%s"),
9695 pMedium->m->strLocationFull.c_str(),
9696 i_vdError(vrc).c_str());
9697 }
9698
9699 Utf8Str targetFormat(pTarget->m->strFormat);
9700 Utf8Str targetLocation(pTarget->m->strLocationFull);
9701 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
9702
9703 Assert( pTarget->m->state == MediumState_Creating
9704 || pTarget->m->state == MediumState_LockedWrite);
9705 Assert(m->state == MediumState_LockedRead);
9706 Assert( pParent.isNull()
9707 || pParent->m->state == MediumState_LockedRead);
9708
9709 /* unlock before the potentially lengthy operation */
9710 thisLock.release();
9711
9712 /* ensure the target directory exists */
9713 if (capabilities & MediumFormatCapabilities_File)
9714 {
9715 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9716 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9717 if (FAILED(rc))
9718 throw rc;
9719 }
9720
9721 PVDISK targetHdd;
9722 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9723 ComAssertRCThrow(vrc, E_FAIL);
9724
9725 try
9726 {
9727 /* Open all media in the target chain. */
9728 MediumLockList::Base::const_iterator targetListBegin =
9729 task.mpTargetMediumLockList->GetBegin();
9730 MediumLockList::Base::const_iterator targetListEnd =
9731 task.mpTargetMediumLockList->GetEnd();
9732 for (MediumLockList::Base::const_iterator it = targetListBegin;
9733 it != targetListEnd;
9734 ++it)
9735 {
9736 const MediumLock &mediumLock = *it;
9737 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9738
9739 /* If the target medium is not created yet there's no
9740 * reason to open it. */
9741 if (pMedium == pTarget && fCreatingTarget)
9742 continue;
9743
9744 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9745
9746 /* sanity check */
9747 Assert( pMedium->m->state == MediumState_LockedRead
9748 || pMedium->m->state == MediumState_LockedWrite);
9749
9750 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9751 if (pMedium->m->state != MediumState_LockedWrite)
9752 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9753 if (pMedium->m->type == MediumType_Shareable)
9754 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9755
9756 /* Open all media in appropriate mode. */
9757 vrc = VDOpen(targetHdd,
9758 pMedium->m->strFormat.c_str(),
9759 pMedium->m->strLocationFull.c_str(),
9760 uOpenFlags | m->uOpenFlagsDef,
9761 pMedium->m->vdImageIfaces);
9762 if (RT_FAILURE(vrc))
9763 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9764 tr("Could not open the medium storage unit '%s'%s"),
9765 pMedium->m->strLocationFull.c_str(),
9766 i_vdError(vrc).c_str());
9767 }
9768
9769 /* target isn't locked, but no changing data is accessed */
9770 if (task.midxSrcImageSame == UINT32_MAX)
9771 {
9772 vrc = VDCopy(hdd,
9773 VD_LAST_IMAGE,
9774 targetHdd,
9775 targetFormat.c_str(),
9776 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9777 false /* fMoveByRename */,
9778 (uint64_t) task.mTargetLogicalSize /* cbSize */,
9779 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
9780 targetId.raw(),
9781 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9782 NULL /* pVDIfsOperation */,
9783 pTarget->m->vdImageIfaces,
9784 task.mVDOperationIfaces);
9785 }
9786 else
9787 {
9788 vrc = VDCopyEx(hdd,
9789 VD_LAST_IMAGE,
9790 targetHdd,
9791 targetFormat.c_str(),
9792 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9793 false /* fMoveByRename */,
9794 (uint64_t) task.mTargetLogicalSize /* cbSize */,
9795 task.midxSrcImageSame,
9796 task.midxDstImageSame,
9797 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
9798 targetId.raw(),
9799 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9800 NULL /* pVDIfsOperation */,
9801 pTarget->m->vdImageIfaces,
9802 task.mVDOperationIfaces);
9803 }
9804 if (RT_FAILURE(vrc))
9805 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9806 tr("Could not create the clone medium '%s'%s"),
9807 targetLocation.c_str(), i_vdError(vrc).c_str());
9808
9809 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
9810 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
9811 unsigned uImageFlags;
9812 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
9813 if (RT_SUCCESS(vrc))
9814 variant = (MediumVariant_T)uImageFlags;
9815 }
9816 catch (HRESULT aRC) { rcTmp = aRC; }
9817
9818 VDDestroy(targetHdd);
9819 }
9820 catch (HRESULT aRC) { rcTmp = aRC; }
9821
9822 VDDestroy(hdd);
9823 }
9824 catch (HRESULT aRC) { rcTmp = aRC; }
9825
9826 ErrorInfoKeeper eik;
9827 MultiResult mrc(rcTmp);
9828
9829 /* Only do the parent changes for newly created media. */
9830 if (SUCCEEDED(mrc) && fCreatingTarget)
9831 {
9832 /* we set m->pParent & children() */
9833 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9834
9835 Assert(pTarget->m->pParent.isNull());
9836
9837 if (pParent)
9838 {
9839 /* Associate the clone with the parent and deassociate
9840 * from VirtualBox. Depth check above. */
9841 pTarget->i_setParent(pParent);
9842
9843 /* register with mVirtualBox as the last step and move to
9844 * Created state only on success (leaving an orphan file is
9845 * better than breaking media registry consistency) */
9846 eik.restore();
9847 ComObjPtr<Medium> pMedium;
9848 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9849 treeLock);
9850 Assert( FAILED(mrc)
9851 || pTarget == pMedium);
9852 eik.fetch();
9853
9854 if (FAILED(mrc))
9855 /* break parent association on failure to register */
9856 pTarget->i_deparent(); // removes target from parent
9857 }
9858 else
9859 {
9860 /* just register */
9861 eik.restore();
9862 ComObjPtr<Medium> pMedium;
9863 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9864 treeLock);
9865 Assert( FAILED(mrc)
9866 || pTarget == pMedium);
9867 eik.fetch();
9868 }
9869 }
9870
9871 if (fCreatingTarget)
9872 {
9873 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
9874
9875 if (SUCCEEDED(mrc))
9876 {
9877 pTarget->m->state = MediumState_Created;
9878
9879 pTarget->m->size = size;
9880 pTarget->m->logicalSize = logicalSize;
9881 pTarget->m->variant = variant;
9882 }
9883 else
9884 {
9885 /* back to NotCreated on failure */
9886 pTarget->m->state = MediumState_NotCreated;
9887
9888 /* reset UUID to prevent it from being reused next time */
9889 if (fGenerateUuid)
9890 unconst(pTarget->m->id).clear();
9891 }
9892 }
9893
9894 /* Copy any filter related settings over to the target. */
9895 if (SUCCEEDED(mrc))
9896 {
9897 /* Copy any filter related settings over. */
9898 ComObjPtr<Medium> pBase = i_getBase();
9899 ComObjPtr<Medium> pTargetBase = pTarget->i_getBase();
9900 std::vector<com::Utf8Str> aFilterPropNames;
9901 std::vector<com::Utf8Str> aFilterPropValues;
9902 mrc = pBase->i_getFilterProperties(aFilterPropNames, aFilterPropValues);
9903 if (SUCCEEDED(mrc))
9904 {
9905 /* Go through the properties and add them to the target medium. */
9906 for (unsigned idx = 0; idx < aFilterPropNames.size(); idx++)
9907 {
9908 mrc = pTargetBase->i_setPropertyDirect(aFilterPropNames[idx], aFilterPropValues[idx]);
9909 if (FAILED(mrc)) break;
9910 }
9911
9912 // now, at the end of this task (always asynchronous), save the settings
9913 if (SUCCEEDED(mrc))
9914 {
9915 // save the settings
9916 i_markRegistriesModified();
9917 /* collect multiple errors */
9918 eik.restore();
9919 m->pVirtualBox->i_saveModifiedRegistries();
9920 eik.fetch();
9921
9922 if (task.NotifyAboutChanges())
9923 {
9924 if (!fCreatingTarget)
9925 {
9926 if (!aFilterPropNames.empty())
9927 m->pVirtualBox->i_onMediumConfigChanged(pTargetBase);
9928 if (pParent)
9929 m->pVirtualBox->i_onMediumConfigChanged(pParent);
9930 }
9931 else
9932 {
9933 m->pVirtualBox->i_onMediumRegistered(pTarget->i_getId(), pTarget->i_getDeviceType(), TRUE);
9934 }
9935 }
9936 }
9937 }
9938 }
9939
9940 /* Everything is explicitly unlocked when the task exits,
9941 * as the task destruction also destroys the source chain. */
9942
9943 /* Make sure the source chain is released early. It could happen
9944 * that we get a deadlock in Appliance::Import when Medium::Close
9945 * is called & the source chain is released at the same time. */
9946 task.mpSourceMediumLockList->Clear();
9947
9948 return mrc;
9949}
9950
9951/**
9952 * Implementation code for the "move" task.
9953 *
9954 * This only gets started from Medium::MoveTo() and always
9955 * runs asynchronously.
9956 *
9957 * @param task
9958 * @return
9959 */
9960HRESULT Medium::i_taskMoveHandler(Medium::MoveTask &task)
9961{
9962 LogFlowFuncEnter();
9963 HRESULT rcOut = S_OK;
9964
9965 /* pTarget is equal "this" in our case */
9966 const ComObjPtr<Medium> &pTarget = task.mMedium;
9967
9968 uint64_t size = 0; NOREF(size);
9969 uint64_t logicalSize = 0; NOREF(logicalSize);
9970 MediumVariant_T variant = MediumVariant_Standard; NOREF(variant);
9971
9972 /*
9973 * it's exactly moving, not cloning
9974 */
9975 if (!i_isMoveOperation(pTarget))
9976 {
9977 HRESULT rc = setError(VBOX_E_FILE_ERROR,
9978 tr("Wrong preconditions for moving the medium %s"),
9979 pTarget->m->strLocationFull.c_str());
9980 LogFlowFunc(("LEAVE: rc=%Rhrc (early)\n", rc));
9981 return rc;
9982 }
9983
9984 try
9985 {
9986 /* Lock all in {parent,child} order. The lock is also used as a
9987 * signal from the task initiator (which releases it only after
9988 * RTThreadCreate()) that we can start the job. */
9989
9990 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9991
9992 PVDISK hdd;
9993 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9994 ComAssertRCThrow(vrc, E_FAIL);
9995
9996 try
9997 {
9998 /* Open all media in the source chain. */
9999 MediumLockList::Base::const_iterator sourceListBegin =
10000 task.mpMediumLockList->GetBegin();
10001 MediumLockList::Base::const_iterator sourceListEnd =
10002 task.mpMediumLockList->GetEnd();
10003 for (MediumLockList::Base::const_iterator it = sourceListBegin;
10004 it != sourceListEnd;
10005 ++it)
10006 {
10007 const MediumLock &mediumLock = *it;
10008 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10009 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10010
10011 /* sanity check */
10012 Assert(pMedium->m->state == MediumState_LockedWrite);
10013
10014 vrc = VDOpen(hdd,
10015 pMedium->m->strFormat.c_str(),
10016 pMedium->m->strLocationFull.c_str(),
10017 VD_OPEN_FLAGS_NORMAL,
10018 pMedium->m->vdImageIfaces);
10019 if (RT_FAILURE(vrc))
10020 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10021 tr("Could not open the medium storage unit '%s'%s"),
10022 pMedium->m->strLocationFull.c_str(),
10023 i_vdError(vrc).c_str());
10024 }
10025
10026 /* we can directly use pTarget->m->"variables" but for better reading we use local copies */
10027 Guid targetId = pTarget->m->id;
10028 Utf8Str targetFormat(pTarget->m->strFormat);
10029 uint64_t targetCapabilities = pTarget->m->formatObj->i_getCapabilities();
10030
10031 /*
10032 * change target location
10033 * m->strNewLocationFull has been set already together with m->fMoveThisMedium in
10034 * i_preparationForMoving()
10035 */
10036 Utf8Str targetLocation = i_getNewLocationForMoving();
10037
10038 /* unlock before the potentially lengthy operation */
10039 thisLock.release();
10040
10041 /* ensure the target directory exists */
10042 if (targetCapabilities & MediumFormatCapabilities_File)
10043 {
10044 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
10045 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
10046 if (FAILED(rc))
10047 throw rc;
10048 }
10049
10050 try
10051 {
10052 vrc = VDCopy(hdd,
10053 VD_LAST_IMAGE,
10054 hdd,
10055 targetFormat.c_str(),
10056 targetLocation.c_str(),
10057 true /* fMoveByRename */,
10058 0 /* cbSize */,
10059 VD_IMAGE_FLAGS_NONE,
10060 targetId.raw(),
10061 VD_OPEN_FLAGS_NORMAL,
10062 NULL /* pVDIfsOperation */,
10063 pTarget->m->vdImageIfaces,
10064 task.mVDOperationIfaces);
10065 if (RT_FAILURE(vrc))
10066 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10067 tr("Could not move medium '%s'%s"),
10068 targetLocation.c_str(), i_vdError(vrc).c_str());
10069 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10070 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10071 unsigned uImageFlags;
10072 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
10073 if (RT_SUCCESS(vrc))
10074 variant = (MediumVariant_T)uImageFlags;
10075
10076 /*
10077 * set current location, because VDCopy\VDCopyEx doesn't do it.
10078 * also reset moving flag
10079 */
10080 i_resetMoveOperationData();
10081 m->strLocationFull = targetLocation;
10082
10083 }
10084 catch (HRESULT aRC) { rcOut = aRC; }
10085
10086 }
10087 catch (HRESULT aRC) { rcOut = aRC; }
10088
10089 VDDestroy(hdd);
10090 }
10091 catch (HRESULT aRC) { rcOut = aRC; }
10092
10093 ErrorInfoKeeper eik;
10094 MultiResult mrc(rcOut);
10095
10096 // now, at the end of this task (always asynchronous), save the settings
10097 if (SUCCEEDED(mrc))
10098 {
10099 // save the settings
10100 i_markRegistriesModified();
10101 /* collect multiple errors */
10102 eik.restore();
10103 m->pVirtualBox->i_saveModifiedRegistries();
10104 eik.fetch();
10105 }
10106
10107 /* Everything is explicitly unlocked when the task exits,
10108 * as the task destruction also destroys the source chain. */
10109
10110 task.mpMediumLockList->Clear();
10111
10112 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
10113 m->pVirtualBox->i_onMediumConfigChanged(this);
10114
10115 LogFlowFunc(("LEAVE: mrc=%Rhrc\n", (HRESULT)mrc));
10116 return mrc;
10117}
10118
10119/**
10120 * Implementation code for the "delete" task.
10121 *
10122 * This task always gets started from Medium::deleteStorage() and can run
10123 * synchronously or asynchronously depending on the "wait" parameter passed to
10124 * that function.
10125 *
10126 * @param task
10127 * @return
10128 */
10129HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
10130{
10131 NOREF(task);
10132 HRESULT rc = S_OK;
10133
10134 try
10135 {
10136 /* The lock is also used as a signal from the task initiator (which
10137 * releases it only after RTThreadCreate()) that we can start the job */
10138 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10139
10140 PVDISK hdd;
10141 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10142 ComAssertRCThrow(vrc, E_FAIL);
10143
10144 Utf8Str format(m->strFormat);
10145 Utf8Str location(m->strLocationFull);
10146
10147 /* unlock before the potentially lengthy operation */
10148 Assert(m->state == MediumState_Deleting);
10149 thisLock.release();
10150
10151 try
10152 {
10153 vrc = VDOpen(hdd,
10154 format.c_str(),
10155 location.c_str(),
10156 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
10157 m->vdImageIfaces);
10158 if (RT_SUCCESS(vrc))
10159 vrc = VDClose(hdd, true /* fDelete */);
10160
10161 if (RT_FAILURE(vrc) && vrc != VERR_FILE_NOT_FOUND)
10162 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10163 tr("Could not delete the medium storage unit '%s'%s"),
10164 location.c_str(), i_vdError(vrc).c_str());
10165
10166 }
10167 catch (HRESULT aRC) { rc = aRC; }
10168
10169 VDDestroy(hdd);
10170 }
10171 catch (HRESULT aRC) { rc = aRC; }
10172
10173 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10174
10175 /* go to the NotCreated state even on failure since the storage
10176 * may have been already partially deleted and cannot be used any
10177 * more. One will be able to manually re-open the storage if really
10178 * needed to re-register it. */
10179 m->state = MediumState_NotCreated;
10180
10181 /* Reset UUID to prevent Create* from reusing it again */
10182 com::Guid uOldId = m->id;
10183 unconst(m->id).clear();
10184
10185 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
10186 {
10187 if (m->pParent.isNotNull())
10188 m->pVirtualBox->i_onMediumConfigChanged(m->pParent);
10189 m->pVirtualBox->i_onMediumRegistered(uOldId, m->devType, FALSE);
10190 }
10191
10192 return rc;
10193}
10194
10195/**
10196 * Implementation code for the "reset" task.
10197 *
10198 * This always gets started asynchronously from Medium::Reset().
10199 *
10200 * @param task
10201 * @return
10202 */
10203HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
10204{
10205 HRESULT rc = S_OK;
10206
10207 uint64_t size = 0, logicalSize = 0;
10208 MediumVariant_T variant = MediumVariant_Standard;
10209
10210 try
10211 {
10212 /* The lock is also used as a signal from the task initiator (which
10213 * releases it only after RTThreadCreate()) that we can start the job */
10214 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10215
10216 /// @todo Below we use a pair of delete/create operations to reset
10217 /// the diff contents but the most efficient way will of course be
10218 /// to add a VDResetDiff() API call
10219
10220 PVDISK hdd;
10221 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10222 ComAssertRCThrow(vrc, E_FAIL);
10223
10224 Guid id = m->id;
10225 Utf8Str format(m->strFormat);
10226 Utf8Str location(m->strLocationFull);
10227
10228 Medium *pParent = m->pParent;
10229 Guid parentId = pParent->m->id;
10230 Utf8Str parentFormat(pParent->m->strFormat);
10231 Utf8Str parentLocation(pParent->m->strLocationFull);
10232
10233 Assert(m->state == MediumState_LockedWrite);
10234
10235 /* unlock before the potentially lengthy operation */
10236 thisLock.release();
10237
10238 try
10239 {
10240 /* Open all media in the target chain but the last. */
10241 MediumLockList::Base::const_iterator targetListBegin =
10242 task.mpMediumLockList->GetBegin();
10243 MediumLockList::Base::const_iterator targetListEnd =
10244 task.mpMediumLockList->GetEnd();
10245 for (MediumLockList::Base::const_iterator it = targetListBegin;
10246 it != targetListEnd;
10247 ++it)
10248 {
10249 const MediumLock &mediumLock = *it;
10250 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10251
10252 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10253
10254 /* sanity check, "this" is checked above */
10255 Assert( pMedium == this
10256 || pMedium->m->state == MediumState_LockedRead);
10257
10258 /* Open all media in appropriate mode. */
10259 vrc = VDOpen(hdd,
10260 pMedium->m->strFormat.c_str(),
10261 pMedium->m->strLocationFull.c_str(),
10262 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
10263 pMedium->m->vdImageIfaces);
10264 if (RT_FAILURE(vrc))
10265 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10266 tr("Could not open the medium storage unit '%s'%s"),
10267 pMedium->m->strLocationFull.c_str(),
10268 i_vdError(vrc).c_str());
10269
10270 /* Done when we hit the media which should be reset */
10271 if (pMedium == this)
10272 break;
10273 }
10274
10275 /* first, delete the storage unit */
10276 vrc = VDClose(hdd, true /* fDelete */);
10277 if (RT_FAILURE(vrc))
10278 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10279 tr("Could not delete the medium storage unit '%s'%s"),
10280 location.c_str(), i_vdError(vrc).c_str());
10281
10282 /* next, create it again */
10283 vrc = VDOpen(hdd,
10284 parentFormat.c_str(),
10285 parentLocation.c_str(),
10286 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
10287 m->vdImageIfaces);
10288 if (RT_FAILURE(vrc))
10289 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10290 tr("Could not open the medium storage unit '%s'%s"),
10291 parentLocation.c_str(), i_vdError(vrc).c_str());
10292
10293 vrc = VDCreateDiff(hdd,
10294 format.c_str(),
10295 location.c_str(),
10296 /// @todo use the same medium variant as before
10297 VD_IMAGE_FLAGS_NONE,
10298 NULL,
10299 id.raw(),
10300 parentId.raw(),
10301 VD_OPEN_FLAGS_NORMAL,
10302 m->vdImageIfaces,
10303 task.mVDOperationIfaces);
10304 if (RT_FAILURE(vrc))
10305 {
10306 if (vrc == VERR_VD_INVALID_TYPE)
10307 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10308 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
10309 location.c_str(), i_vdError(vrc).c_str());
10310 else
10311 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10312 tr("Could not create the differencing medium storage unit '%s'%s"),
10313 location.c_str(), i_vdError(vrc).c_str());
10314 }
10315
10316 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10317 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10318 unsigned uImageFlags;
10319 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
10320 if (RT_SUCCESS(vrc))
10321 variant = (MediumVariant_T)uImageFlags;
10322 }
10323 catch (HRESULT aRC) { rc = aRC; }
10324
10325 VDDestroy(hdd);
10326 }
10327 catch (HRESULT aRC) { rc = aRC; }
10328
10329 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10330
10331 m->size = size;
10332 m->logicalSize = logicalSize;
10333 m->variant = variant;
10334
10335 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
10336 m->pVirtualBox->i_onMediumConfigChanged(this);
10337
10338 /* Everything is explicitly unlocked when the task exits,
10339 * as the task destruction also destroys the media chain. */
10340
10341 return rc;
10342}
10343
10344/**
10345 * Implementation code for the "compact" task.
10346 *
10347 * @param task
10348 * @return
10349 */
10350HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
10351{
10352 HRESULT rc = S_OK;
10353
10354 /* Lock all in {parent,child} order. The lock is also used as a
10355 * signal from the task initiator (which releases it only after
10356 * RTThreadCreate()) that we can start the job. */
10357 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10358
10359 try
10360 {
10361 PVDISK hdd;
10362 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10363 ComAssertRCThrow(vrc, E_FAIL);
10364
10365 try
10366 {
10367 /* Open all media in the chain. */
10368 MediumLockList::Base::const_iterator mediumListBegin =
10369 task.mpMediumLockList->GetBegin();
10370 MediumLockList::Base::const_iterator mediumListEnd =
10371 task.mpMediumLockList->GetEnd();
10372 MediumLockList::Base::const_iterator mediumListLast =
10373 mediumListEnd;
10374 --mediumListLast;
10375 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10376 it != mediumListEnd;
10377 ++it)
10378 {
10379 const MediumLock &mediumLock = *it;
10380 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10381 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10382
10383 /* sanity check */
10384 if (it == mediumListLast)
10385 Assert(pMedium->m->state == MediumState_LockedWrite);
10386 else
10387 Assert(pMedium->m->state == MediumState_LockedRead);
10388
10389 /* Open all media but last in read-only mode. Do not handle
10390 * shareable media, as compaction and sharing are mutually
10391 * exclusive. */
10392 vrc = VDOpen(hdd,
10393 pMedium->m->strFormat.c_str(),
10394 pMedium->m->strLocationFull.c_str(),
10395 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10396 pMedium->m->vdImageIfaces);
10397 if (RT_FAILURE(vrc))
10398 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10399 tr("Could not open the medium storage unit '%s'%s"),
10400 pMedium->m->strLocationFull.c_str(),
10401 i_vdError(vrc).c_str());
10402 }
10403
10404 Assert(m->state == MediumState_LockedWrite);
10405
10406 Utf8Str location(m->strLocationFull);
10407
10408 /* unlock before the potentially lengthy operation */
10409 thisLock.release();
10410
10411 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
10412 if (RT_FAILURE(vrc))
10413 {
10414 if (vrc == VERR_NOT_SUPPORTED)
10415 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10416 tr("Compacting is not yet supported for medium '%s'"),
10417 location.c_str());
10418 else if (vrc == VERR_NOT_IMPLEMENTED)
10419 throw setErrorBoth(E_NOTIMPL, vrc,
10420 tr("Compacting is not implemented, medium '%s'"),
10421 location.c_str());
10422 else
10423 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10424 tr("Could not compact medium '%s'%s"),
10425 location.c_str(),
10426 i_vdError(vrc).c_str());
10427 }
10428 }
10429 catch (HRESULT aRC) { rc = aRC; }
10430
10431 VDDestroy(hdd);
10432 }
10433 catch (HRESULT aRC) { rc = aRC; }
10434
10435 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
10436 m->pVirtualBox->i_onMediumConfigChanged(this);
10437
10438 /* Everything is explicitly unlocked when the task exits,
10439 * as the task destruction also destroys the media chain. */
10440
10441 return rc;
10442}
10443
10444/**
10445 * Implementation code for the "resize" task.
10446 *
10447 * @param task
10448 * @return
10449 */
10450HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
10451{
10452 HRESULT rc = S_OK;
10453
10454 uint64_t size = 0, logicalSize = 0;
10455
10456 try
10457 {
10458 /* The lock is also used as a signal from the task initiator (which
10459 * releases it only after RTThreadCreate()) that we can start the job */
10460 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10461
10462 PVDISK hdd;
10463 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10464 ComAssertRCThrow(vrc, E_FAIL);
10465
10466 try
10467 {
10468 /* Open all media in the chain. */
10469 MediumLockList::Base::const_iterator mediumListBegin =
10470 task.mpMediumLockList->GetBegin();
10471 MediumLockList::Base::const_iterator mediumListEnd =
10472 task.mpMediumLockList->GetEnd();
10473 MediumLockList::Base::const_iterator mediumListLast =
10474 mediumListEnd;
10475 --mediumListLast;
10476 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10477 it != mediumListEnd;
10478 ++it)
10479 {
10480 const MediumLock &mediumLock = *it;
10481 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10482 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10483
10484 /* sanity check */
10485 if (it == mediumListLast)
10486 Assert(pMedium->m->state == MediumState_LockedWrite);
10487 else
10488 Assert(pMedium->m->state == MediumState_LockedRead ||
10489 // Allow resize the target image during mergeTo in case
10490 // of direction from parent to child because all intermediate
10491 // images are marked to MediumState_Deleting and will be
10492 // destroyed after successful merge
10493 pMedium->m->state == MediumState_Deleting);
10494
10495 /* Open all media but last in read-only mode. Do not handle
10496 * shareable media, as compaction and sharing are mutually
10497 * exclusive. */
10498 vrc = VDOpen(hdd,
10499 pMedium->m->strFormat.c_str(),
10500 pMedium->m->strLocationFull.c_str(),
10501 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10502 pMedium->m->vdImageIfaces);
10503 if (RT_FAILURE(vrc))
10504 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10505 tr("Could not open the medium storage unit '%s'%s"),
10506 pMedium->m->strLocationFull.c_str(),
10507 i_vdError(vrc).c_str());
10508 }
10509
10510 Assert(m->state == MediumState_LockedWrite);
10511
10512 Utf8Str location(m->strLocationFull);
10513
10514 /* unlock before the potentially lengthy operation */
10515 thisLock.release();
10516
10517 VDGEOMETRY geo = {0, 0, 0}; /* auto */
10518 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
10519 if (RT_FAILURE(vrc))
10520 {
10521 if (vrc == VERR_VD_SHRINK_NOT_SUPPORTED)
10522 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10523 tr("Shrinking is not yet supported for medium '%s'"),
10524 location.c_str());
10525 if (vrc == VERR_NOT_SUPPORTED)
10526 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10527 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
10528 task.mSize, location.c_str());
10529 else if (vrc == VERR_NOT_IMPLEMENTED)
10530 throw setErrorBoth(E_NOTIMPL, vrc,
10531 tr("Resizing is not implemented, medium '%s'"),
10532 location.c_str());
10533 else
10534 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10535 tr("Could not resize medium '%s'%s"),
10536 location.c_str(),
10537 i_vdError(vrc).c_str());
10538 }
10539 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10540 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10541 }
10542 catch (HRESULT aRC) { rc = aRC; }
10543
10544 VDDestroy(hdd);
10545 }
10546 catch (HRESULT aRC) { rc = aRC; }
10547
10548 if (SUCCEEDED(rc))
10549 {
10550 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10551 m->size = size;
10552 m->logicalSize = logicalSize;
10553
10554 if (task.NotifyAboutChanges())
10555 m->pVirtualBox->i_onMediumConfigChanged(this);
10556 }
10557
10558 /* Everything is explicitly unlocked when the task exits,
10559 * as the task destruction also destroys the media chain. */
10560
10561 return rc;
10562}
10563
10564/**
10565 * Implementation code for the "import" task.
10566 *
10567 * This only gets started from Medium::importFile() and always runs
10568 * asynchronously. It potentially touches the media registry, so we
10569 * always save the VirtualBox.xml file when we're done here.
10570 *
10571 * @param task
10572 * @return
10573 */
10574HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
10575{
10576 /** @todo r=klaus The code below needs to be double checked with regard
10577 * to lock order violations, it probably causes lock order issues related
10578 * to the AutoCaller usage. */
10579 HRESULT rcTmp = S_OK;
10580
10581 const ComObjPtr<Medium> &pParent = task.mParent;
10582
10583 bool fCreatingTarget = false;
10584
10585 uint64_t size = 0, logicalSize = 0;
10586 MediumVariant_T variant = MediumVariant_Standard;
10587 bool fGenerateUuid = false;
10588
10589 try
10590 {
10591 if (!pParent.isNull())
10592 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
10593 {
10594 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
10595 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10596 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
10597 pParent->m->strLocationFull.c_str());
10598 }
10599
10600 /* Lock all in {parent,child} order. The lock is also used as a
10601 * signal from the task initiator (which releases it only after
10602 * RTThreadCreate()) that we can start the job. */
10603 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
10604
10605 fCreatingTarget = m->state == MediumState_Creating;
10606
10607 /* The object may request a specific UUID (through a special form of
10608 * the moveTo() argument). Otherwise we have to generate it */
10609 Guid targetId = m->id;
10610
10611 fGenerateUuid = targetId.isZero();
10612 if (fGenerateUuid)
10613 {
10614 targetId.create();
10615 /* VirtualBox::i_registerMedium() will need UUID */
10616 unconst(m->id) = targetId;
10617 }
10618
10619
10620 PVDISK hdd;
10621 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10622 ComAssertRCThrow(vrc, E_FAIL);
10623
10624 try
10625 {
10626 /* Open source medium. */
10627 vrc = VDOpen(hdd,
10628 task.mFormat->i_getId().c_str(),
10629 task.mFilename.c_str(),
10630 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
10631 task.mVDImageIfaces);
10632 if (RT_FAILURE(vrc))
10633 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10634 tr("Could not open the medium storage unit '%s'%s"),
10635 task.mFilename.c_str(),
10636 i_vdError(vrc).c_str());
10637
10638 Utf8Str targetFormat(m->strFormat);
10639 Utf8Str targetLocation(m->strLocationFull);
10640 uint64_t capabilities = task.mFormat->i_getCapabilities();
10641
10642 Assert( m->state == MediumState_Creating
10643 || m->state == MediumState_LockedWrite);
10644 Assert( pParent.isNull()
10645 || pParent->m->state == MediumState_LockedRead);
10646
10647 /* unlock before the potentially lengthy operation */
10648 thisLock.release();
10649
10650 /* ensure the target directory exists */
10651 if (capabilities & MediumFormatCapabilities_File)
10652 {
10653 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
10654 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
10655 if (FAILED(rc))
10656 throw rc;
10657 }
10658
10659 PVDISK targetHdd;
10660 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
10661 ComAssertRCThrow(vrc, E_FAIL);
10662
10663 try
10664 {
10665 /* Open all media in the target chain. */
10666 MediumLockList::Base::const_iterator targetListBegin =
10667 task.mpTargetMediumLockList->GetBegin();
10668 MediumLockList::Base::const_iterator targetListEnd =
10669 task.mpTargetMediumLockList->GetEnd();
10670 for (MediumLockList::Base::const_iterator it = targetListBegin;
10671 it != targetListEnd;
10672 ++it)
10673 {
10674 const MediumLock &mediumLock = *it;
10675 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10676
10677 /* If the target medium is not created yet there's no
10678 * reason to open it. */
10679 if (pMedium == this && fCreatingTarget)
10680 continue;
10681
10682 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10683
10684 /* sanity check */
10685 Assert( pMedium->m->state == MediumState_LockedRead
10686 || pMedium->m->state == MediumState_LockedWrite);
10687
10688 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
10689 if (pMedium->m->state != MediumState_LockedWrite)
10690 uOpenFlags = VD_OPEN_FLAGS_READONLY;
10691 if (pMedium->m->type == MediumType_Shareable)
10692 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
10693
10694 /* Open all media in appropriate mode. */
10695 vrc = VDOpen(targetHdd,
10696 pMedium->m->strFormat.c_str(),
10697 pMedium->m->strLocationFull.c_str(),
10698 uOpenFlags | m->uOpenFlagsDef,
10699 pMedium->m->vdImageIfaces);
10700 if (RT_FAILURE(vrc))
10701 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10702 tr("Could not open the medium storage unit '%s'%s"),
10703 pMedium->m->strLocationFull.c_str(),
10704 i_vdError(vrc).c_str());
10705 }
10706
10707 vrc = VDCopy(hdd,
10708 VD_LAST_IMAGE,
10709 targetHdd,
10710 targetFormat.c_str(),
10711 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
10712 false /* fMoveByRename */,
10713 0 /* cbSize */,
10714 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
10715 targetId.raw(),
10716 VD_OPEN_FLAGS_NORMAL,
10717 NULL /* pVDIfsOperation */,
10718 m->vdImageIfaces,
10719 task.mVDOperationIfaces);
10720 if (RT_FAILURE(vrc))
10721 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10722 tr("Could not create the imported medium '%s'%s"),
10723 targetLocation.c_str(), i_vdError(vrc).c_str());
10724
10725 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
10726 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
10727 unsigned uImageFlags;
10728 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
10729 if (RT_SUCCESS(vrc))
10730 variant = (MediumVariant_T)uImageFlags;
10731 }
10732 catch (HRESULT aRC) { rcTmp = aRC; }
10733
10734 VDDestroy(targetHdd);
10735 }
10736 catch (HRESULT aRC) { rcTmp = aRC; }
10737
10738 VDDestroy(hdd);
10739 }
10740 catch (HRESULT aRC) { rcTmp = aRC; }
10741
10742 ErrorInfoKeeper eik;
10743 MultiResult mrc(rcTmp);
10744
10745 /* Only do the parent changes for newly created media. */
10746 if (SUCCEEDED(mrc) && fCreatingTarget)
10747 {
10748 /* we set m->pParent & children() */
10749 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10750
10751 Assert(m->pParent.isNull());
10752
10753 if (pParent)
10754 {
10755 /* Associate the imported medium with the parent and deassociate
10756 * from VirtualBox. Depth check above. */
10757 i_setParent(pParent);
10758
10759 /* register with mVirtualBox as the last step and move to
10760 * Created state only on success (leaving an orphan file is
10761 * better than breaking media registry consistency) */
10762 eik.restore();
10763 ComObjPtr<Medium> pMedium;
10764 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
10765 treeLock);
10766 Assert(this == pMedium);
10767 eik.fetch();
10768
10769 if (FAILED(mrc))
10770 /* break parent association on failure to register */
10771 this->i_deparent(); // removes target from parent
10772 }
10773 else
10774 {
10775 /* just register */
10776 eik.restore();
10777 ComObjPtr<Medium> pMedium;
10778 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
10779 Assert(this == pMedium);
10780 eik.fetch();
10781 }
10782 }
10783
10784 if (fCreatingTarget)
10785 {
10786 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
10787
10788 if (SUCCEEDED(mrc))
10789 {
10790 m->state = MediumState_Created;
10791
10792 m->size = size;
10793 m->logicalSize = logicalSize;
10794 m->variant = variant;
10795 }
10796 else
10797 {
10798 /* back to NotCreated on failure */
10799 m->state = MediumState_NotCreated;
10800
10801 /* reset UUID to prevent it from being reused next time */
10802 if (fGenerateUuid)
10803 unconst(m->id).clear();
10804 }
10805 }
10806
10807 // now, at the end of this task (always asynchronous), save the settings
10808 {
10809 // save the settings
10810 i_markRegistriesModified();
10811 /* collect multiple errors */
10812 eik.restore();
10813 m->pVirtualBox->i_saveModifiedRegistries();
10814 eik.fetch();
10815 }
10816
10817 /* Everything is explicitly unlocked when the task exits,
10818 * as the task destruction also destroys the target chain. */
10819
10820 /* Make sure the target chain is released early, otherwise it can
10821 * lead to deadlocks with concurrent IAppliance activities. */
10822 task.mpTargetMediumLockList->Clear();
10823
10824 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
10825 {
10826 if (pParent)
10827 m->pVirtualBox->i_onMediumConfigChanged(pParent);
10828 if (fCreatingTarget)
10829 m->pVirtualBox->i_onMediumConfigChanged(this);
10830 else
10831 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
10832 }
10833
10834 return mrc;
10835}
10836
10837/**
10838 * Sets up the encryption settings for a filter.
10839 */
10840void Medium::i_taskEncryptSettingsSetup(MediumCryptoFilterSettings *pSettings, const char *pszCipher,
10841 const char *pszKeyStore, const char *pszPassword,
10842 bool fCreateKeyStore)
10843{
10844 pSettings->pszCipher = pszCipher;
10845 pSettings->pszPassword = pszPassword;
10846 pSettings->pszKeyStoreLoad = pszKeyStore;
10847 pSettings->fCreateKeyStore = fCreateKeyStore;
10848 pSettings->pbDek = NULL;
10849 pSettings->cbDek = 0;
10850 pSettings->vdFilterIfaces = NULL;
10851
10852 pSettings->vdIfCfg.pfnAreKeysValid = i_vdCryptoConfigAreKeysValid;
10853 pSettings->vdIfCfg.pfnQuerySize = i_vdCryptoConfigQuerySize;
10854 pSettings->vdIfCfg.pfnQuery = i_vdCryptoConfigQuery;
10855 pSettings->vdIfCfg.pfnQueryBytes = NULL;
10856
10857 pSettings->vdIfCrypto.pfnKeyRetain = i_vdCryptoKeyRetain;
10858 pSettings->vdIfCrypto.pfnKeyRelease = i_vdCryptoKeyRelease;
10859 pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = i_vdCryptoKeyStorePasswordRetain;
10860 pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = i_vdCryptoKeyStorePasswordRelease;
10861 pSettings->vdIfCrypto.pfnKeyStoreSave = i_vdCryptoKeyStoreSave;
10862 pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = i_vdCryptoKeyStoreReturnParameters;
10863
10864 int vrc = VDInterfaceAdd(&pSettings->vdIfCfg.Core,
10865 "Medium::vdInterfaceCfgCrypto",
10866 VDINTERFACETYPE_CONFIG, pSettings,
10867 sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces);
10868 AssertRC(vrc);
10869
10870 vrc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core,
10871 "Medium::vdInterfaceCrypto",
10872 VDINTERFACETYPE_CRYPTO, pSettings,
10873 sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces);
10874 AssertRC(vrc);
10875}
10876
10877/**
10878 * Implementation code for the "encrypt" task.
10879 *
10880 * @param task
10881 * @return
10882 */
10883HRESULT Medium::i_taskEncryptHandler(Medium::EncryptTask &task)
10884{
10885# ifndef VBOX_WITH_EXTPACK
10886 RT_NOREF(task);
10887# endif
10888 HRESULT rc = S_OK;
10889
10890 /* Lock all in {parent,child} order. The lock is also used as a
10891 * signal from the task initiator (which releases it only after
10892 * RTThreadCreate()) that we can start the job. */
10893 ComObjPtr<Medium> pBase = i_getBase();
10894 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10895
10896 try
10897 {
10898# ifdef VBOX_WITH_EXTPACK
10899 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
10900 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
10901 {
10902 /* Load the plugin */
10903 Utf8Str strPlugin;
10904 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
10905 if (SUCCEEDED(rc))
10906 {
10907 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
10908 if (RT_FAILURE(vrc))
10909 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10910 tr("Encrypting the image failed because the encryption plugin could not be loaded (%s)"),
10911 i_vdError(vrc).c_str());
10912 }
10913 else
10914 throw setError(VBOX_E_NOT_SUPPORTED,
10915 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
10916 ORACLE_PUEL_EXTPACK_NAME);
10917 }
10918 else
10919 throw setError(VBOX_E_NOT_SUPPORTED,
10920 tr("Encryption is not supported because the extension pack '%s' is missing"),
10921 ORACLE_PUEL_EXTPACK_NAME);
10922
10923 PVDISK pDisk = NULL;
10924 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
10925 ComAssertRCThrow(vrc, E_FAIL);
10926
10927 MediumCryptoFilterSettings CryptoSettingsRead;
10928 MediumCryptoFilterSettings CryptoSettingsWrite;
10929
10930 void *pvBuf = NULL;
10931 const char *pszPasswordNew = NULL;
10932 try
10933 {
10934 /* Set up disk encryption filters. */
10935 if (task.mstrCurrentPassword.isEmpty())
10936 {
10937 /*
10938 * Query whether the medium property indicating that encryption is
10939 * configured is existing.
10940 */
10941 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10942 if (it != pBase->m->mapProperties.end())
10943 throw setError(VBOX_E_PASSWORD_INCORRECT,
10944 tr("The password given for the encrypted image is incorrect"));
10945 }
10946 else
10947 {
10948 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10949 if (it == pBase->m->mapProperties.end())
10950 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10951 tr("The image is not configured for encryption"));
10952
10953 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, it->second.c_str(), task.mstrCurrentPassword.c_str(),
10954 false /* fCreateKeyStore */);
10955 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
10956 if (vrc == VERR_VD_PASSWORD_INCORRECT)
10957 throw setError(VBOX_E_PASSWORD_INCORRECT,
10958 tr("The password to decrypt the image is incorrect"));
10959 else if (RT_FAILURE(vrc))
10960 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10961 tr("Failed to load the decryption filter: %s"),
10962 i_vdError(vrc).c_str());
10963 }
10964
10965 if (task.mstrCipher.isNotEmpty())
10966 {
10967 if ( task.mstrNewPassword.isEmpty()
10968 && task.mstrNewPasswordId.isEmpty()
10969 && task.mstrCurrentPassword.isNotEmpty())
10970 {
10971 /* An empty password and password ID will default to the current password. */
10972 pszPasswordNew = task.mstrCurrentPassword.c_str();
10973 }
10974 else if (task.mstrNewPassword.isEmpty())
10975 throw setError(VBOX_E_OBJECT_NOT_FOUND,
10976 tr("A password must be given for the image encryption"));
10977 else if (task.mstrNewPasswordId.isEmpty())
10978 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10979 tr("A valid identifier for the password must be given"));
10980 else
10981 pszPasswordNew = task.mstrNewPassword.c_str();
10982
10983 i_taskEncryptSettingsSetup(&CryptoSettingsWrite, task.mstrCipher.c_str(), NULL,
10984 pszPasswordNew, true /* fCreateKeyStore */);
10985 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_WRITE, CryptoSettingsWrite.vdFilterIfaces);
10986 if (RT_FAILURE(vrc))
10987 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
10988 tr("Failed to load the encryption filter: %s"),
10989 i_vdError(vrc).c_str());
10990 }
10991 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
10992 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10993 tr("The password and password identifier must be empty if the output should be unencrypted"));
10994
10995 /* Open all media in the chain. */
10996 MediumLockList::Base::const_iterator mediumListBegin =
10997 task.mpMediumLockList->GetBegin();
10998 MediumLockList::Base::const_iterator mediumListEnd =
10999 task.mpMediumLockList->GetEnd();
11000 MediumLockList::Base::const_iterator mediumListLast =
11001 mediumListEnd;
11002 --mediumListLast;
11003 for (MediumLockList::Base::const_iterator it = mediumListBegin;
11004 it != mediumListEnd;
11005 ++it)
11006 {
11007 const MediumLock &mediumLock = *it;
11008 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
11009 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
11010
11011 Assert(pMedium->m->state == MediumState_LockedWrite);
11012
11013 /* Open all media but last in read-only mode. Do not handle
11014 * shareable media, as compaction and sharing are mutually
11015 * exclusive. */
11016 vrc = VDOpen(pDisk,
11017 pMedium->m->strFormat.c_str(),
11018 pMedium->m->strLocationFull.c_str(),
11019 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
11020 pMedium->m->vdImageIfaces);
11021 if (RT_FAILURE(vrc))
11022 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
11023 tr("Could not open the medium storage unit '%s'%s"),
11024 pMedium->m->strLocationFull.c_str(),
11025 i_vdError(vrc).c_str());
11026 }
11027
11028 Assert(m->state == MediumState_LockedWrite);
11029
11030 Utf8Str location(m->strLocationFull);
11031
11032 /* unlock before the potentially lengthy operation */
11033 thisLock.release();
11034
11035 vrc = VDPrepareWithFilters(pDisk, task.mVDOperationIfaces);
11036 if (RT_FAILURE(vrc))
11037 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
11038 tr("Could not prepare disk images for encryption (%Rrc): %s"),
11039 vrc, i_vdError(vrc).c_str());
11040
11041 thisLock.acquire();
11042 /* If everything went well set the new key store. */
11043 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
11044 if (it != pBase->m->mapProperties.end())
11045 pBase->m->mapProperties.erase(it);
11046
11047 /* Delete KeyId if encryption is removed or the password did change. */
11048 if ( task.mstrNewPasswordId.isNotEmpty()
11049 || task.mstrCipher.isEmpty())
11050 {
11051 it = pBase->m->mapProperties.find("CRYPT/KeyId");
11052 if (it != pBase->m->mapProperties.end())
11053 pBase->m->mapProperties.erase(it);
11054 }
11055
11056 if (CryptoSettingsWrite.pszKeyStore)
11057 {
11058 pBase->m->mapProperties["CRYPT/KeyStore"] = Utf8Str(CryptoSettingsWrite.pszKeyStore);
11059 if (task.mstrNewPasswordId.isNotEmpty())
11060 pBase->m->mapProperties["CRYPT/KeyId"] = task.mstrNewPasswordId;
11061 }
11062
11063 if (CryptoSettingsRead.pszCipherReturned)
11064 RTStrFree(CryptoSettingsRead.pszCipherReturned);
11065
11066 if (CryptoSettingsWrite.pszCipherReturned)
11067 RTStrFree(CryptoSettingsWrite.pszCipherReturned);
11068
11069 thisLock.release();
11070 pBase->i_markRegistriesModified();
11071 m->pVirtualBox->i_saveModifiedRegistries();
11072 }
11073 catch (HRESULT aRC) { rc = aRC; }
11074
11075 if (pvBuf)
11076 RTMemFree(pvBuf);
11077
11078 VDDestroy(pDisk);
11079# else
11080 throw setError(VBOX_E_NOT_SUPPORTED,
11081 tr("Encryption is not supported because extension pack support is not built in"));
11082# endif
11083 }
11084 catch (HRESULT aRC) { rc = aRC; }
11085
11086 /* Everything is explicitly unlocked when the task exits,
11087 * as the task destruction also destroys the media chain. */
11088
11089 return rc;
11090}
11091
11092/* vi: set tabstop=4 shiftwidth=4 expandtab: */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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