VirtualBox

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

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

Main/Medium: Fix leftover from recursion elimination, should not return but continue processing the stack content. bugref:7717

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

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