VirtualBox

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

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

Main/Machine+Medium+Snapshot+VirtualBox: Recursion elimination to save stack space. Caused trouble with the current settings limits already in ASAN builds, now much higher limits would be possible, but that's not urgent. Also fix the behavior of forgetting medium objects when unregistering VMs (was previously not doing what the API documentation said in the CleanupMode_UnregisterOnly case). bugref:7717

Settings.cpp: Recursion elimination and make the handling of settings reading and writing more similar.

ValidationKit/tests/api/tdTreeDepth1.py: Improve testcase. Make sure that VM unregistering does the right thing with the associated medium objects.

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