VirtualBox

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

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

Main/Medium: Fix the refcount updating behavior of Medium::uninit. It must not increase the refcount of "this" since this could cause double freeing since the uninit call might have been triggered by the refcount reaching 0. bugref:7717

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