VirtualBox

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

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

Main,FE/VBoxManage: Support exporting machines as appliances which have encrypted disks. Because the OVF standard doesn't support encrypted disks so far we always decrypt exported images which requires the password before starting the export proess

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

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