VirtualBox

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

Last change on this file since 95271 was 95117, checked in by vboxsync, 3 years ago

Main/MediumImpl: std::unary_function was removed in c++17 and VC++ really does not have it in that mode, so #if our way around the two uses here (c++17).

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

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