VirtualBox

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

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

bugref:8344. Fixed adding a wrong extension for HDD medium type.

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

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