VirtualBox

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

Last change on this file since 55567 was 55285, checked in by vboxsync, 10 years ago

Main/Medium: Preserve any errors when deleting the medium locks list

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

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