VirtualBox

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

Last change on this file since 68155 was 68043, checked in by vboxsync, 7 years ago

Main/Medium: fix lock order violation when setting the medium description

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