VirtualBox

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

Last change on this file since 79812 was 79793, checked in by vboxsync, 5 years ago

Main/Medium: Fix for setting create-only medium properties when querying info. Related to bugref:5899.

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

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