1
/* $Id: MediumImpl.cpp 35553 2011-01-14 00:02:56Z vboxsync $ */
3
* VirtualBox COM class implementation
7
* Copyright (C) 2008-2010 Oracle Corporation
9
* This file is part of VirtualBox Open Source Edition (OSE), as
10
* available from http://www.virtualbox.org. This file is free software;
11
* you can redistribute it and/or modify it under the terms of the GNU
12
* General Public License (GPL) as published by the Free Software
13
* Foundation, in version 2 as it comes in the "COPYING" file of the
14
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18
#include "MediumImpl.h"
19
#include "ProgressImpl.h"
20
#include "SystemPropertiesImpl.h"
21
#include "VirtualBoxImpl.h"
23
#include "AutoCaller.h"
26
#include <VBox/com/array.h>
27
#include "VBox/com/MultiResult.h"
28
#include "VBox/com/ErrorInfo.h"
31
#include <VBox/settings.h>
33
#include <iprt/param.h>
34
#include <iprt/path.h>
35
#include <iprt/file.h>
37
#include <iprt/cpp/utils.h>
43
////////////////////////////////////////////////////////////////////////////////
45
// Medium data definition
47
////////////////////////////////////////////////////////////////////////////////
49
/** Describes how a machine refers to this medium. */
52
/** Equality predicate for stdc++. */
53
struct EqualsTo : public std::unary_function <BackRef, bool>
55
explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
57
bool operator()(const argument_type &aThat) const
59
return aThat.machineId == machineId;
65
BackRef(const Guid &aMachineId,
66
const Guid &aSnapshotId = Guid::Empty)
67
: machineId(aMachineId),
68
fInCurState(aSnapshotId.isEmpty())
70
if (!aSnapshotId.isEmpty())
71
llSnapshotIds.push_back(aSnapshotId);
76
GuidList llSnapshotIds;
79
typedef std::list<BackRef> BackRefList;
85
state(MediumState_NotCreated),
86
variant(MediumVariant_Standard),
89
preLockState(MediumState_NotCreated),
90
queryInfoSem(NIL_RTSEMEVENTMULTI),
91
queryInfoRunning(false),
92
type(MediumType_Normal),
93
devType(DeviceType_HardDisk),
95
hddOpenMode(OpenReadWrite),
99
numCreateDiffTasks(0),
104
/** weak VirtualBox parent */
105
VirtualBox * const pVirtualBox;
107
// pParent and llChildren are protected by VirtualBox::getMediaTreeLockHandle()
108
ComObjPtr<Medium> pParent;
109
MediaList llChildren; // to add a child, just call push_back; to remove a child, call child->deparent() which does a lookup
111
GuidList llRegistryIDs; // media registries in which this medium is listed
114
Utf8Str strDescription;
116
MediumVariant_T variant;
117
Utf8Str strLocationFull;
119
Utf8Str strLastAccessError;
121
BackRefList backRefs;
124
MediumState_T preLockState;
126
RTSEMEVENTMULTI queryInfoSem;
127
bool queryInfoRunning : 1;
129
const Utf8Str strFormat;
130
ComObjPtr<MediumFormat> formatObj;
133
DeviceType_T devType;
134
uint64_t logicalSize;
136
HDDOpenMode hddOpenMode;
140
const Guid uuidImage;
141
const Guid uuidParentImage;
145
settings::StringsMap mapProperties;
149
uint32_t numCreateDiffTasks;
151
Utf8Str vdError; /*< Error remembered by the VD error callback. */
153
VDINTERFACE vdIfError;
154
VDINTERFACEERROR vdIfCallsError;
156
VDINTERFACE vdIfConfig;
157
VDINTERFACECONFIG vdIfCallsConfig;
159
VDINTERFACE vdIfTcpNet;
160
VDINTERFACETCPNET vdIfCallsTcpNet;
162
PVDINTERFACE vdDiskIfaces;
163
PVDINTERFACE vdImageIfaces;
166
typedef struct VDSOCKETINT
168
/** Socket handle. */
170
} VDSOCKETINT, *PVDSOCKETINT;
172
////////////////////////////////////////////////////////////////////////////////
176
////////////////////////////////////////////////////////////////////////////////
179
* Medium::Task class for asynchronous operations.
181
* @note Instances of this class must be created using new() because the
182
* task thread function will delete them when the task is complete.
184
* @note The constructor of this class adds a caller on the managed Medium
185
* object which is automatically released upon destruction.
190
Task(Medium *aMedium, Progress *aProgress)
191
: mVDOperationIfaces(NULL),
192
m_pllRegistriesThatNeedSaving(NULL),
194
mMediumCaller(aMedium),
195
mThread(NIL_RTTHREAD),
196
mProgress(aProgress),
197
mVirtualBoxCaller(NULL)
199
AssertReturnVoidStmt(aMedium, mRC = E_FAIL);
200
mRC = mMediumCaller.rc();
204
/* Get strong VirtualBox reference, see below. */
205
VirtualBox *pVirtualBox = aMedium->m->pVirtualBox;
206
mVirtualBox = pVirtualBox;
207
mVirtualBoxCaller.attach(pVirtualBox);
208
mRC = mVirtualBoxCaller.rc();
212
/* Set up a per-operation progress interface, can be used freely (for
213
* binary operations you can use it either on the source or target). */
214
mVDIfCallsProgress.cbSize = sizeof(VDINTERFACEPROGRESS);
215
mVDIfCallsProgress.enmInterface = VDINTERFACETYPE_PROGRESS;
216
mVDIfCallsProgress.pfnProgress = vdProgressCall;
217
int vrc = VDInterfaceAdd(&mVDIfProgress,
218
"Medium::Task::vdInterfaceProgress",
219
VDINTERFACETYPE_PROGRESS,
222
&mVDOperationIfaces);
228
// Make all destructors virtual. Just in case.
232
HRESULT rc() const { return mRC; }
233
bool isOk() const { return SUCCEEDED(rc()); }
235
static int fntMediumTask(RTTHREAD aThread, void *pvUser);
237
bool isAsync() { return mThread != NIL_RTTHREAD; }
239
PVDINTERFACE mVDOperationIfaces;
241
// Whether the caller needs to call VirtualBox::saveSettings() after
242
// the task function returns. Only used in synchronous (wait) mode;
243
// otherwise the task will save the settings itself.
244
GuidList *m_pllRegistriesThatNeedSaving;
246
const ComObjPtr<Medium> mMedium;
247
AutoCaller mMediumCaller;
249
friend HRESULT Medium::runNow(Medium::Task*, GuidList *);
256
virtual HRESULT handler() = 0;
258
const ComObjPtr<Progress> mProgress;
260
static DECLCALLBACK(int) vdProgressCall(void *pvUser, unsigned uPercent);
262
VDINTERFACE mVDIfProgress;
263
VDINTERFACEPROGRESS mVDIfCallsProgress;
265
/* Must have a strong VirtualBox reference during a task otherwise the
266
* reference count might drop to 0 while a task is still running. This
267
* would result in weird behavior, including deadlocks due to uninit and
268
* locking order issues. The deadlock often is not detectable because the
269
* uninit uses event semaphores which sabotages deadlock detection. */
270
ComObjPtr<VirtualBox> mVirtualBox;
271
AutoCaller mVirtualBoxCaller;
274
class Medium::CreateBaseTask : public Medium::Task
277
CreateBaseTask(Medium *aMedium,
280
MediumVariant_T aVariant)
281
: Medium::Task(aMedium, aProgress),
287
MediumVariant_T mVariant;
290
virtual HRESULT handler();
293
class Medium::CreateDiffTask : public Medium::Task
296
CreateDiffTask(Medium *aMedium,
299
MediumVariant_T aVariant,
300
MediumLockList *aMediumLockList,
301
bool fKeepMediumLockList = false)
302
: Medium::Task(aMedium, aProgress),
303
mpMediumLockList(aMediumLockList),
306
mTargetCaller(aTarget),
307
mfKeepMediumLockList(fKeepMediumLockList)
309
AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
310
mRC = mTargetCaller.rc();
317
if (!mfKeepMediumLockList && mpMediumLockList)
318
delete mpMediumLockList;
321
MediumLockList *mpMediumLockList;
323
const ComObjPtr<Medium> mTarget;
324
MediumVariant_T mVariant;
327
virtual HRESULT handler();
329
AutoCaller mTargetCaller;
330
bool mfKeepMediumLockList;
333
class Medium::CloneTask : public Medium::Task
336
CloneTask(Medium *aMedium,
339
MediumVariant_T aVariant,
341
MediumLockList *aSourceMediumLockList,
342
MediumLockList *aTargetMediumLockList,
343
bool fKeepSourceMediumLockList = false,
344
bool fKeepTargetMediumLockList = false)
345
: Medium::Task(aMedium, aProgress),
348
mpSourceMediumLockList(aSourceMediumLockList),
349
mpTargetMediumLockList(aTargetMediumLockList),
351
mTargetCaller(aTarget),
352
mParentCaller(aParent),
353
mfKeepSourceMediumLockList(fKeepSourceMediumLockList),
354
mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
356
AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
357
mRC = mTargetCaller.rc();
360
/* aParent may be NULL */
361
mRC = mParentCaller.rc();
364
AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
365
AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
370
if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
371
delete mpSourceMediumLockList;
372
if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
373
delete mpTargetMediumLockList;
376
const ComObjPtr<Medium> mTarget;
377
const ComObjPtr<Medium> mParent;
378
MediumLockList *mpSourceMediumLockList;
379
MediumLockList *mpTargetMediumLockList;
380
MediumVariant_T mVariant;
383
virtual HRESULT handler();
385
AutoCaller mTargetCaller;
386
AutoCaller mParentCaller;
387
bool mfKeepSourceMediumLockList;
388
bool mfKeepTargetMediumLockList;
391
class Medium::CompactTask : public Medium::Task
394
CompactTask(Medium *aMedium,
396
MediumLockList *aMediumLockList,
397
bool fKeepMediumLockList = false)
398
: Medium::Task(aMedium, aProgress),
399
mpMediumLockList(aMediumLockList),
400
mfKeepMediumLockList(fKeepMediumLockList)
402
AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
407
if (!mfKeepMediumLockList && mpMediumLockList)
408
delete mpMediumLockList;
411
MediumLockList *mpMediumLockList;
414
virtual HRESULT handler();
416
bool mfKeepMediumLockList;
419
class Medium::ResizeTask : public Medium::Task
422
ResizeTask(Medium *aMedium,
425
MediumLockList *aMediumLockList,
426
bool fKeepMediumLockList = false)
427
: Medium::Task(aMedium, aProgress),
429
mpMediumLockList(aMediumLockList),
430
mfKeepMediumLockList(fKeepMediumLockList)
432
AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
437
if (!mfKeepMediumLockList && mpMediumLockList)
438
delete mpMediumLockList;
442
MediumLockList *mpMediumLockList;
445
virtual HRESULT handler();
447
bool mfKeepMediumLockList;
450
class Medium::ResetTask : public Medium::Task
453
ResetTask(Medium *aMedium,
455
MediumLockList *aMediumLockList,
456
bool fKeepMediumLockList = false)
457
: Medium::Task(aMedium, aProgress),
458
mpMediumLockList(aMediumLockList),
459
mfKeepMediumLockList(fKeepMediumLockList)
464
if (!mfKeepMediumLockList && mpMediumLockList)
465
delete mpMediumLockList;
468
MediumLockList *mpMediumLockList;
471
virtual HRESULT handler();
473
bool mfKeepMediumLockList;
476
class Medium::DeleteTask : public Medium::Task
479
DeleteTask(Medium *aMedium,
481
MediumLockList *aMediumLockList,
482
bool fKeepMediumLockList = false)
483
: Medium::Task(aMedium, aProgress),
484
mpMediumLockList(aMediumLockList),
485
mfKeepMediumLockList(fKeepMediumLockList)
490
if (!mfKeepMediumLockList && mpMediumLockList)
491
delete mpMediumLockList;
494
MediumLockList *mpMediumLockList;
497
virtual HRESULT handler();
499
bool mfKeepMediumLockList;
502
class Medium::MergeTask : public Medium::Task
505
MergeTask(Medium *aMedium,
508
Medium *aParentForTarget,
509
const MediaList &aChildrenToReparent,
511
MediumLockList *aMediumLockList,
512
bool fKeepMediumLockList = false)
513
: Medium::Task(aMedium, aProgress),
515
mfMergeForward(fMergeForward),
516
mParentForTarget(aParentForTarget),
517
mChildrenToReparent(aChildrenToReparent),
518
mpMediumLockList(aMediumLockList),
519
mTargetCaller(aTarget),
520
mParentForTargetCaller(aParentForTarget),
521
mfChildrenCaller(false),
522
mfKeepMediumLockList(fKeepMediumLockList)
524
AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
525
for (MediaList::const_iterator it = mChildrenToReparent.begin();
526
it != mChildrenToReparent.end();
529
HRESULT rc2 = (*it)->addCaller();
533
for (MediaList::const_iterator it2 = mChildrenToReparent.begin();
537
(*it2)->releaseCaller();
542
mfChildrenCaller = true;
547
if (!mfKeepMediumLockList && mpMediumLockList)
548
delete mpMediumLockList;
549
if (mfChildrenCaller)
551
for (MediaList::const_iterator it = mChildrenToReparent.begin();
552
it != mChildrenToReparent.end();
555
(*it)->releaseCaller();
560
const ComObjPtr<Medium> mTarget;
562
/* When mChildrenToReparent is empty then mParentForTarget is non-null.
563
* In other words: they are used in different cases. */
564
const ComObjPtr<Medium> mParentForTarget;
565
MediaList mChildrenToReparent;
566
MediumLockList *mpMediumLockList;
569
virtual HRESULT handler();
571
AutoCaller mTargetCaller;
572
AutoCaller mParentForTargetCaller;
573
bool mfChildrenCaller;
574
bool mfKeepMediumLockList;
577
class Medium::ExportTask : public Medium::Task
580
ExportTask(Medium *aMedium,
582
const char *aFilename,
583
MediumFormat *aFormat,
584
MediumVariant_T aVariant,
585
void *aVDImageIOCallbacks,
586
void *aVDImageIOUser,
587
MediumLockList *aSourceMediumLockList,
588
bool fKeepSourceMediumLockList = false)
589
: Medium::Task(aMedium, aProgress),
590
mpSourceMediumLockList(aSourceMediumLockList),
591
mFilename(aFilename),
594
mfKeepSourceMediumLockList(fKeepSourceMediumLockList)
596
AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
598
mVDImageIfaces = aMedium->m->vdImageIfaces;
599
if (aVDImageIOCallbacks)
601
int vrc = VDInterfaceAdd(&mVDInterfaceIO, "Medium::vdInterfaceIO",
602
VDINTERFACETYPE_IO, aVDImageIOCallbacks,
603
aVDImageIOUser, &mVDImageIfaces);
604
AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
610
if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
611
delete mpSourceMediumLockList;
614
MediumLockList *mpSourceMediumLockList;
616
ComObjPtr<MediumFormat> mFormat;
617
MediumVariant_T mVariant;
618
PVDINTERFACE mVDImageIfaces;
621
virtual HRESULT handler();
623
bool mfKeepSourceMediumLockList;
624
VDINTERFACE mVDInterfaceIO;
627
class Medium::ImportTask : public Medium::Task
630
ImportTask(Medium *aMedium,
632
const char *aFilename,
633
MediumFormat *aFormat,
634
MediumVariant_T aVariant,
635
void *aVDImageIOCallbacks,
636
void *aVDImageIOUser,
638
MediumLockList *aTargetMediumLockList,
639
bool fKeepTargetMediumLockList = false)
640
: Medium::Task(aMedium, aProgress),
641
mFilename(aFilename),
645
mpTargetMediumLockList(aTargetMediumLockList),
646
mParentCaller(aParent),
647
mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
649
AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
650
/* aParent may be NULL */
651
mRC = mParentCaller.rc();
655
mVDImageIfaces = aMedium->m->vdImageIfaces;
656
if (aVDImageIOCallbacks)
658
int vrc = VDInterfaceAdd(&mVDInterfaceIO, "Medium::vdInterfaceIO",
659
VDINTERFACETYPE_IO, aVDImageIOCallbacks,
660
aVDImageIOUser, &mVDImageIfaces);
661
AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
667
if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
668
delete mpTargetMediumLockList;
672
ComObjPtr<MediumFormat> mFormat;
673
MediumVariant_T mVariant;
674
const ComObjPtr<Medium> mParent;
675
MediumLockList *mpTargetMediumLockList;
676
PVDINTERFACE mVDImageIfaces;
679
virtual HRESULT handler();
681
AutoCaller mParentCaller;
682
bool mfKeepTargetMediumLockList;
683
VDINTERFACE mVDInterfaceIO;
687
* Thread function for time-consuming medium tasks.
689
* @param pvUser Pointer to the Medium::Task instance.
692
DECLCALLBACK(int) Medium::Task::fntMediumTask(RTTHREAD aThread, void *pvUser)
695
AssertReturn(pvUser, (int)E_INVALIDARG);
696
Medium::Task *pTask = static_cast<Medium::Task *>(pvUser);
698
pTask->mThread = aThread;
700
HRESULT rc = pTask->handler();
702
/* complete the progress if run asynchronously */
703
if (pTask->isAsync())
705
if (!pTask->mProgress.isNull())
706
pTask->mProgress->notifyComplete(rc);
709
/* pTask is no longer needed, delete it. */
712
LogFlowFunc(("rc=%Rhrc\n", rc));
719
* PFNVDPROGRESS callback handler for Task operations.
721
* @param pvUser Pointer to the Progress instance.
722
* @param uPercent Completion percentage (0-100).
725
DECLCALLBACK(int) Medium::Task::vdProgressCall(void *pvUser, unsigned uPercent)
727
Progress *that = static_cast<Progress *>(pvUser);
731
/* update the progress object, capping it at 99% as the final percent
732
* is used for additional operations like setting the UUIDs and similar. */
733
HRESULT rc = that->SetCurrentOperationProgress(uPercent * 99 / 100);
737
return VERR_CANCELLED;
739
return VERR_INVALID_STATE;
747
* Implementation code for the "create base" task.
749
HRESULT Medium::CreateBaseTask::handler()
751
return mMedium->taskCreateBaseHandler(*this);
755
* Implementation code for the "create diff" task.
757
HRESULT Medium::CreateDiffTask::handler()
759
return mMedium->taskCreateDiffHandler(*this);
763
* Implementation code for the "clone" task.
765
HRESULT Medium::CloneTask::handler()
767
return mMedium->taskCloneHandler(*this);
771
* Implementation code for the "compact" task.
773
HRESULT Medium::CompactTask::handler()
775
return mMedium->taskCompactHandler(*this);
779
* Implementation code for the "resize" task.
781
HRESULT Medium::ResizeTask::handler()
783
return mMedium->taskResizeHandler(*this);
788
* Implementation code for the "reset" task.
790
HRESULT Medium::ResetTask::handler()
792
return mMedium->taskResetHandler(*this);
796
* Implementation code for the "delete" task.
798
HRESULT Medium::DeleteTask::handler()
800
return mMedium->taskDeleteHandler(*this);
804
* Implementation code for the "merge" task.
806
HRESULT Medium::MergeTask::handler()
808
return mMedium->taskMergeHandler(*this);
812
* Implementation code for the "export" task.
814
HRESULT Medium::ExportTask::handler()
816
return mMedium->taskExportHandler(*this);
820
* Implementation code for the "import" task.
822
HRESULT Medium::ImportTask::handler()
824
return mMedium->taskImportHandler(*this);
827
////////////////////////////////////////////////////////////////////////////////
829
// Medium constructor / destructor
831
////////////////////////////////////////////////////////////////////////////////
833
DEFINE_EMPTY_CTOR_DTOR(Medium)
835
HRESULT Medium::FinalConstruct()
839
/* Initialize the callbacks of the VD error interface */
840
m->vdIfCallsError.cbSize = sizeof(VDINTERFACEERROR);
841
m->vdIfCallsError.enmInterface = VDINTERFACETYPE_ERROR;
842
m->vdIfCallsError.pfnError = vdErrorCall;
843
m->vdIfCallsError.pfnMessage = NULL;
845
/* Initialize the callbacks of the VD config interface */
846
m->vdIfCallsConfig.cbSize = sizeof(VDINTERFACECONFIG);
847
m->vdIfCallsConfig.enmInterface = VDINTERFACETYPE_CONFIG;
848
m->vdIfCallsConfig.pfnAreKeysValid = vdConfigAreKeysValid;
849
m->vdIfCallsConfig.pfnQuerySize = vdConfigQuerySize;
850
m->vdIfCallsConfig.pfnQuery = vdConfigQuery;
852
/* Initialize the callbacks of the VD TCP interface (we always use the host
853
* IP stack for now) */
854
m->vdIfCallsTcpNet.cbSize = sizeof(VDINTERFACETCPNET);
855
m->vdIfCallsTcpNet.enmInterface = VDINTERFACETYPE_TCPNET;
856
m->vdIfCallsTcpNet.pfnSocketCreate = vdTcpSocketCreate;
857
m->vdIfCallsTcpNet.pfnSocketDestroy = vdTcpSocketDestroy;
858
m->vdIfCallsTcpNet.pfnClientConnect = vdTcpClientConnect;
859
m->vdIfCallsTcpNet.pfnClientClose = vdTcpClientClose;
860
m->vdIfCallsTcpNet.pfnIsClientConnected = vdTcpIsClientConnected;
861
m->vdIfCallsTcpNet.pfnSelectOne = vdTcpSelectOne;
862
m->vdIfCallsTcpNet.pfnRead = vdTcpRead;
863
m->vdIfCallsTcpNet.pfnWrite = vdTcpWrite;
864
m->vdIfCallsTcpNet.pfnSgWrite = vdTcpSgWrite;
865
m->vdIfCallsTcpNet.pfnFlush = vdTcpFlush;
866
m->vdIfCallsTcpNet.pfnSetSendCoalescing = vdTcpSetSendCoalescing;
867
m->vdIfCallsTcpNet.pfnGetLocalAddress = vdTcpGetLocalAddress;
868
m->vdIfCallsTcpNet.pfnGetPeerAddress = vdTcpGetPeerAddress;
869
m->vdIfCallsTcpNet.pfnSelectOneEx = NULL;
870
m->vdIfCallsTcpNet.pfnPoke = NULL;
872
/* Initialize the per-disk interface chain (could be done more globally,
873
* but it's not wasting much time or space so it's not worth it). */
875
vrc = VDInterfaceAdd(&m->vdIfError,
876
"Medium::vdInterfaceError",
877
VDINTERFACETYPE_ERROR,
878
&m->vdIfCallsError, this, &m->vdDiskIfaces);
879
AssertRCReturn(vrc, E_FAIL);
881
/* Initialize the per-image interface chain */
882
vrc = VDInterfaceAdd(&m->vdIfConfig,
883
"Medium::vdInterfaceConfig",
884
VDINTERFACETYPE_CONFIG,
885
&m->vdIfCallsConfig, this, &m->vdImageIfaces);
886
AssertRCReturn(vrc, E_FAIL);
888
vrc = VDInterfaceAdd(&m->vdIfTcpNet,
889
"Medium::vdInterfaceTcpNet",
890
VDINTERFACETYPE_TCPNET,
891
&m->vdIfCallsTcpNet, this, &m->vdImageIfaces);
892
AssertRCReturn(vrc, E_FAIL);
894
vrc = RTSemEventMultiCreate(&m->queryInfoSem);
895
AssertRCReturn(vrc, E_FAIL);
896
vrc = RTSemEventMultiSignal(m->queryInfoSem);
897
AssertRCReturn(vrc, E_FAIL);
902
void Medium::FinalRelease()
910
* Initializes an empty hard disk object without creating or opening an associated
913
* This gets called by VirtualBox::CreateHardDisk() in which case uuidMachineRegistry
914
* is empty since starting with VirtualBox 4.0, we no longer add opened media to a
915
* registry automatically (this is deferred until the medium is attached to a machine).
917
* This also gets called when VirtualBox creates diff images; in this case uuidMachineRegistry
918
* is set to the registry of the parent image to make sure they all end up in the same
921
* For hard disks that don't have the VD_CAP_CREATE_FIXED or
922
* VD_CAP_CREATE_DYNAMIC capability (and therefore cannot be created or deleted
923
* with the means of VirtualBox) the associated storage unit is assumed to be
924
* ready for use so the state of the hard disk object will be set to Created.
926
* @param aVirtualBox VirtualBox object.
928
* @param aLocation Storage unit location.
929
* @param uuidMachineRegistry The registry to which this medium should be added (global registry UUI or medium UUID or empty if none).
930
* @param pllRegistriesThatNeedSaving Optional list to receive the UUIDs of the media registries that need saving.
932
HRESULT Medium::init(VirtualBox *aVirtualBox,
933
const Utf8Str &aFormat,
934
const Utf8Str &aLocation,
935
const Guid &uuidMachineRegistry,
936
GuidList *pllRegistriesThatNeedSaving)
938
AssertReturn(aVirtualBox != NULL, E_FAIL);
939
AssertReturn(!aFormat.isEmpty(), E_FAIL);
941
/* Enclose the state transition NotReady->InInit->Ready */
942
AutoInitSpan autoInitSpan(this);
943
AssertReturn(autoInitSpan.isOk(), E_FAIL);
947
unconst(m->pVirtualBox) = aVirtualBox;
949
if (!uuidMachineRegistry.isEmpty())
950
m->llRegistryIDs.push_back(uuidMachineRegistry);
953
m->state = MediumState_NotCreated;
955
/* cannot be a host drive */
956
m->hostDrive = false;
958
/* No storage unit is created yet, no need to queryInfo() */
960
rc = setFormat(aFormat);
961
if (FAILED(rc)) return rc;
963
rc = setLocation(aLocation);
964
if (FAILED(rc)) return rc;
966
if (!(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateFixed
967
| MediumFormatCapabilities_CreateDynamic))
970
/* Storage for hard disks of this format can neither be explicitly
971
* created by VirtualBox nor deleted, so we place the hard disk to
972
* Inaccessible state here and also add it to the registry. The
973
* state means that one has to use RefreshState() to update the
974
* medium format specific fields. */
975
m->state = MediumState_Inaccessible;
977
unconst(m->id).create();
979
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
980
rc = m->pVirtualBox->registerHardDisk(this, pllRegistriesThatNeedSaving);
983
/* Confirm a successful initialization when it's the case */
985
autoInitSpan.setSucceeded();
991
* Initializes the medium object by opening the storage unit at the specified
992
* location. The enOpenMode parameter defines whether the medium will be opened
993
* read/write or read-only.
995
* This gets called by VirtualBox::OpenMedium() and also by
996
* Machine::AttachDevice() and createImplicitDiffs() when new diff
997
* images are created.
999
* There is no registry for this case since starting with VirtualBox 4.0, we
1000
* no longer add opened media to a registry automatically (this is deferred
1001
* until the medium is attached to a machine).
1003
* For hard disks, the UUID, format and the parent of this medium will be
1004
* determined when reading the medium storage unit. For DVD and floppy images,
1005
* which have no UUIDs in their storage units, new UUIDs are created.
1006
* If the detected or set parent is not known to VirtualBox, then this method
1009
* @param aVirtualBox VirtualBox object.
1010
* @param aLocation Storage unit location.
1011
* @param enOpenMode Whether to open the medium read/write or read-only.
1012
* @param aDeviceType Device type of medium.
1014
HRESULT Medium::init(VirtualBox *aVirtualBox,
1015
const Utf8Str &aLocation,
1016
HDDOpenMode enOpenMode,
1017
DeviceType_T aDeviceType)
1019
AssertReturn(aVirtualBox, E_INVALIDARG);
1020
AssertReturn(!aLocation.isEmpty(), E_INVALIDARG);
1022
/* Enclose the state transition NotReady->InInit->Ready */
1023
AutoInitSpan autoInitSpan(this);
1024
AssertReturn(autoInitSpan.isOk(), E_FAIL);
1028
unconst(m->pVirtualBox) = aVirtualBox;
1030
/* there must be a storage unit */
1031
m->state = MediumState_Created;
1033
/* remember device type for correct unregistering later */
1034
m->devType = aDeviceType;
1036
/* cannot be a host drive */
1037
m->hostDrive = false;
1039
/* remember the open mode (defaults to ReadWrite) */
1040
m->hddOpenMode = enOpenMode;
1042
if (aDeviceType == DeviceType_DVD)
1043
m->type = MediumType_Readonly;
1044
else if (aDeviceType == DeviceType_Floppy)
1045
m->type = MediumType_Writethrough;
1047
rc = setLocation(aLocation);
1048
if (FAILED(rc)) return rc;
1050
/* get all the information about the medium from the storage unit */
1051
rc = queryInfo(false /* fSetImageId */, false /* fSetParentId */);
1055
/* if the storage unit is not accessible, it's not acceptable for the
1056
* newly opened media so convert this into an error */
1057
if (m->state == MediumState_Inaccessible)
1059
Assert(!m->strLastAccessError.isEmpty());
1060
rc = setError(E_FAIL, "%s", m->strLastAccessError.c_str());
1064
AssertReturn(!m->id.isEmpty(), E_FAIL);
1066
/* storage format must be detected by queryInfo() if the medium is accessible */
1067
AssertReturn(!m->strFormat.isEmpty(), E_FAIL);
1071
/* Confirm a successful initialization when it's the case */
1073
autoInitSpan.setSucceeded();
1079
* Initializes the medium object by loading its data from the given settings
1080
* node. In this mode, the medium will always be opened read/write.
1082
* In this case, since we're loading from a registry, uuidMachineRegistry is
1083
* always set: it's either the global registry UUID or a machine UUID when
1084
* loading from a per-machine registry.
1086
* @param aVirtualBox VirtualBox object.
1087
* @param aParent Parent medium disk or NULL for a root (base) medium.
1088
* @param aDeviceType Device type of the medium.
1089
* @param uuidMachineRegistry The registry to which this medium should be added (global registry UUI or medium UUID).
1090
* @param aNode Configuration settings.
1091
* @param strMachineFolder The machine folder with which to resolve relative paths; if empty, then we use the VirtualBox home directory
1093
* @note Locks the medium tree for writing.
1095
HRESULT Medium::init(VirtualBox *aVirtualBox,
1097
DeviceType_T aDeviceType,
1098
const Guid &uuidMachineRegistry,
1099
const settings::Medium &data,
1100
const Utf8Str &strMachineFolder)
1102
using namespace settings;
1104
AssertReturn(aVirtualBox, E_INVALIDARG);
1106
/* Enclose the state transition NotReady->InInit->Ready */
1107
AutoInitSpan autoInitSpan(this);
1108
AssertReturn(autoInitSpan.isOk(), E_FAIL);
1112
unconst(m->pVirtualBox) = aVirtualBox;
1114
if (!uuidMachineRegistry.isEmpty())
1115
m->llRegistryIDs.push_back(uuidMachineRegistry);
1117
/* register with VirtualBox/parent early, since uninit() will
1118
* unconditionally unregister on failure */
1121
// differencing medium: add to parent
1122
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1123
m->pParent = aParent;
1124
aParent->m->llChildren.push_back(this);
1127
/* see below why we don't call queryInfo() (and therefore treat the medium
1128
* as inaccessible for now */
1129
m->state = MediumState_Inaccessible;
1130
m->strLastAccessError = tr("Accessibility check was not yet performed");
1133
unconst(m->id) = data.uuid;
1135
/* assume not a host drive */
1136
m->hostDrive = false;
1139
m->strDescription = data.strDescription;
1142
if (aDeviceType == DeviceType_HardDisk)
1144
AssertReturn(!data.strFormat.isEmpty(), E_FAIL);
1145
rc = setFormat(data.strFormat);
1146
if (FAILED(rc)) return rc;
1150
/// @todo handle host drive settings here as well?
1151
if (!data.strFormat.isEmpty())
1152
rc = setFormat(data.strFormat);
1154
rc = setFormat("RAW");
1155
if (FAILED(rc)) return rc;
1158
/* optional, only for diffs, default is false; we can only auto-reset
1159
* diff media so they must have a parent */
1160
if (aParent != NULL)
1161
m->autoReset = data.fAutoReset;
1163
m->autoReset = false;
1165
/* properties (after setting the format as it populates the map). Note that
1166
* if some properties are not supported but present in the settings file,
1167
* they will still be read and accessible (for possible backward
1168
* compatibility; we can also clean them up from the XML upon next
1169
* XML format version change if we wish) */
1170
for (settings::StringsMap::const_iterator it = data.properties.begin();
1171
it != data.properties.end();
1174
const Utf8Str &name = it->first;
1175
const Utf8Str &value = it->second;
1176
m->mapProperties[name] = value;
1179
// compose full path of the medium, if it's not fully qualified...
1180
// slightly convoluted logic here. If the caller has given us a
1181
// machine folder, then a relative path will be relative to that:
1183
if ( !strMachineFolder.isEmpty()
1184
&& !RTPathStartsWithRoot(data.strLocation.c_str())
1187
strFull = strMachineFolder;
1188
strFull += RTPATH_SLASH;
1189
strFull += data.strLocation;
1193
// Otherwise use the old VirtualBox "make absolute path" logic:
1194
rc = m->pVirtualBox->calculateFullPath(data.strLocation, strFull);
1195
if (FAILED(rc)) return rc;
1198
rc = setLocation(strFull);
1199
if (FAILED(rc)) return rc;
1201
if (aDeviceType == DeviceType_HardDisk)
1203
/* type is only for base hard disks */
1204
if (m->pParent.isNull())
1205
m->type = data.hdType;
1207
else if (aDeviceType == DeviceType_DVD)
1208
m->type = MediumType_Readonly;
1210
m->type = MediumType_Writethrough;
1212
/* remember device type for correct unregistering later */
1213
m->devType = aDeviceType;
1215
LogFlowThisFunc(("m->strLocationFull='%s', m->strFormat=%s, m->id={%RTuuid}\n",
1216
m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw()));
1218
/* Don't call queryInfo() for registered media to prevent the calling
1219
* thread (i.e. the VirtualBox server startup thread) from an unexpected
1220
* freeze but mark it as initially inaccessible instead. The vital UUID,
1221
* location and format properties are read from the registry file above; to
1222
* get the actual state and the rest of the data, the user will have to call
1223
* COMGETTER(State). */
1225
AutoWriteLock treeLock(aVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1227
/* load all children */
1228
for (settings::MediaList::const_iterator it = data.llChildren.begin();
1229
it != data.llChildren.end();
1232
const settings::Medium &med = *it;
1234
ComObjPtr<Medium> pHD;
1236
rc = pHD->init(aVirtualBox,
1239
uuidMachineRegistry,
1242
if (FAILED(rc)) break;
1244
rc = m->pVirtualBox->registerHardDisk(pHD, NULL /* pllRegistriesThatNeedSaving */ );
1245
if (FAILED(rc)) break;
1248
/* Confirm a successful initialization when it's the case */
1250
autoInitSpan.setSucceeded();
1256
* Initializes the medium object by providing the host drive information.
1257
* Not used for anything but the host floppy/host DVD case.
1259
* There is no registry for this case.
1261
* @param aVirtualBox VirtualBox object.
1262
* @param aDeviceType Device type of the medium.
1263
* @param aLocation Location of the host drive.
1264
* @param aDescription Comment for this host drive.
1266
* @note Locks VirtualBox lock for writing.
1268
HRESULT Medium::init(VirtualBox *aVirtualBox,
1269
DeviceType_T aDeviceType,
1270
const Utf8Str &aLocation,
1271
const Utf8Str &aDescription /* = Utf8Str::Empty */)
1273
ComAssertRet(aDeviceType == DeviceType_DVD || aDeviceType == DeviceType_Floppy, E_INVALIDARG);
1274
ComAssertRet(!aLocation.isEmpty(), E_INVALIDARG);
1276
/* Enclose the state transition NotReady->InInit->Ready */
1277
AutoInitSpan autoInitSpan(this);
1278
AssertReturn(autoInitSpan.isOk(), E_FAIL);
1280
unconst(m->pVirtualBox) = aVirtualBox;
1282
/* fake up a UUID which is unique, but also reproducible */
1285
if (aDeviceType == DeviceType_DVD)
1286
memcpy(&uuid.au8[0], "DVD", 3);
1288
memcpy(&uuid.au8[0], "FD", 2);
1289
/* use device name, adjusted to the end of uuid, shortened if necessary */
1290
size_t lenLocation = aLocation.length();
1291
if (lenLocation > 12)
1292
memcpy(&uuid.au8[4], aLocation.c_str() + (lenLocation - 12), 12);
1294
memcpy(&uuid.au8[4 + 12 - lenLocation], aLocation.c_str(), lenLocation);
1295
unconst(m->id) = uuid;
1297
if (aDeviceType == DeviceType_DVD)
1298
m->type = MediumType_Readonly;
1300
m->type = MediumType_Writethrough;
1301
m->devType = aDeviceType;
1302
m->state = MediumState_Created;
1303
m->hostDrive = true;
1304
HRESULT rc = setFormat("RAW");
1305
if (FAILED(rc)) return rc;
1306
rc = setLocation(aLocation);
1307
if (FAILED(rc)) return rc;
1308
m->strDescription = aDescription;
1310
/// @todo generate uuid (similarly to host network interface uuid) from location and device type
1312
autoInitSpan.setSucceeded();
1317
* Uninitializes the instance.
1319
* Called either from FinalRelease() or by the parent when it gets destroyed.
1321
* @note All children of this medium get uninitialized by calling their
1324
* @note Caller must hold the tree lock of the medium tree this medium is on.
1326
void Medium::uninit()
1328
/* Enclose the state transition Ready->InUninit->NotReady */
1329
AutoUninitSpan autoUninitSpan(this);
1330
if (autoUninitSpan.uninitDone())
1333
if (!m->formatObj.isNull())
1335
/* remove the caller reference we added in setFormat() */
1336
m->formatObj->releaseCaller();
1337
m->formatObj.setNull();
1340
if (m->state == MediumState_Deleting)
1342
/* This medium has been already deleted (directly or as part of a
1343
* merge). Reparenting has already been done. */
1344
Assert(m->pParent.isNull());
1348
MediaList::iterator it;
1349
for (it = m->llChildren.begin();
1350
it != m->llChildren.end();
1353
Medium *pChild = *it;
1354
pChild->m->pParent.setNull();
1357
m->llChildren.clear(); // this unsets all the ComPtrs and probably calls delete
1361
// this is a differencing disk: then remove it from the parent's children list
1366
RTSemEventMultiSignal(m->queryInfoSem);
1367
RTSemEventMultiDestroy(m->queryInfoSem);
1368
m->queryInfoSem = NIL_RTSEMEVENTMULTI;
1370
unconst(m->pVirtualBox) = NULL;
1374
* Internal helper that removes "this" from the list of children of its
1375
* parent. Used in uninit() and other places when reparenting is necessary.
1377
* The caller must hold the medium tree lock!
1379
void Medium::deparent()
1381
MediaList &llParent = m->pParent->m->llChildren;
1382
for (MediaList::iterator it = llParent.begin();
1383
it != llParent.end();
1386
Medium *pParentsChild = *it;
1387
if (this == pParentsChild)
1393
m->pParent.setNull();
1397
* Internal helper that removes "this" from the list of children of its
1398
* parent. Used in uninit() and other places when reparenting is necessary.
1400
* The caller must hold the medium tree lock!
1402
void Medium::setParent(const ComObjPtr<Medium> &pParent)
1404
m->pParent = pParent;
1406
pParent->m->llChildren.push_back(this);
1410
////////////////////////////////////////////////////////////////////////////////
1412
// IMedium public methods
1414
////////////////////////////////////////////////////////////////////////////////
1416
STDMETHODIMP Medium::COMGETTER(Id)(BSTR *aId)
1418
CheckComArgOutPointerValid(aId);
1420
AutoCaller autoCaller(this);
1421
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1423
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1425
m->id.toUtf16().cloneTo(aId);
1430
STDMETHODIMP Medium::COMGETTER(Description)(BSTR *aDescription)
1432
CheckComArgOutPointerValid(aDescription);
1434
AutoCaller autoCaller(this);
1435
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1437
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
m->strDescription.cloneTo(aDescription);
1444
STDMETHODIMP Medium::COMSETTER(Description)(IN_BSTR aDescription)
1446
AutoCaller autoCaller(this);
1447
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1449
// AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1451
/// @todo update m->description and save the global registry (and local
1452
/// registries of portable VMs referring to this medium), this will also
1453
/// require to add the mRegistered flag to data
1455
NOREF(aDescription);
1457
ReturnComNotImplemented();
1460
STDMETHODIMP Medium::COMGETTER(State)(MediumState_T *aState)
1462
CheckComArgOutPointerValid(aState);
1464
AutoCaller autoCaller(this);
1465
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1467
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1473
STDMETHODIMP Medium::COMGETTER(Variant)(ULONG *aVariant)
1475
CheckComArgOutPointerValid(aVariant);
1477
AutoCaller autoCaller(this);
1478
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1480
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1481
*aVariant = m->variant;
1487
STDMETHODIMP Medium::COMGETTER(Location)(BSTR *aLocation)
1489
CheckComArgOutPointerValid(aLocation);
1491
AutoCaller autoCaller(this);
1492
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1494
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1496
m->strLocationFull.cloneTo(aLocation);
1501
STDMETHODIMP Medium::COMSETTER(Location)(IN_BSTR aLocation)
1503
CheckComArgStrNotEmptyOrNull(aLocation);
1505
AutoCaller autoCaller(this);
1506
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1508
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1510
/// @todo NEWMEDIA for file names, add the default extension if no extension
1511
/// is present (using the information from the VD backend which also implies
1512
/// that one more parameter should be passed to setLocation() requesting
1513
/// that functionality since it is only allowed when called from this method
1515
/// @todo NEWMEDIA rename the file and set m->location on success, then save
1516
/// the global registry (and local registries of portable VMs referring to
1517
/// this medium), this will also require to add the mRegistered flag to data
1519
ReturnComNotImplemented();
1522
STDMETHODIMP Medium::COMGETTER(Name)(BSTR *aName)
1524
CheckComArgOutPointerValid(aName);
1526
AutoCaller autoCaller(this);
1527
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1529
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1531
getName().cloneTo(aName);
1536
STDMETHODIMP Medium::COMGETTER(DeviceType)(DeviceType_T *aDeviceType)
1538
CheckComArgOutPointerValid(aDeviceType);
1540
AutoCaller autoCaller(this);
1541
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1543
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1545
*aDeviceType = m->devType;
1550
STDMETHODIMP Medium::COMGETTER(HostDrive)(BOOL *aHostDrive)
1552
CheckComArgOutPointerValid(aHostDrive);
1554
AutoCaller autoCaller(this);
1555
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1557
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1559
*aHostDrive = m->hostDrive;
1564
STDMETHODIMP Medium::COMGETTER(Size)(LONG64 *aSize)
1566
CheckComArgOutPointerValid(aSize);
1568
AutoCaller autoCaller(this);
1569
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1571
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1578
STDMETHODIMP Medium::COMGETTER(Format)(BSTR *aFormat)
1580
CheckComArgOutPointerValid(aFormat);
1582
AutoCaller autoCaller(this);
1583
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1585
/* no need to lock, m->strFormat is const */
1586
m->strFormat.cloneTo(aFormat);
1591
STDMETHODIMP Medium::COMGETTER(MediumFormat)(IMediumFormat **aMediumFormat)
1593
CheckComArgOutPointerValid(aMediumFormat);
1595
AutoCaller autoCaller(this);
1596
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1598
/* no need to lock, m->formatObj is const */
1599
m->formatObj.queryInterfaceTo(aMediumFormat);
1604
STDMETHODIMP Medium::COMGETTER(Type)(MediumType_T *aType)
1606
CheckComArgOutPointerValid(aType);
1608
AutoCaller autoCaller(this);
1609
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1611
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1618
STDMETHODIMP Medium::COMSETTER(Type)(MediumType_T aType)
1620
AutoCaller autoCaller(this);
1621
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1623
// we access mParent and members
1624
AutoMultiWriteLock2 mlock(&m->pVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
1628
case MediumState_Created:
1629
case MediumState_Inaccessible:
1632
return setStateError();
1635
if (m->type == aType)
1641
DeviceType_T devType = getDeviceType();
1642
// DVD media can only be readonly.
1643
if (devType == DeviceType_DVD && aType != MediumType_Readonly)
1644
return setError(VBOX_E_INVALID_OBJECT_STATE,
1645
tr("Cannot change the type of DVD medium '%s'"),
1646
m->strLocationFull.c_str());
1647
// Floppy media can only be writethrough or readonly.
1648
if ( devType == DeviceType_Floppy
1649
&& aType != MediumType_Writethrough
1650
&& aType != MediumType_Readonly)
1651
return setError(VBOX_E_INVALID_OBJECT_STATE,
1652
tr("Cannot change the type of floppy medium '%s'"),
1653
m->strLocationFull.c_str());
1655
/* cannot change the type of a differencing medium */
1657
return setError(VBOX_E_INVALID_OBJECT_STATE,
1658
tr("Cannot change the type of medium '%s' because it is a differencing medium"),
1659
m->strLocationFull.c_str());
1661
/* Cannot change the type of a medium being in use by more than one VM.
1662
* If the change is to Immutable or MultiAttach then it must not be
1663
* directly attached to any VM, otherwise the assumptions about indirect
1664
* attachment elsewhere are violated and the VM becomes inaccessible.
1665
* Attaching an immutable medium triggers the diff creation, and this is
1666
* vital for the correct operation. */
1667
if ( m->backRefs.size() > 1
1668
|| ( ( aType == MediumType_Immutable
1669
|| aType == MediumType_MultiAttach)
1670
&& m->backRefs.size() > 0))
1671
return setError(VBOX_E_INVALID_OBJECT_STATE,
1672
tr("Cannot change the type of medium '%s' because it is attached to %d virtual machines"),
1673
m->strLocationFull.c_str(), m->backRefs.size());
1677
case MediumType_Normal:
1678
case MediumType_Immutable:
1679
case MediumType_MultiAttach:
1681
/* normal can be easily converted to immutable and vice versa even
1682
* if they have children as long as they are not attached to any
1683
* machine themselves */
1686
case MediumType_Writethrough:
1687
case MediumType_Shareable:
1688
case MediumType_Readonly:
1690
/* cannot change to writethrough, shareable or readonly
1691
* if there are children */
1692
if (getChildren().size() != 0)
1693
return setError(VBOX_E_OBJECT_IN_USE,
1694
tr("Cannot change type for medium '%s' since it has %d child media"),
1695
m->strLocationFull.c_str(), getChildren().size());
1696
if (aType == MediumType_Shareable)
1698
if (m->state == MediumState_Inaccessible)
1700
HRESULT rc = queryInfo(false /* fSetImageId */, false /* fSetParentId */);
1703
tr("Cannot change type for medium '%s' to 'Shareable' because the medium is inaccessible"),
1704
m->strLocationFull.c_str());
1707
MediumVariant_T variant = getVariant();
1708
if (!(variant & MediumVariant_Fixed))
1709
return setError(VBOX_E_INVALID_OBJECT_STATE,
1710
tr("Cannot change type for medium '%s' to 'Shareable' since it is a dynamic medium storage unit"),
1711
m->strLocationFull.c_str());
1713
else if (aType == MediumType_Readonly && devType == DeviceType_HardDisk)
1715
// Readonly hard disks are not allowed, this medium type is reserved for
1716
// DVDs and floppy images at the moment. Later we might allow readonly hard
1717
// disks, but that's extremely unusual and many guest OSes will have trouble.
1718
return setError(VBOX_E_INVALID_OBJECT_STATE,
1719
tr("Cannot change type for medium '%s' to 'Readonly' since it is a hard disk"),
1720
m->strLocationFull.c_str());
1725
AssertFailedReturn(E_FAIL);
1728
if (aType == MediumType_MultiAttach)
1730
// This type is new with VirtualBox 4.0 and therefore requires settings
1731
// version 1.11 in the settings backend. Unfortunately it is not enough to do
1732
// the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
1733
// two reasons: The medium type is a property of the media registry tree, which
1734
// can reside in the global config file (for pre-4.0 media); we would therefore
1735
// possibly need to bump the global config version. We don't want to do that though
1736
// because that might make downgrading to pre-4.0 impossible.
1737
// As a result, we can only use these two new types if the medium is NOT in the
1739
const Guid &uuidGlobalRegistry = m->pVirtualBox->getGlobalRegistryId();
1740
if (isInRegistry(uuidGlobalRegistry))
1741
return setError(VBOX_E_INVALID_OBJECT_STATE,
1742
tr("Cannot change type for medium '%s': the media type 'MultiAttach' can only be used "
1743
"on media registered with a machine that was created with VirtualBox 4.0 or later"),
1744
m->strLocationFull.c_str());
1749
// save the settings
1750
GuidList llRegistriesThatNeedSaving;
1751
addToRegistryIDList(llRegistriesThatNeedSaving);
1753
HRESULT rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
1758
STDMETHODIMP Medium::COMGETTER(Parent)(IMedium **aParent)
1760
CheckComArgOutPointerValid(aParent);
1762
AutoCaller autoCaller(this);
1763
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1765
/* we access mParent */
1766
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1768
m->pParent.queryInterfaceTo(aParent);
1773
STDMETHODIMP Medium::COMGETTER(Children)(ComSafeArrayOut(IMedium *, aChildren))
1775
CheckComArgOutSafeArrayPointerValid(aChildren);
1777
AutoCaller autoCaller(this);
1778
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1780
/* we access children */
1781
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1783
SafeIfaceArray<IMedium> children(this->getChildren());
1784
children.detachTo(ComSafeArrayOutArg(aChildren));
1789
STDMETHODIMP Medium::COMGETTER(Base)(IMedium **aBase)
1791
CheckComArgOutPointerValid(aBase);
1793
/* base() will do callers/locking */
1795
getBase().queryInterfaceTo(aBase);
1800
STDMETHODIMP Medium::COMGETTER(ReadOnly)(BOOL *aReadOnly)
1802
CheckComArgOutPointerValid(aReadOnly);
1804
AutoCaller autoCaller(this);
1805
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1807
/* isRadOnly() will do locking */
1809
*aReadOnly = isReadOnly();
1814
STDMETHODIMP Medium::COMGETTER(LogicalSize)(LONG64 *aLogicalSize)
1816
CheckComArgOutPointerValid(aLogicalSize);
1819
AutoCaller autoCaller(this);
1820
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1822
/* we access mParent */
1823
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1825
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
if (m->pParent.isNull())
1829
*aLogicalSize = m->logicalSize;
1835
/* We assume that some backend may decide to return a meaningless value in
1836
* response to VDGetSize() for differencing media and therefore always
1837
* ask the base medium ourselves. */
1839
/* base() will do callers/locking */
1841
return getBase()->COMGETTER(LogicalSize)(aLogicalSize);
1844
STDMETHODIMP Medium::COMGETTER(AutoReset)(BOOL *aAutoReset)
1846
CheckComArgOutPointerValid(aAutoReset);
1848
AutoCaller autoCaller(this);
1849
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1851
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1853
if (m->pParent.isNull())
1854
*aAutoReset = FALSE;
1856
*aAutoReset = m->autoReset;
1861
STDMETHODIMP Medium::COMSETTER(AutoReset)(BOOL aAutoReset)
1863
AutoCaller autoCaller(this);
1864
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1866
AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1868
if (m->pParent.isNull())
1869
return setError(VBOX_E_NOT_SUPPORTED,
1870
tr("Medium '%s' is not differencing"),
1871
m->strLocationFull.c_str());
1875
if (m->autoReset != !!aAutoReset)
1877
m->autoReset = !!aAutoReset;
1879
// save the settings
1880
GuidList llRegistriesThatNeedSaving;
1881
addToRegistryIDList(llRegistriesThatNeedSaving);
1883
rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
1889
STDMETHODIMP Medium::COMGETTER(LastAccessError)(BSTR *aLastAccessError)
1891
CheckComArgOutPointerValid(aLastAccessError);
1893
AutoCaller autoCaller(this);
1894
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1896
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
m->strLastAccessError.cloneTo(aLastAccessError);
1903
STDMETHODIMP Medium::COMGETTER(MachineIds)(ComSafeArrayOut(BSTR,aMachineIds))
1905
CheckComArgOutSafeArrayPointerValid(aMachineIds);
1907
AutoCaller autoCaller(this);
1908
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1910
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1912
com::SafeArray<BSTR> machineIds;
1914
if (m->backRefs.size() != 0)
1916
machineIds.reset(m->backRefs.size());
1919
for (BackRefList::const_iterator it = m->backRefs.begin();
1920
it != m->backRefs.end(); ++it, ++i)
1922
it->machineId.toUtf16().detachTo(&machineIds[i]);
1926
machineIds.detachTo(ComSafeArrayOutArg(aMachineIds));
1931
STDMETHODIMP Medium::SetIDs(BOOL aSetImageId,
1936
AutoCaller autoCaller(this);
1937
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1939
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1943
case MediumState_Created:
1946
return setStateError();
1949
Guid imageId, parentId;
1952
imageId = Guid(aImageId);
1953
if (imageId.isEmpty())
1954
return setError(E_INVALIDARG, tr("Argument %s is empty"), "aImageId");
1957
parentId = Guid(aParentId);
1959
unconst(m->uuidImage) = imageId;
1960
unconst(m->uuidParentImage) = parentId;
1962
HRESULT rc = queryInfo(!!aSetImageId /* fSetImageId */,
1963
!!aSetParentId /* fSetParentId */);
1968
STDMETHODIMP Medium::RefreshState(MediumState_T *aState)
1970
CheckComArgOutPointerValid(aState);
1972
AutoCaller autoCaller(this);
1973
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1975
/* queryInfo() locks this for writing. */
1976
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1982
case MediumState_Created:
1983
case MediumState_Inaccessible:
1984
case MediumState_LockedRead:
1986
rc = queryInfo(false /* fSetImageId */, false /* fSetParentId */);
1998
STDMETHODIMP Medium::GetSnapshotIds(IN_BSTR aMachineId,
1999
ComSafeArrayOut(BSTR, aSnapshotIds))
2001
CheckComArgExpr(aMachineId, Guid(aMachineId).isEmpty() == false);
2002
CheckComArgOutSafeArrayPointerValid(aSnapshotIds);
2004
AutoCaller autoCaller(this);
2005
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2007
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2009
com::SafeArray<BSTR> snapshotIds;
2011
Guid id(aMachineId);
2012
for (BackRefList::const_iterator it = m->backRefs.begin();
2013
it != m->backRefs.end(); ++it)
2015
if (it->machineId == id)
2017
size_t size = it->llSnapshotIds.size();
2019
/* if the medium is attached to the machine in the current state, we
2020
* return its ID as the first element of the array */
2021
if (it->fInCurState)
2026
snapshotIds.reset(size);
2029
if (it->fInCurState)
2030
it->machineId.toUtf16().detachTo(&snapshotIds[j++]);
2032
for (GuidList::const_iterator jt = it->llSnapshotIds.begin();
2033
jt != it->llSnapshotIds.end();
2036
(*jt).toUtf16().detachTo(&snapshotIds[j]);
2044
snapshotIds.detachTo(ComSafeArrayOutArg(aSnapshotIds));
2050
* @note @a aState may be NULL if the state value is not needed (only for
2051
* in-process calls).
2053
STDMETHODIMP Medium::LockRead(MediumState_T *aState)
2055
AutoCaller autoCaller(this);
2056
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2058
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2060
/* Wait for a concurrently running queryInfo() to complete */
2061
while (m->queryInfoRunning)
2064
RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
2068
/* return the current state before */
2076
case MediumState_Created:
2077
case MediumState_Inaccessible:
2078
case MediumState_LockedRead:
2082
ComAssertMsgBreak(m->readers != 0, ("Counter overflow"), rc = E_FAIL);
2084
/* Remember pre-lock state */
2085
if (m->state != MediumState_LockedRead)
2086
m->preLockState = m->state;
2088
LogFlowThisFunc(("Okay - prev state=%d readers=%d\n", m->state, m->readers));
2089
m->state = MediumState_LockedRead;
2095
LogFlowThisFunc(("Failing - state=%d\n", m->state));
2096
rc = setStateError();
2105
* @note @a aState may be NULL if the state value is not needed (only for
2106
* in-process calls).
2108
STDMETHODIMP Medium::UnlockRead(MediumState_T *aState)
2110
AutoCaller autoCaller(this);
2111
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2113
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2119
case MediumState_LockedRead:
2121
Assert(m->readers != 0);
2124
/* Reset the state after the last reader */
2125
if (m->readers == 0)
2127
m->state = m->preLockState;
2128
/* There are cases where we inject the deleting state into
2129
* a medium locked for reading. Make sure #unmarkForDeletion()
2130
* gets the right state afterwards. */
2131
if (m->preLockState == MediumState_Deleting)
2132
m->preLockState = MediumState_Created;
2135
LogFlowThisFunc(("new state=%d\n", m->state));
2140
LogFlowThisFunc(("Failing - state=%d\n", m->state));
2141
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
2142
tr("Medium '%s' is not locked for reading"),
2143
m->strLocationFull.c_str());
2148
/* return the current state after */
2156
* @note @a aState may be NULL if the state value is not needed (only for
2157
* in-process calls).
2159
STDMETHODIMP Medium::LockWrite(MediumState_T *aState)
2161
AutoCaller autoCaller(this);
2162
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2164
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2166
/* Wait for a concurrently running queryInfo() to complete */
2167
while (m->queryInfoRunning)
2170
RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
2174
/* return the current state before */
2182
case MediumState_Created:
2183
case MediumState_Inaccessible:
2185
m->preLockState = m->state;
2187
LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
2188
m->state = MediumState_LockedWrite;
2193
LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
2194
rc = setStateError();
2203
* @note @a aState may be NULL if the state value is not needed (only for
2204
* in-process calls).
2206
STDMETHODIMP Medium::UnlockWrite(MediumState_T *aState)
2208
AutoCaller autoCaller(this);
2209
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2211
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2217
case MediumState_LockedWrite:
2219
m->state = m->preLockState;
2220
/* There are cases where we inject the deleting state into
2221
* a medium locked for writing. Make sure #unmarkForDeletion()
2222
* gets the right state afterwards. */
2223
if (m->preLockState == MediumState_Deleting)
2224
m->preLockState = MediumState_Created;
2225
LogFlowThisFunc(("new state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
2230
LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
2231
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
2232
tr("Medium '%s' is not locked for writing"),
2233
m->strLocationFull.c_str());
2238
/* return the current state after */
2245
STDMETHODIMP Medium::Close()
2247
AutoCaller autoCaller(this);
2248
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2250
// make a copy of VirtualBox pointer which gets nulled by uninit()
2251
ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
2253
GuidList llRegistriesThatNeedSaving;
2254
HRESULT rc = close(&llRegistriesThatNeedSaving, autoCaller);
2256
pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
2261
STDMETHODIMP Medium::GetProperty(IN_BSTR aName, BSTR *aValue)
2263
CheckComArgStrNotEmptyOrNull(aName);
2264
CheckComArgOutPointerValid(aValue);
2266
AutoCaller autoCaller(this);
2267
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2269
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2271
settings::StringsMap::const_iterator it = m->mapProperties.find(Utf8Str(aName));
2272
if (it == m->mapProperties.end())
2273
return setError(VBOX_E_OBJECT_NOT_FOUND,
2274
tr("Property '%ls' does not exist"), aName);
2276
it->second.cloneTo(aValue);
2281
STDMETHODIMP Medium::SetProperty(IN_BSTR aName, IN_BSTR aValue)
2283
CheckComArgStrNotEmptyOrNull(aName);
2285
AutoCaller autoCaller(this);
2286
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2288
AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2292
case MediumState_Created:
2293
case MediumState_Inaccessible:
2296
return setStateError();
2299
settings::StringsMap::iterator it = m->mapProperties.find(Utf8Str(aName));
2300
if (it == m->mapProperties.end())
2301
return setError(VBOX_E_OBJECT_NOT_FOUND,
2302
tr("Property '%ls' does not exist"),
2305
it->second = aValue;
2307
// save the settings
2308
GuidList llRegistriesThatNeedSaving;
2309
addToRegistryIDList(llRegistriesThatNeedSaving);
2311
HRESULT rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
2316
STDMETHODIMP Medium::GetProperties(IN_BSTR aNames,
2317
ComSafeArrayOut(BSTR, aReturnNames),
2318
ComSafeArrayOut(BSTR, aReturnValues))
2320
CheckComArgOutSafeArrayPointerValid(aReturnNames);
2321
CheckComArgOutSafeArrayPointerValid(aReturnValues);
2323
AutoCaller autoCaller(this);
2324
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2326
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2328
/// @todo make use of aNames according to the documentation
2331
com::SafeArray<BSTR> names(m->mapProperties.size());
2332
com::SafeArray<BSTR> values(m->mapProperties.size());
2335
for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
2336
it != m->mapProperties.end();
2339
it->first.cloneTo(&names[i]);
2340
it->second.cloneTo(&values[i]);
2344
names.detachTo(ComSafeArrayOutArg(aReturnNames));
2345
values.detachTo(ComSafeArrayOutArg(aReturnValues));
2350
STDMETHODIMP Medium::SetProperties(ComSafeArrayIn(IN_BSTR, aNames),
2351
ComSafeArrayIn(IN_BSTR, aValues))
2353
CheckComArgSafeArrayNotNull(aNames);
2354
CheckComArgSafeArrayNotNull(aValues);
2356
AutoCaller autoCaller(this);
2357
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2359
AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2361
com::SafeArray<IN_BSTR> names(ComSafeArrayInArg(aNames));
2362
com::SafeArray<IN_BSTR> values(ComSafeArrayInArg(aValues));
2364
/* first pass: validate names */
2369
if (m->mapProperties.find(Utf8Str(names[i])) == m->mapProperties.end())
2370
return setError(VBOX_E_OBJECT_NOT_FOUND,
2371
tr("Property '%ls' does not exist"), names[i]);
2374
/* second pass: assign */
2379
settings::StringsMap::iterator it = m->mapProperties.find(Utf8Str(names[i]));
2380
AssertReturn(it != m->mapProperties.end(), E_FAIL);
2382
it->second = Utf8Str(values[i]);
2385
// save the settings
2386
GuidList llRegistriesThatNeedSaving;
2387
addToRegistryIDList(llRegistriesThatNeedSaving);
2389
HRESULT rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
2394
STDMETHODIMP Medium::CreateBaseStorage(LONG64 aLogicalSize,
2396
IProgress **aProgress)
2398
CheckComArgOutPointerValid(aProgress);
2399
if (aLogicalSize < 0)
2400
return setError(E_INVALIDARG, tr("The medium size argument (%lld) is negative"), aLogicalSize);
2402
AutoCaller autoCaller(this);
2403
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2406
ComObjPtr <Progress> pProgress;
2407
Medium::Task *pTask = NULL;
2411
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2413
aVariant = (MediumVariant_T)((unsigned)aVariant & (unsigned)~MediumVariant_Diff);
2414
if ( !(aVariant & MediumVariant_Fixed)
2415
&& !(m->formatObj->getCapabilities() & MediumFormatCapabilities_CreateDynamic))
2416
throw setError(VBOX_E_NOT_SUPPORTED,
2417
tr("Medium format '%s' does not support dynamic storage creation"),
2418
m->strFormat.c_str());
2419
if ( (aVariant & MediumVariant_Fixed)
2420
&& !(m->formatObj->getCapabilities() & MediumFormatCapabilities_CreateDynamic))
2421
throw setError(VBOX_E_NOT_SUPPORTED,
2422
tr("Medium format '%s' does not support fixed storage creation"),
2423
m->strFormat.c_str());
2425
if (m->state != MediumState_NotCreated)
2426
throw setStateError();
2428
pProgress.createObject();
2429
rc = pProgress->init(m->pVirtualBox,
2430
static_cast<IMedium*>(this),
2431
(aVariant & MediumVariant_Fixed)
2432
? BstrFmt(tr("Creating fixed medium storage unit '%s'"), m->strLocationFull.c_str()).raw()
2433
: BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
2434
TRUE /* aCancelable */);
2438
/* setup task object to carry out the operation asynchronously */
2439
pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize,
2440
(MediumVariant_T)aVariant);
2446
m->state = MediumState_Creating;
2448
catch (HRESULT aRC) { rc = aRC; }
2452
rc = startThread(pTask);
2455
pProgress.queryInterfaceTo(aProgress);
2457
else if (pTask != NULL)
2463
STDMETHODIMP Medium::DeleteStorage(IProgress **aProgress)
2465
CheckComArgOutPointerValid(aProgress);
2467
AutoCaller autoCaller(this);
2468
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2470
ComObjPtr<Progress> pProgress;
2472
GuidList llRegistriesThatNeedSaving;
2473
HRESULT rc = deleteStorage(&pProgress,
2475
&llRegistriesThatNeedSaving);
2476
m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
2479
pProgress.queryInterfaceTo(aProgress);
2484
STDMETHODIMP Medium::CreateDiffStorage(IMedium *aTarget,
2486
IProgress **aProgress)
2488
CheckComArgNotNull(aTarget);
2489
CheckComArgOutPointerValid(aProgress);
2491
AutoCaller autoCaller(this);
2492
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2494
ComObjPtr<Medium> diff = static_cast<Medium*>(aTarget);
2496
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2498
if (m->type == MediumType_Writethrough)
2499
return setError(VBOX_E_INVALID_OBJECT_STATE,
2500
tr("Medium type of '%s' is Writethrough"),
2501
m->strLocationFull.c_str());
2502
else if (m->type == MediumType_Shareable)
2503
return setError(VBOX_E_INVALID_OBJECT_STATE,
2504
tr("Medium type of '%s' is Shareable"),
2505
m->strLocationFull.c_str());
2506
else if (m->type == MediumType_Readonly)
2507
return setError(VBOX_E_INVALID_OBJECT_STATE,
2508
tr("Medium type of '%s' is Readonly"),
2509
m->strLocationFull.c_str());
2511
/* Apply the normal locking logic to the entire chain. */
2512
MediumLockList *pMediumLockList(new MediumLockList());
2513
HRESULT rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
2514
true /* fMediumLockWrite */,
2519
delete pMediumLockList;
2523
ComObjPtr <Progress> pProgress;
2525
rc = createDiffStorage(diff, (MediumVariant_T)aVariant, pMediumLockList,
2526
&pProgress, false /* aWait */,
2527
NULL /* pfNeedsGlobalSaveSettings*/);
2529
delete pMediumLockList;
2531
pProgress.queryInterfaceTo(aProgress);
2536
STDMETHODIMP Medium::MergeTo(IMedium *aTarget, IProgress **aProgress)
2538
CheckComArgNotNull(aTarget);
2539
CheckComArgOutPointerValid(aProgress);
2540
ComAssertRet(aTarget != this, E_INVALIDARG);
2542
AutoCaller autoCaller(this);
2543
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2545
ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget);
2547
bool fMergeForward = false;
2548
ComObjPtr<Medium> pParentForTarget;
2549
MediaList childrenToReparent;
2550
MediumLockList *pMediumLockList = NULL;
2554
rc = prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward,
2555
pParentForTarget, childrenToReparent, pMediumLockList);
2556
if (FAILED(rc)) return rc;
2558
ComObjPtr <Progress> pProgress;
2560
rc = mergeTo(pTarget, fMergeForward, pParentForTarget, childrenToReparent,
2561
pMediumLockList, &pProgress, false /* aWait */,
2562
NULL /* pfNeedsGlobalSaveSettings */);
2564
cancelMergeTo(childrenToReparent, pMediumLockList);
2566
pProgress.queryInterfaceTo(aProgress);
2571
STDMETHODIMP Medium::CloneTo(IMedium *aTarget,
2574
IProgress **aProgress)
2576
CheckComArgNotNull(aTarget);
2577
CheckComArgOutPointerValid(aProgress);
2578
ComAssertRet(aTarget != this, E_INVALIDARG);
2580
AutoCaller autoCaller(this);
2581
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2583
ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget);
2584
ComObjPtr<Medium> pParent;
2586
pParent = static_cast<Medium*>(aParent);
2589
ComObjPtr<Progress> pProgress;
2590
Medium::Task *pTask = NULL;
2594
// locking: we need the tree lock first because we access parent pointers
2595
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2596
// and we need to write-lock the media involved
2597
AutoMultiWriteLock3 alock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
2599
if ( pTarget->m->state != MediumState_NotCreated
2600
&& pTarget->m->state != MediumState_Created)
2601
throw pTarget->setStateError();
2603
/* Build the source lock list. */
2604
MediumLockList *pSourceMediumLockList(new MediumLockList());
2605
rc = createMediumLockList(true /* fFailIfInaccessible */,
2606
false /* fMediumLockWrite */,
2608
*pSourceMediumLockList);
2611
delete pSourceMediumLockList;
2615
/* Build the target lock list (including the to-be parent chain). */
2616
MediumLockList *pTargetMediumLockList(new MediumLockList());
2617
rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
2618
true /* fMediumLockWrite */,
2620
*pTargetMediumLockList);
2623
delete pSourceMediumLockList;
2624
delete pTargetMediumLockList;
2628
rc = pSourceMediumLockList->Lock();
2631
delete pSourceMediumLockList;
2632
delete pTargetMediumLockList;
2634
tr("Failed to lock source media '%s'"),
2635
getLocationFull().c_str());
2637
rc = pTargetMediumLockList->Lock();
2640
delete pSourceMediumLockList;
2641
delete pTargetMediumLockList;
2643
tr("Failed to lock target media '%s'"),
2644
pTarget->getLocationFull().c_str());
2647
pProgress.createObject();
2648
rc = pProgress->init(m->pVirtualBox,
2649
static_cast <IMedium *>(this),
2650
BstrFmt(tr("Creating clone medium '%s'"), pTarget->m->strLocationFull.c_str()).raw(),
2651
TRUE /* aCancelable */);
2654
delete pSourceMediumLockList;
2655
delete pTargetMediumLockList;
2659
/* setup task object to carry out the operation asynchronously */
2660
pTask = new Medium::CloneTask(this, pProgress, pTarget,
2661
(MediumVariant_T)aVariant,
2662
pParent, pSourceMediumLockList,
2663
pTargetMediumLockList);
2669
if (pTarget->m->state == MediumState_NotCreated)
2670
pTarget->m->state = MediumState_Creating;
2672
catch (HRESULT aRC) { rc = aRC; }
2676
rc = startThread(pTask);
2679
pProgress.queryInterfaceTo(aProgress);
2681
else if (pTask != NULL)
2687
STDMETHODIMP Medium::Compact(IProgress **aProgress)
2689
CheckComArgOutPointerValid(aProgress);
2691
AutoCaller autoCaller(this);
2692
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2695
ComObjPtr <Progress> pProgress;
2696
Medium::Task *pTask = NULL;
2700
/* We need to lock both the current object, and the tree lock (would
2701
* cause a lock order violation otherwise) for createMediumLockList. */
2702
AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
2704
COMMA_LOCKVAL_SRC_POS);
2706
/* Build the medium lock list. */
2707
MediumLockList *pMediumLockList(new MediumLockList());
2708
rc = createMediumLockList(true /* fFailIfInaccessible */ ,
2709
true /* fMediumLockWrite */,
2714
delete pMediumLockList;
2718
rc = pMediumLockList->Lock();
2721
delete pMediumLockList;
2723
tr("Failed to lock media when compacting '%s'"),
2724
getLocationFull().c_str());
2727
pProgress.createObject();
2728
rc = pProgress->init(m->pVirtualBox,
2729
static_cast <IMedium *>(this),
2730
BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
2731
TRUE /* aCancelable */);
2734
delete pMediumLockList;
2738
/* setup task object to carry out the operation asynchronously */
2739
pTask = new Medium::CompactTask(this, pProgress, pMediumLockList);
2745
catch (HRESULT aRC) { rc = aRC; }
2749
rc = startThread(pTask);
2752
pProgress.queryInterfaceTo(aProgress);
2754
else if (pTask != NULL)
2760
STDMETHODIMP Medium::Resize(LONG64 aLogicalSize, IProgress **aProgress)
2762
CheckComArgOutPointerValid(aProgress);
2764
AutoCaller autoCaller(this);
2765
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2768
ComObjPtr <Progress> pProgress;
2769
Medium::Task *pTask = NULL;
2773
/* We need to lock both the current object, and the tree lock (would
2774
* cause a lock order violation otherwise) for createMediumLockList. */
2775
AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
2777
COMMA_LOCKVAL_SRC_POS);
2779
/* Build the medium lock list. */
2780
MediumLockList *pMediumLockList(new MediumLockList());
2781
rc = createMediumLockList(true /* fFailIfInaccessible */ ,
2782
true /* fMediumLockWrite */,
2787
delete pMediumLockList;
2791
rc = pMediumLockList->Lock();
2794
delete pMediumLockList;
2796
tr("Failed to lock media when compacting '%s'"),
2797
getLocationFull().c_str());
2800
pProgress.createObject();
2801
rc = pProgress->init(m->pVirtualBox,
2802
static_cast <IMedium *>(this),
2803
BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
2804
TRUE /* aCancelable */);
2807
delete pMediumLockList;
2811
/* setup task object to carry out the operation asynchronously */
2812
pTask = new Medium::ResizeTask(this, aLogicalSize, pProgress, pMediumLockList);
2818
catch (HRESULT aRC) { rc = aRC; }
2822
rc = startThread(pTask);
2825
pProgress.queryInterfaceTo(aProgress);
2827
else if (pTask != NULL)
2833
STDMETHODIMP Medium::Reset(IProgress **aProgress)
2835
CheckComArgOutPointerValid(aProgress);
2837
AutoCaller autoCaller(this);
2838
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2841
ComObjPtr <Progress> pProgress;
2842
Medium::Task *pTask = NULL;
2846
/* canClose() needs the tree lock */
2847
AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
2849
COMMA_LOCKVAL_SRC_POS);
2851
LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
2853
if (m->pParent.isNull())
2854
throw setError(VBOX_E_NOT_SUPPORTED,
2855
tr("Medium type of '%s' is not differencing"),
2856
m->strLocationFull.c_str());
2862
/* Build the medium lock list. */
2863
MediumLockList *pMediumLockList(new MediumLockList());
2864
rc = createMediumLockList(true /* fFailIfInaccessible */,
2865
true /* fMediumLockWrite */,
2870
delete pMediumLockList;
2874
rc = pMediumLockList->Lock();
2877
delete pMediumLockList;
2879
tr("Failed to lock media when resetting '%s'"),
2880
getLocationFull().c_str());
2883
pProgress.createObject();
2884
rc = pProgress->init(m->pVirtualBox,
2885
static_cast<IMedium*>(this),
2886
BstrFmt(tr("Resetting differencing medium '%s'"), m->strLocationFull.c_str()).raw(),
2887
FALSE /* aCancelable */);
2891
/* setup task object to carry out the operation asynchronously */
2892
pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
2898
catch (HRESULT aRC) { rc = aRC; }
2902
rc = startThread(pTask);
2905
pProgress.queryInterfaceTo(aProgress);
2909
/* Note: on success, the task will unlock this */
2911
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2912
HRESULT rc2 = UnlockWrite(NULL);
2919
LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
2924
////////////////////////////////////////////////////////////////////////////////
2926
// Medium public internal methods
2928
////////////////////////////////////////////////////////////////////////////////
2931
* Internal method to return the medium's parent medium. Must have caller + locking!
2934
const ComObjPtr<Medium>& Medium::getParent() const
2940
* Internal method to return the medium's list of child media. Must have caller + locking!
2943
const MediaList& Medium::getChildren() const
2945
return m->llChildren;
2949
* Internal method to return the medium's GUID. Must have caller + locking!
2952
const Guid& Medium::getId() const
2958
* Internal method to return the medium's state. Must have caller + locking!
2961
MediumState_T Medium::getState() const
2967
* Internal method to return the medium's variant. Must have caller + locking!
2970
MediumVariant_T Medium::getVariant() const
2976
* Internal method which returns true if this medium represents a host drive.
2979
bool Medium::isHostDrive() const
2981
return m->hostDrive;
2985
* Internal method to return the medium's full location. Must have caller + locking!
2988
const Utf8Str& Medium::getLocationFull() const
2990
return m->strLocationFull;
2994
* Internal method to return the medium's format string. Must have caller + locking!
2997
const Utf8Str& Medium::getFormat() const
2999
return m->strFormat;
3003
* Internal method to return the medium's format object. Must have caller + locking!
3006
const ComObjPtr<MediumFormat>& Medium::getMediumFormat() const
3008
return m->formatObj;
3012
* Internal method that returns true if the medium is represented by a file on the host disk
3013
* (and not iSCSI or something).
3016
bool Medium::isMediumFormatFile() const
3019
&& (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
3026
* Internal method to return the medium's size. Must have caller + locking!
3029
uint64_t Medium::getSize() const
3035
* Returns the medium device type. Must have caller + locking!
3038
DeviceType_T Medium::getDeviceType() const
3044
* Returns the medium type. Must have caller + locking!
3047
MediumType_T Medium::getType() const
3053
* Returns a short version of the location attribute.
3055
* @note Must be called from under this object's read or write lock.
3057
Utf8Str Medium::getName()
3059
Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
3064
* This adds the given UUID to the list of media registries in which this
3065
* medium should be registered. The UUID can either be a machine UUID,
3066
* to add a machine registry, or the global registry UUID as returned by
3067
* VirtualBox::getGlobalRegistryId().
3069
* Note that for hard disks, this method does nothing if the medium is
3070
* already in another registry to avoid having hard disks in more than
3071
* one registry, which causes trouble with keeping diff images in sync.
3072
* See getFirstRegistryMachineId() for details.
3075
* @return true if the registry was added; false if the given id was already on the list.
3077
bool Medium::addRegistry(const Guid& id)
3079
AutoCaller autoCaller(this);
3080
if (FAILED(autoCaller.rc())) return false;
3082
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3084
if ( m->devType == DeviceType_HardDisk
3085
&& m->llRegistryIDs.size() > 0
3089
// no need to add the UUID twice
3090
for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3091
it != m->llRegistryIDs.end();
3098
m->llRegistryIDs.push_back(id);
3103
* Returns true if id is in the list of media registries for this medium.
3107
bool Medium::isInRegistry(const Guid& id)
3109
AutoCaller autoCaller(this);
3110
if (FAILED(autoCaller.rc())) return false;
3112
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3114
for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3115
it != m->llRegistryIDs.end();
3126
* Internal method to return the medium's first registry machine (i.e. the machine in whose
3127
* machine XML this medium is listed).
3129
* Every medium must now (4.0) reside in at least one media registry, which is identified by
3130
* a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
3131
* machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
3132
* object if the machine is old and still needs the global registry in VirtualBox.xml.
3134
* By definition, hard disks may only be in one media registry, in which all its children
3135
* will be stored as well. Otherwise we run into problems with having keep multiple registries
3136
* in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
3137
* case, only VM2's registry is used for the disk in question.)
3139
* ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
3142
* Must have caller + locking!
3146
const Guid& Medium::getFirstRegistryMachineId() const
3148
return m->llRegistryIDs.front();
3152
* Adds all the IDs of the registries in which this medium is registered to the given list
3153
* of UUIDs, but only if they are not on the list yet.
3154
* @param llRegistryIDs
3156
HRESULT Medium::addToRegistryIDList(GuidList &llRegistryIDs)
3158
AutoCaller autoCaller(this);
3159
if (FAILED(autoCaller.rc())) return false;
3161
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3163
for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3164
it != m->llRegistryIDs.end();
3167
m->pVirtualBox->addGuidToListUniquely(llRegistryIDs, *it);
3174
* Adds the given machine and optionally the snapshot to the list of the objects
3175
* this medium is attached to.
3177
* @param aMachineId Machine ID.
3178
* @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
3180
HRESULT Medium::addBackReference(const Guid &aMachineId,
3181
const Guid &aSnapshotId /*= Guid::Empty*/)
3183
AssertReturn(!aMachineId.isEmpty(), E_FAIL);
3185
LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
3187
AutoCaller autoCaller(this);
3188
AssertComRCReturnRC(autoCaller.rc());
3190
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3194
case MediumState_Created:
3195
case MediumState_Inaccessible:
3196
case MediumState_LockedRead:
3197
case MediumState_LockedWrite:
3201
return setStateError();
3204
if (m->numCreateDiffTasks > 0)
3205
return setError(VBOX_E_OBJECT_IN_USE,
3206
tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created"),
3207
m->strLocationFull.c_str(),
3209
m->numCreateDiffTasks);
3211
BackRefList::iterator it = std::find_if(m->backRefs.begin(),
3213
BackRef::EqualsTo(aMachineId));
3214
if (it == m->backRefs.end())
3216
BackRef ref(aMachineId, aSnapshotId);
3217
m->backRefs.push_back(ref);
3222
// if the caller has not supplied a snapshot ID, then we're attaching
3223
// to a machine a medium which represents the machine's current state,
3225
if (aSnapshotId.isEmpty())
3227
/* sanity: no duplicate attachments */
3228
AssertReturn(!it->fInCurState, E_FAIL);
3229
it->fInCurState = true;
3234
// otherwise: a snapshot medium is being attached
3236
/* sanity: no duplicate attachments */
3237
for (GuidList::const_iterator jt = it->llSnapshotIds.begin();
3238
jt != it->llSnapshotIds.end();
3241
const Guid &idOldSnapshot = *jt;
3243
if (idOldSnapshot == aSnapshotId)
3248
return setError(VBOX_E_OBJECT_IN_USE,
3249
tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
3250
m->strLocationFull.c_str(),
3253
idOldSnapshot.raw());
3257
it->llSnapshotIds.push_back(aSnapshotId);
3258
it->fInCurState = false;
3260
LogFlowThisFuncLeave();
3266
* Removes the given machine and optionally the snapshot from the list of the
3267
* objects this medium is attached to.
3269
* @param aMachineId Machine ID.
3270
* @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
3273
HRESULT Medium::removeBackReference(const Guid &aMachineId,
3274
const Guid &aSnapshotId /*= Guid::Empty*/)
3276
AssertReturn(!aMachineId.isEmpty(), E_FAIL);
3278
AutoCaller autoCaller(this);
3279
AssertComRCReturnRC(autoCaller.rc());
3281
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3283
BackRefList::iterator it =
3284
std::find_if(m->backRefs.begin(), m->backRefs.end(),
3285
BackRef::EqualsTo(aMachineId));
3286
AssertReturn(it != m->backRefs.end(), E_FAIL);
3288
if (aSnapshotId.isEmpty())
3290
/* remove the current state attachment */
3291
it->fInCurState = false;
3295
/* remove the snapshot attachment */
3296
GuidList::iterator jt = std::find(it->llSnapshotIds.begin(),
3297
it->llSnapshotIds.end(),
3300
AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
3301
it->llSnapshotIds.erase(jt);
3304
/* if the backref becomes empty, remove it */
3305
if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
3306
m->backRefs.erase(it);
3312
* Internal method to return the medium's list of backrefs. Must have caller + locking!
3315
const Guid* Medium::getFirstMachineBackrefId() const
3317
if (!m->backRefs.size())
3320
return &m->backRefs.front().machineId;
3323
const Guid* Medium::getFirstMachineBackrefSnapshotId() const
3325
if (!m->backRefs.size())
3328
const BackRef &ref = m->backRefs.front();
3329
if (!ref.llSnapshotIds.size())
3332
return &ref.llSnapshotIds.front();
3337
* Debugging helper that gets called after VirtualBox initialization that writes all
3338
* machine backreferences to the debug log.
3340
void Medium::dumpBackRefs()
3342
AutoCaller autoCaller(this);
3343
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3345
LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
3347
for (BackRefList::iterator it2 = m->backRefs.begin();
3348
it2 != m->backRefs.end();
3351
const BackRef &ref = *it2;
3352
LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d)\n", ref.machineId.raw(), ref.fInCurState));
3354
for (GuidList::const_iterator jt2 = it2->llSnapshotIds.begin();
3355
jt2 != it2->llSnapshotIds.end();
3358
const Guid &id = *jt2;
3359
LogFlowThisFunc((" Backref from snapshot {%RTuuid}\n", id.raw()));
3366
* Checks if the given change of \a aOldPath to \a aNewPath affects the location
3367
* of this media and updates it if necessary to reflect the new location.
3369
* @param aOldPath Old path (full).
3370
* @param aNewPath New path (full).
3372
* @note Locks this object for writing.
3374
HRESULT Medium::updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
3376
AssertReturn(!strOldPath.isEmpty(), E_FAIL);
3377
AssertReturn(!strNewPath.isEmpty(), E_FAIL);
3379
AutoCaller autoCaller(this);
3380
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3382
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3384
LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
3386
const char *pcszMediumPath = m->strLocationFull.c_str();
3388
if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
3390
Utf8Str newPath(strNewPath);
3391
newPath.append(pcszMediumPath + strOldPath.length());
3392
unconst(m->strLocationFull) = newPath;
3394
LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
3401
* Returns the base medium of the media chain this medium is part of.
3403
* The base medium is found by walking up the parent-child relationship axis.
3404
* If the medium doesn't have a parent (i.e. it's a base medium), it
3405
* returns itself in response to this method.
3407
* @param aLevel Where to store the number of ancestors of this medium
3408
* (zero for the base), may be @c NULL.
3410
* @note Locks medium tree for reading.
3412
ComObjPtr<Medium> Medium::getBase(uint32_t *aLevel /*= NULL*/)
3414
ComObjPtr<Medium> pBase;
3417
AutoCaller autoCaller(this);
3418
AssertReturn(autoCaller.isOk(), pBase);
3420
/* we access mParent */
3421
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3430
AutoCaller baseCaller(pBase);
3431
AssertReturn(baseCaller.isOk(), pBase);
3433
if (pBase->m->pParent.isNull())
3436
pBase = pBase->m->pParent;
3448
* Returns @c true if this medium cannot be modified because it has
3449
* dependents (children) or is part of the snapshot. Related to the medium
3450
* type and posterity, not to the current media state.
3452
* @note Locks this object and medium tree for reading.
3454
bool Medium::isReadOnly()
3456
AutoCaller autoCaller(this);
3457
AssertComRCReturn(autoCaller.rc(), false);
3459
/* we access children */
3460
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3462
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3466
case MediumType_Normal:
3468
if (getChildren().size() != 0)
3471
for (BackRefList::const_iterator it = m->backRefs.begin();
3472
it != m->backRefs.end(); ++it)
3473
if (it->llSnapshotIds.size() != 0)
3478
case MediumType_Immutable:
3479
case MediumType_MultiAttach:
3481
case MediumType_Writethrough:
3482
case MediumType_Shareable:
3483
case MediumType_Readonly: /* explicit readonly media has no diffs */
3489
AssertFailedReturn(false);
3493
* Saves medium data by appending a new child node to the given
3494
* parent XML settings node.
3496
* @param data Settings struct to be updated.
3497
* @param strHardDiskFolder Folder for which paths should be relative.
3499
* @note Locks this object, medium tree and children for reading.
3501
HRESULT Medium::saveSettings(settings::Medium &data,
3502
const Utf8Str &strHardDiskFolder)
3504
AutoCaller autoCaller(this);
3505
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3507
/* we access mParent */
3508
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3510
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3514
// make path relative if needed
3515
if ( !strHardDiskFolder.isEmpty()
3516
&& RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
3518
data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
3520
data.strLocation = m->strLocationFull;
3521
data.strFormat = m->strFormat;
3523
/* optional, only for diffs, default is false */
3525
data.fAutoReset = m->autoReset;
3527
data.fAutoReset = false;
3530
data.strDescription = m->strDescription;
3532
/* optional properties */
3533
data.properties.clear();
3534
for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
3535
it != m->mapProperties.end();
3538
/* only save properties that have non-default values */
3539
if (!it->second.isEmpty())
3541
const Utf8Str &name = it->first;
3542
const Utf8Str &value = it->second;
3543
data.properties[name] = value;
3547
/* only for base media */
3548
if (m->pParent.isNull())
3549
data.hdType = m->type;
3551
/* save all children */
3552
for (MediaList::const_iterator it = getChildren().begin();
3553
it != getChildren().end();
3556
settings::Medium med;
3557
HRESULT rc = (*it)->saveSettings(med, strHardDiskFolder);
3558
AssertComRCReturnRC(rc);
3559
data.llChildren.push_back(med);
3566
* Constructs a medium lock list for this medium. The lock is not taken.
3568
* @note Locks the medium tree for reading.
3570
* @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
3571
* inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
3572
* this is necessary for a VM's removable media VM startup for which we do not want to fail.
3573
* @param fMediumLockWrite Whether to associate a write lock with this medium.
3574
* @param pToBeParent Medium which will become the parent of this medium.
3575
* @param mediumLockList Where to store the resulting list.
3577
HRESULT Medium::createMediumLockList(bool fFailIfInaccessible,
3578
bool fMediumLockWrite,
3579
Medium *pToBeParent,
3580
MediumLockList &mediumLockList)
3582
AutoCaller autoCaller(this);
3583
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3587
/* we access parent medium objects */
3588
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3590
/* paranoid sanity checking if the medium has a to-be parent medium */
3593
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3594
ComAssertRet(getParent().isNull(), E_FAIL);
3595
ComAssertRet(getChildren().size() == 0, E_FAIL);
3598
ErrorInfoKeeper eik;
3599
MultiResult mrc(S_OK);
3601
ComObjPtr<Medium> pMedium = this;
3602
while (!pMedium.isNull())
3604
// need write lock for RefreshState if medium is inaccessible
3605
AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
3607
/* Accessibility check must be first, otherwise locking interferes
3608
* with getting the medium state. Lock lists are not created for
3609
* fun, and thus getting the medium status is no luxury. */
3610
MediumState_T mediumState = pMedium->getState();
3611
if (mediumState == MediumState_Inaccessible)
3613
rc = pMedium->RefreshState(&mediumState);
3614
if (FAILED(rc)) return rc;
3616
if (mediumState == MediumState_Inaccessible)
3618
// ignore inaccessible ISO media and silently return S_OK,
3619
// otherwise VM startup (esp. restore) may fail without good reason
3620
if (!fFailIfInaccessible)
3623
// otherwise report an error
3625
rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
3626
if (FAILED(rc)) return rc;
3628
/* collect multiple errors */
3630
Assert(!error.isEmpty());
3631
mrc = setError(E_FAIL,
3634
// error message will be something like
3635
// "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
3640
if (pMedium == this)
3641
mediumLockList.Prepend(pMedium, fMediumLockWrite);
3643
mediumLockList.Prepend(pMedium, false);
3645
pMedium = pMedium->getParent();
3646
if (pMedium.isNull() && pToBeParent)
3648
pMedium = pToBeParent;
3657
* Creates a new differencing storage unit using the format of the given target
3658
* medium and the location. Note that @c aTarget must be NotCreated.
3660
* The @a aMediumLockList parameter contains the associated medium lock list,
3661
* which must be in locked state. If @a aWait is @c true then the caller is
3662
* responsible for unlocking.
3664
* If @a aProgress is not NULL but the object it points to is @c null then a
3665
* new progress object will be created and assigned to @a *aProgress on
3666
* success, otherwise the existing progress object is used. If @a aProgress is
3667
* NULL, then no progress object is created/used at all.
3669
* When @a aWait is @c false, this method will create a thread to perform the
3670
* create operation asynchronously and will return immediately. Otherwise, it
3671
* will perform the operation on the calling thread and will not return to the
3672
* caller until the operation is completed. Note that @a aProgress cannot be
3673
* NULL when @a aWait is @c false (this method will assert in this case).
3675
* @param aTarget Target medium.
3676
* @param aVariant Precise medium variant to create.
3677
* @param aMediumLockList List of media which should be locked.
3678
* @param aProgress Where to find/store a Progress object to track
3679
* operation completion.
3680
* @param aWait @c true if this method should block instead of
3681
* creating an asynchronous thread.
3682
* @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs that will receive the registry IDs that need saving.
3683
* This only works in "wait" mode; otherwise saveSettings is called automatically by the thread that
3684
* was created, and this parameter is ignored.
3686
* @note Locks this object and @a aTarget for writing.
3688
HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
3689
MediumVariant_T aVariant,
3690
MediumLockList *aMediumLockList,
3691
ComObjPtr<Progress> *aProgress,
3693
GuidList *pllRegistriesThatNeedSaving)
3695
AssertReturn(!aTarget.isNull(), E_FAIL);
3696
AssertReturn(aMediumLockList, E_FAIL);
3697
AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
3699
AutoCaller autoCaller(this);
3700
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3702
AutoCaller targetCaller(aTarget);
3703
if (FAILED(targetCaller.rc())) return targetCaller.rc();
3706
ComObjPtr<Progress> pProgress;
3707
Medium::Task *pTask = NULL;
3711
AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
3713
ComAssertThrow( m->type != MediumType_Writethrough
3714
&& m->type != MediumType_Shareable
3715
&& m->type != MediumType_Readonly, E_FAIL);
3716
ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
3718
if (aTarget->m->state != MediumState_NotCreated)
3719
throw aTarget->setStateError();
3721
/* Check that the medium is not attached to the current state of
3722
* any VM referring to it. */
3723
for (BackRefList::const_iterator it = m->backRefs.begin();
3724
it != m->backRefs.end();
3727
if (it->fInCurState)
3729
/* Note: when a VM snapshot is being taken, all normal media
3730
* attached to the VM in the current state will be, as an
3731
* exception, also associated with the snapshot which is about
3732
* to create (see SnapshotMachine::init()) before deassociating
3733
* them from the current state (which takes place only on
3734
* success in Machine::fixupHardDisks()), so that the size of
3735
* snapshotIds will be 1 in this case. The extra condition is
3736
* used to filter out this legal situation. */
3737
if (it->llSnapshotIds.size() == 0)
3738
throw setError(VBOX_E_INVALID_OBJECT_STATE,
3739
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"),
3740
m->strLocationFull.c_str(), it->machineId.raw());
3742
Assert(it->llSnapshotIds.size() == 1);
3746
if (aProgress != NULL)
3748
/* use the existing progress object... */
3749
pProgress = *aProgress;
3751
/* ...but create a new one if it is null */
3752
if (pProgress.isNull())
3754
pProgress.createObject();
3755
rc = pProgress->init(m->pVirtualBox,
3756
static_cast<IMedium*>(this),
3757
BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
3758
TRUE /* aCancelable */);
3764
/* setup task object to carry out the operation sync/async */
3765
pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
3767
aWait /* fKeepMediumLockList */);
3773
/* register a task (it will deregister itself when done) */
3774
++m->numCreateDiffTasks;
3775
Assert(m->numCreateDiffTasks != 0); /* overflow? */
3777
aTarget->m->state = MediumState_Creating;
3779
catch (HRESULT aRC) { rc = aRC; }
3784
rc = runNow(pTask, pllRegistriesThatNeedSaving);
3786
rc = startThread(pTask);
3788
if (SUCCEEDED(rc) && aProgress != NULL)
3789
*aProgress = pProgress;
3791
else if (pTask != NULL)
3798
* Returns a preferred format for differencing media.
3800
Utf8Str Medium::getPreferredDiffFormat()
3802
AutoCaller autoCaller(this);
3803
AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
3805
/* check that our own format supports diffs */
3806
if (!(m->formatObj->getCapabilities() & MediumFormatCapabilities_Differencing))
3808
/* use the default format if not */
3810
m->pVirtualBox->getDefaultHardDiskFormat(tmp);
3814
/* m->strFormat is const, no need to lock */
3815
return m->strFormat;
3819
* Implementation for the public Medium::Close() with the exception of calling
3820
* VirtualBox::saveSettings(), in case someone wants to call this for several
3823
* After this returns with success, uninit() has been called on the medium, and
3824
* the object is no longer usable ("not ready" state).
3826
* @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs that will receive the registry IDs that need saving.
3827
* @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here
3828
* upon which the Medium instance gets uninitialized.
3831
HRESULT Medium::close(GuidList *pllRegistriesThatNeedSaving,
3832
AutoCaller &autoCaller)
3834
// we're accessing parent/child and backrefs, so lock the tree first, then ourselves
3835
AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
3837
COMMA_LOCKVAL_SRC_POS);
3839
LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str()));
3841
bool wasCreated = true;
3845
case MediumState_NotCreated:
3848
case MediumState_Created:
3849
case MediumState_Inaccessible:
3852
return setStateError();
3855
if (m->backRefs.size() != 0)
3856
return setError(VBOX_E_OBJECT_IN_USE,
3857
tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
3858
m->strLocationFull.c_str(), m->backRefs.size());
3860
// perform extra media-dependent close checks
3861
HRESULT rc = canClose();
3862
if (FAILED(rc)) return rc;
3866
// remove from the list of known media before performing actual
3867
// uninitialization (to keep the media registry consistent on
3868
// failure to do so)
3869
rc = unregisterWithVirtualBox(pllRegistriesThatNeedSaving);
3870
if (FAILED(rc)) return rc;
3873
// leave the AutoCaller, as otherwise uninit() will simply hang
3874
autoCaller.release();
3876
// Keep the locks held until after uninit, as otherwise the consistency
3877
// of the medium tree cannot be guaranteed.
3886
* Deletes the medium storage unit.
3888
* If @a aProgress is not NULL but the object it points to is @c null then a new
3889
* progress object will be created and assigned to @a *aProgress on success,
3890
* otherwise the existing progress object is used. If Progress is NULL, then no
3891
* progress object is created/used at all.
3893
* When @a aWait is @c false, this method will create a thread to perform the
3894
* delete operation asynchronously and will return immediately. Otherwise, it
3895
* will perform the operation on the calling thread and will not return to the
3896
* caller until the operation is completed. Note that @a aProgress cannot be
3897
* NULL when @a aWait is @c false (this method will assert in this case).
3899
* @param aProgress Where to find/store a Progress object to track operation
3901
* @param aWait @c true if this method should block instead of creating
3902
* an asynchronous thread.
3903
* @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3904
* by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3905
* This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
3906
* and this parameter is ignored.
3908
* @note Locks mVirtualBox and this object for writing. Locks medium tree for
3911
HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
3913
GuidList *pllRegistriesThatNeedSaving)
3915
AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
3917
AutoCaller autoCaller(this);
3918
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3921
ComObjPtr<Progress> pProgress;
3922
Medium::Task *pTask = NULL;
3926
/* we're accessing the media tree, and canClose() needs it too */
3927
AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
3929
COMMA_LOCKVAL_SRC_POS);
3930
LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
3932
if ( !(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
3933
| MediumFormatCapabilities_CreateFixed)))
3934
throw setError(VBOX_E_NOT_SUPPORTED,
3935
tr("Medium format '%s' does not support storage deletion"),
3936
m->strFormat.c_str());
3938
/* Note that we are fine with Inaccessible state too: a) for symmetry
3939
* with create calls and b) because it doesn't really harm to try, if
3940
* it is really inaccessible, the delete operation will fail anyway.
3941
* Accepting Inaccessible state is especially important because all
3942
* registered media are initially Inaccessible upon VBoxSVC startup
3943
* until COMGETTER(RefreshState) is called. Accept Deleting state
3944
* because some callers need to put the medium in this state early
3945
* to prevent races. */
3948
case MediumState_Created:
3949
case MediumState_Deleting:
3950
case MediumState_Inaccessible:
3953
throw setStateError();
3956
if (m->backRefs.size() != 0)
3958
Utf8Str strMachines;
3959
for (BackRefList::const_iterator it = m->backRefs.begin();
3960
it != m->backRefs.end();
3963
const BackRef &b = *it;
3964
if (strMachines.length())
3965
strMachines.append(", ");
3966
strMachines.append(b.machineId.toString().c_str());
3971
throw setError(VBOX_E_OBJECT_IN_USE,
3972
tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
3973
m->strLocationFull.c_str(),
3975
strMachines.c_str());
3982
/* go to Deleting state, so that the medium is not actually locked */
3983
if (m->state != MediumState_Deleting)
3985
rc = markForDeletion();
3990
/* Build the medium lock list. */
3991
MediumLockList *pMediumLockList(new MediumLockList());
3992
rc = createMediumLockList(true /* fFailIfInaccessible */,
3993
true /* fMediumLockWrite */,
3998
delete pMediumLockList;
4002
rc = pMediumLockList->Lock();
4005
delete pMediumLockList;
4007
tr("Failed to lock media when deleting '%s'"),
4008
getLocationFull().c_str());
4011
/* try to remove from the list of known media before performing
4012
* actual deletion (we favor the consistency of the media registry
4013
* which would have been broken if unregisterWithVirtualBox() failed
4014
* after we successfully deleted the storage) */
4015
rc = unregisterWithVirtualBox(pllRegistriesThatNeedSaving);
4018
// no longer need lock
4019
multilock.release();
4021
if (aProgress != NULL)
4023
/* use the existing progress object... */
4024
pProgress = *aProgress;
4026
/* ...but create a new one if it is null */
4027
if (pProgress.isNull())
4029
pProgress.createObject();
4030
rc = pProgress->init(m->pVirtualBox,
4031
static_cast<IMedium*>(this),
4032
BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
4033
FALSE /* aCancelable */);
4039
/* setup task object to carry out the operation sync/async */
4040
pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
4046
catch (HRESULT aRC) { rc = aRC; }
4051
rc = runNow(pTask, NULL /* pfNeedsGlobalSaveSettings*/);
4053
rc = startThread(pTask);
4055
if (SUCCEEDED(rc) && aProgress != NULL)
4056
*aProgress = pProgress;
4064
/* Undo deleting state if necessary. */
4065
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4066
unmarkForDeletion();
4073
* Mark a medium for deletion.
4075
* @note Caller must hold the write lock on this medium!
4077
HRESULT Medium::markForDeletion()
4079
ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4082
case MediumState_Created:
4083
case MediumState_Inaccessible:
4084
m->preLockState = m->state;
4085
m->state = MediumState_Deleting;
4088
return setStateError();
4093
* Removes the "mark for deletion".
4095
* @note Caller must hold the write lock on this medium!
4097
HRESULT Medium::unmarkForDeletion()
4099
ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4102
case MediumState_Deleting:
4103
m->state = m->preLockState;
4106
return setStateError();
4111
* Mark a medium for deletion which is in locked state.
4113
* @note Caller must hold the write lock on this medium!
4115
HRESULT Medium::markLockedForDeletion()
4117
ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4118
if ( ( m->state == MediumState_LockedRead
4119
|| m->state == MediumState_LockedWrite)
4120
&& m->preLockState == MediumState_Created)
4122
m->preLockState = MediumState_Deleting;
4126
return setStateError();
4130
* Removes the "mark for deletion" for a medium in locked state.
4132
* @note Caller must hold the write lock on this medium!
4134
HRESULT Medium::unmarkLockedForDeletion()
4136
ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4137
if ( ( m->state == MediumState_LockedRead
4138
|| m->state == MediumState_LockedWrite)
4139
&& m->preLockState == MediumState_Deleting)
4141
m->preLockState = MediumState_Created;
4145
return setStateError();
4149
* Prepares this (source) medium, target medium and all intermediate media
4150
* for the merge operation.
4152
* This method is to be called prior to calling the #mergeTo() to perform
4153
* necessary consistency checks and place involved media to appropriate
4154
* states. If #mergeTo() is not called or fails, the state modifications
4155
* performed by this method must be undone by #cancelMergeTo().
4157
* See #mergeTo() for more information about merging.
4159
* @param pTarget Target medium.
4160
* @param aMachineId Allowed machine attachment. NULL means do not check.
4161
* @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
4163
* @param fLockMedia Flag whether to lock the medium lock list or not.
4164
* If set to false and the medium lock list locking fails
4165
* later you must call #cancelMergeTo().
4166
* @param fMergeForward Resulting merge direction (out).
4167
* @param pParentForTarget New parent for target medium after merge (out).
4168
* @param aChildrenToReparent List of children of the source which will have
4169
* to be reparented to the target after merge (out).
4170
* @param aMediumLockList Medium locking information (out).
4172
* @note Locks medium tree for reading. Locks this object, aTarget and all
4173
* intermediate media for writing.
4175
HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
4176
const Guid *aMachineId,
4177
const Guid *aSnapshotId,
4179
bool &fMergeForward,
4180
ComObjPtr<Medium> &pParentForTarget,
4181
MediaList &aChildrenToReparent,
4182
MediumLockList * &aMediumLockList)
4184
AssertReturn(pTarget != NULL, E_FAIL);
4185
AssertReturn(pTarget != this, E_FAIL);
4187
AutoCaller autoCaller(this);
4188
AssertComRCReturnRC(autoCaller.rc());
4190
AutoCaller targetCaller(pTarget);
4191
AssertComRCReturnRC(targetCaller.rc());
4194
fMergeForward = false;
4195
pParentForTarget.setNull();
4196
aChildrenToReparent.clear();
4197
Assert(aMediumLockList == NULL);
4198
aMediumLockList = NULL;
4202
// locking: we need the tree lock first because we access parent pointers
4203
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4205
/* more sanity checking and figuring out the merge direction */
4206
ComObjPtr<Medium> pMedium = getParent();
4207
while (!pMedium.isNull() && pMedium != pTarget)
4208
pMedium = pMedium->getParent();
4209
if (pMedium == pTarget)
4210
fMergeForward = false;
4213
pMedium = pTarget->getParent();
4214
while (!pMedium.isNull() && pMedium != this)
4215
pMedium = pMedium->getParent();
4216
if (pMedium == this)
4217
fMergeForward = true;
4222
AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4223
tgtLoc = pTarget->getLocationFull();
4226
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4227
throw setError(VBOX_E_INVALID_OBJECT_STATE,
4228
tr("Media '%s' and '%s' are unrelated"),
4229
m->strLocationFull.c_str(), tgtLoc.c_str());
4233
/* Build the lock list. */
4234
aMediumLockList = new MediumLockList();
4236
rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
4237
true /* fMediumLockWrite */,
4241
rc = createMediumLockList(true /* fFailIfInaccessible */,
4242
false /* fMediumLockWrite */,
4248
/* Sanity checking, must be after lock list creation as it depends on
4249
* valid medium states. The medium objects must be accessible. Only
4250
* do this if immediate locking is requested, otherwise it fails when
4251
* we construct a medium lock list for an already running VM. Snapshot
4252
* deletion uses this to simplify its life. */
4256
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4257
if (m->state != MediumState_Created)
4258
throw setStateError();
4261
AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4262
if (pTarget->m->state != MediumState_Created)
4263
throw pTarget->setStateError();
4267
/* check medium attachment and other sanity conditions */
4270
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4271
if (getChildren().size() > 1)
4273
throw setError(VBOX_E_INVALID_OBJECT_STATE,
4274
tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4275
m->strLocationFull.c_str(), getChildren().size());
4277
/* One backreference is only allowed if the machine ID is not empty
4278
* and it matches the machine the medium is attached to (including
4279
* the snapshot ID if not empty). */
4280
if ( m->backRefs.size() != 0
4282
|| m->backRefs.size() != 1
4283
|| aMachineId->isEmpty()
4284
|| *getFirstMachineBackrefId() != *aMachineId
4285
|| ( (!aSnapshotId || !aSnapshotId->isEmpty())
4286
&& *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
4287
throw setError(VBOX_E_OBJECT_IN_USE,
4288
tr("Medium '%s' is attached to %d virtual machines"),
4289
m->strLocationFull.c_str(), m->backRefs.size());
4290
if (m->type == MediumType_Immutable)
4291
throw setError(VBOX_E_INVALID_OBJECT_STATE,
4292
tr("Medium '%s' is immutable"),
4293
m->strLocationFull.c_str());
4294
if (m->type == MediumType_MultiAttach)
4295
throw setError(VBOX_E_INVALID_OBJECT_STATE,
4296
tr("Medium '%s' is multi-attach"),
4297
m->strLocationFull.c_str());
4301
AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4302
if (pTarget->getChildren().size() > 1)
4304
throw setError(VBOX_E_OBJECT_IN_USE,
4305
tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4306
pTarget->m->strLocationFull.c_str(),
4307
pTarget->getChildren().size());
4309
if (pTarget->m->type == MediumType_Immutable)
4310
throw setError(VBOX_E_INVALID_OBJECT_STATE,
4311
tr("Medium '%s' is immutable"),
4312
pTarget->m->strLocationFull.c_str());
4313
if (pTarget->m->type == MediumType_MultiAttach)
4314
throw setError(VBOX_E_INVALID_OBJECT_STATE,
4315
tr("Medium '%s' is multi-attach"),
4316
pTarget->m->strLocationFull.c_str());
4318
ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
4319
ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
4320
for (pLast = pLastIntermediate;
4321
!pLast.isNull() && pLast != pTarget && pLast != this;
4322
pLast = pLast->getParent())
4324
AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4325
if (pLast->getChildren().size() > 1)
4327
throw setError(VBOX_E_OBJECT_IN_USE,
4328
tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4329
pLast->m->strLocationFull.c_str(),
4330
pLast->getChildren().size());
4332
if (pLast->m->backRefs.size() != 0)
4333
throw setError(VBOX_E_OBJECT_IN_USE,
4334
tr("Medium '%s' is attached to %d virtual machines"),
4335
pLast->m->strLocationFull.c_str(),
4336
pLast->m->backRefs.size());
4340
/* Update medium states appropriately */
4341
if (m->state == MediumState_Created)
4343
rc = markForDeletion();
4350
throw setStateError();
4351
else if ( m->state == MediumState_LockedWrite
4352
|| m->state == MediumState_LockedRead)
4354
/* Either mark it for deletion in locked state or allow
4355
* others to have done so. */
4356
if (m->preLockState == MediumState_Created)
4357
markLockedForDeletion();
4358
else if (m->preLockState != MediumState_Deleting)
4359
throw setStateError();
4362
throw setStateError();
4367
/* we will need parent to reparent target */
4368
pParentForTarget = m->pParent;
4372
/* we will need to reparent children of the source */
4373
for (MediaList::const_iterator it = getChildren().begin();
4374
it != getChildren().end();
4380
rc = pMedium->LockWrite(NULL);
4385
aChildrenToReparent.push_back(pMedium);
4388
for (pLast = pLastIntermediate;
4389
!pLast.isNull() && pLast != pTarget && pLast != this;
4390
pLast = pLast->getParent())
4392
AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4393
if (pLast->m->state == MediumState_Created)
4395
rc = pLast->markForDeletion();
4400
throw pLast->setStateError();
4403
/* Tweak the lock list in the backward merge case, as the target
4404
* isn't marked to be locked for writing yet. */
4407
MediumLockList::Base::iterator lockListBegin =
4408
aMediumLockList->GetBegin();
4409
MediumLockList::Base::iterator lockListEnd =
4410
aMediumLockList->GetEnd();
4412
for (MediumLockList::Base::iterator it = lockListBegin;
4416
MediumLock &mediumLock = *it;
4417
if (mediumLock.GetMedium() == pTarget)
4419
HRESULT rc2 = mediumLock.UpdateLock(true);
4428
rc = aMediumLockList->Lock();
4431
AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4433
tr("Failed to lock media when merging to '%s'"),
4434
pTarget->getLocationFull().c_str());
4438
catch (HRESULT aRC) { rc = aRC; }
4442
delete aMediumLockList;
4443
aMediumLockList = NULL;
4450
* Merges this medium to the specified medium which must be either its
4451
* direct ancestor or descendant.
4453
* Given this medium is SOURCE and the specified medium is TARGET, we will
4454
* get two variants of the merge operation:
4457
* ------------------------->
4458
* [Extra] <- SOURCE <- Intermediate <- TARGET
4459
* Any Del Del LockWr
4463
* <-------------------------
4464
* TARGET <- Intermediate <- SOURCE <- [Extra]
4465
* LockWr Del Del LockWr
4467
* Each diagram shows the involved media on the media chain where
4468
* SOURCE and TARGET belong. Under each medium there is a state value which
4469
* the medium must have at a time of the mergeTo() call.
4471
* The media in the square braces may be absent (e.g. when the forward
4472
* operation takes place and SOURCE is the base medium, or when the backward
4473
* merge operation takes place and TARGET is the last child in the chain) but if
4474
* they present they are involved too as shown.
4476
* Neither the source medium nor intermediate media may be attached to
4477
* any VM directly or in the snapshot, otherwise this method will assert.
4479
* The #prepareMergeTo() method must be called prior to this method to place all
4480
* involved to necessary states and perform other consistency checks.
4482
* If @a aWait is @c true then this method will perform the operation on the
4483
* calling thread and will not return to the caller until the operation is
4484
* completed. When this method succeeds, all intermediate medium objects in
4485
* the chain will be uninitialized, the state of the target medium (and all
4486
* involved extra media) will be restored. @a aMediumLockList will not be
4487
* deleted, whether the operation is successful or not. The caller has to do
4488
* this if appropriate. Note that this (source) medium is not uninitialized
4489
* because of possible AutoCaller instances held by the caller of this method
4490
* on the current thread. It's therefore the responsibility of the caller to
4491
* call Medium::uninit() after releasing all callers.
4493
* If @a aWait is @c false then this method will create a thread to perform the
4494
* operation asynchronously and will return immediately. If the operation
4495
* succeeds, the thread will uninitialize the source medium object and all
4496
* intermediate medium objects in the chain, reset the state of the target
4497
* medium (and all involved extra media) and delete @a aMediumLockList.
4498
* If the operation fails, the thread will only reset the states of all
4499
* involved media and delete @a aMediumLockList.
4501
* When this method fails (regardless of the @a aWait mode), it is a caller's
4502
* responsibility to undo state changes and delete @a aMediumLockList using
4505
* If @a aProgress is not NULL but the object it points to is @c null then a new
4506
* progress object will be created and assigned to @a *aProgress on success,
4507
* otherwise the existing progress object is used. If Progress is NULL, then no
4508
* progress object is created/used at all. Note that @a aProgress cannot be
4509
* NULL when @a aWait is @c false (this method will assert in this case).
4511
* @param pTarget Target medium.
4512
* @param fMergeForward Merge direction.
4513
* @param pParentForTarget New parent for target medium after merge.
4514
* @param aChildrenToReparent List of children of the source which will have
4515
* to be reparented to the target after merge.
4516
* @param aMediumLockList Medium locking information.
4517
* @param aProgress Where to find/store a Progress object to track operation
4519
* @param aWait @c true if this method should block instead of creating
4520
* an asynchronous thread.
4521
* @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
4522
* by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
4523
* This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
4524
* and this parameter is ignored.
4526
* @note Locks the tree lock for writing. Locks the media from the chain
4529
HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
4531
const ComObjPtr<Medium> &pParentForTarget,
4532
const MediaList &aChildrenToReparent,
4533
MediumLockList *aMediumLockList,
4534
ComObjPtr <Progress> *aProgress,
4536
GuidList *pllRegistriesThatNeedSaving)
4538
AssertReturn(pTarget != NULL, E_FAIL);
4539
AssertReturn(pTarget != this, E_FAIL);
4540
AssertReturn(aMediumLockList != NULL, E_FAIL);
4541
AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4543
AutoCaller autoCaller(this);
4544
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4546
AutoCaller targetCaller(pTarget);
4547
AssertComRCReturnRC(targetCaller.rc());
4550
ComObjPtr <Progress> pProgress;
4551
Medium::Task *pTask = NULL;
4555
if (aProgress != NULL)
4557
/* use the existing progress object... */
4558
pProgress = *aProgress;
4560
/* ...but create a new one if it is null */
4561
if (pProgress.isNull())
4565
AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4566
tgtName = pTarget->getName();
4569
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4571
pProgress.createObject();
4572
rc = pProgress->init(m->pVirtualBox,
4573
static_cast<IMedium*>(this),
4574
BstrFmt(tr("Merging medium '%s' to '%s'"),
4576
tgtName.c_str()).raw(),
4577
TRUE /* aCancelable */);
4583
/* setup task object to carry out the operation sync/async */
4584
pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
4585
pParentForTarget, aChildrenToReparent,
4586
pProgress, aMediumLockList,
4587
aWait /* fKeepMediumLockList */);
4593
catch (HRESULT aRC) { rc = aRC; }
4598
rc = runNow(pTask, pllRegistriesThatNeedSaving);
4600
rc = startThread(pTask);
4602
if (SUCCEEDED(rc) && aProgress != NULL)
4603
*aProgress = pProgress;
4605
else if (pTask != NULL)
4612
* Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
4613
* called or fails. Frees memory occupied by @a aMediumLockList and unlocks
4614
* the medium objects in @a aChildrenToReparent.
4616
* @param aChildrenToReparent List of children of the source which will have
4617
* to be reparented to the target after merge.
4618
* @param aMediumLockList Medium locking information.
4620
* @note Locks the media from the chain for writing.
4622
void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
4623
MediumLockList *aMediumLockList)
4625
AutoCaller autoCaller(this);
4626
AssertComRCReturnVoid(autoCaller.rc());
4628
AssertReturnVoid(aMediumLockList != NULL);
4630
/* Revert media marked for deletion to previous state. */
4632
MediumLockList::Base::const_iterator mediumListBegin =
4633
aMediumLockList->GetBegin();
4634
MediumLockList::Base::const_iterator mediumListEnd =
4635
aMediumLockList->GetEnd();
4636
for (MediumLockList::Base::const_iterator it = mediumListBegin;
4637
it != mediumListEnd;
4640
const MediumLock &mediumLock = *it;
4641
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4642
AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4644
if (pMedium->m->state == MediumState_Deleting)
4646
rc = pMedium->unmarkForDeletion();
4651
/* the destructor will do the work */
4652
delete aMediumLockList;
4654
/* unlock the children which had to be reparented */
4655
for (MediaList::const_iterator it = aChildrenToReparent.begin();
4656
it != aChildrenToReparent.end();
4659
const ComObjPtr<Medium> &pMedium = *it;
4661
AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4662
pMedium->UnlockWrite(NULL);
4667
* Fix the parent UUID of all children to point to this medium as their
4670
HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
4672
MediumLockList mediumLockList;
4673
HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
4674
false /* fMediumLockWrite */,
4677
AssertComRCReturnRC(rc);
4682
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
4683
ComAssertRCThrow(vrc, E_FAIL);
4687
MediumLockList::Base::iterator lockListBegin =
4688
mediumLockList.GetBegin();
4689
MediumLockList::Base::iterator lockListEnd =
4690
mediumLockList.GetEnd();
4691
for (MediumLockList::Base::iterator it = lockListBegin;
4695
MediumLock &mediumLock = *it;
4696
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4697
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4701
pMedium->m->strFormat.c_str(),
4702
pMedium->m->strLocationFull.c_str(),
4703
VD_OPEN_FLAGS_READONLY,
4704
pMedium->m->vdImageIfaces);
4705
if (RT_FAILURE(vrc))
4709
for (MediaList::const_iterator it = childrenToReparent.begin();
4710
it != childrenToReparent.end();
4713
/* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
4715
(*it)->m->strFormat.c_str(),
4716
(*it)->m->strLocationFull.c_str(),
4718
(*it)->m->vdImageIfaces);
4719
if (RT_FAILURE(vrc))
4722
vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
4723
if (RT_FAILURE(vrc))
4726
vrc = VDClose(hdd, false /* fDelete */);
4727
if (RT_FAILURE(vrc))
4730
(*it)->UnlockWrite(NULL);
4733
catch (HRESULT aRC) { rc = aRC; }
4736
throw setError(E_FAIL,
4737
tr("Could not update medium UUID references to parent '%s' (%s)"),
4738
m->strLocationFull.c_str(),
4739
vdError(aVRC).c_str());
4744
catch (HRESULT aRC) { rc = aRC; }
4750
* Used by IAppliance to export disk images.
4752
* @param aFilename Filename to create (UTF8).
4753
* @param aFormat Medium format for creating @a aFilename.
4754
* @param aVariant Which exact image format variant to use
4755
* for the destination image.
4756
* @param aVDImageIOCallbacks Pointer to the callback table for a
4757
* VDINTERFACEIO interface. May be NULL.
4758
* @param aVDImageIOUser Opaque data for the callbacks.
4759
* @param aProgress Progress object to use.
4761
* @note The source format is defined by the Medium instance.
4763
HRESULT Medium::exportFile(const char *aFilename,
4764
const ComObjPtr<MediumFormat> &aFormat,
4765
MediumVariant_T aVariant,
4766
void *aVDImageIOCallbacks, void *aVDImageIOUser,
4767
const ComObjPtr<Progress> &aProgress)
4769
AssertPtrReturn(aFilename, E_INVALIDARG);
4770
AssertReturn(!aFormat.isNull(), E_INVALIDARG);
4771
AssertReturn(!aProgress.isNull(), E_INVALIDARG);
4773
AutoCaller autoCaller(this);
4774
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4777
Medium::Task *pTask = NULL;
4781
// locking: we need the tree lock first because we access parent pointers
4782
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4783
// and we need to write-lock the media involved
4784
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4786
/* Build the source lock list. */
4787
MediumLockList *pSourceMediumLockList(new MediumLockList());
4788
rc = createMediumLockList(true /* fFailIfInaccessible */,
4789
false /* fMediumLockWrite */,
4791
*pSourceMediumLockList);
4794
delete pSourceMediumLockList;
4798
rc = pSourceMediumLockList->Lock();
4801
delete pSourceMediumLockList;
4803
tr("Failed to lock source media '%s'"),
4804
getLocationFull().c_str());
4807
/* setup task object to carry out the operation asynchronously */
4808
pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
4809
aVariant, aVDImageIOCallbacks,
4810
aVDImageIOUser, pSourceMediumLockList);
4816
catch (HRESULT aRC) { rc = aRC; }
4819
rc = startThread(pTask);
4820
else if (pTask != NULL)
4827
* Used by IAppliance to import disk images.
4829
* @param aFilename Filename to read (UTF8).
4830
* @param aFormat Medium format for reading @a aFilename.
4831
* @param aVariant Which exact image format variant to use
4832
* for the destination image.
4833
* @param aVDImageIOCallbacks Pointer to the callback table for a
4834
* VDINTERFACEIO interface. May be NULL.
4835
* @param aVDImageIOUser Opaque data for the callbacks.
4836
* @param aParent Parent medium. May be NULL.
4837
* @param aProgress Progress object to use.
4839
* @note The destination format is defined by the Medium instance.
4841
HRESULT Medium::importFile(const char *aFilename,
4842
const ComObjPtr<MediumFormat> &aFormat,
4843
MediumVariant_T aVariant,
4844
void *aVDImageIOCallbacks, void *aVDImageIOUser,
4845
const ComObjPtr<Medium> &aParent,
4846
const ComObjPtr<Progress> &aProgress)
4848
AssertPtrReturn(aFilename, E_INVALIDARG);
4849
AssertReturn(!aFormat.isNull(), E_INVALIDARG);
4850
AssertReturn(!aProgress.isNull(), E_INVALIDARG);
4852
AutoCaller autoCaller(this);
4853
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4856
Medium::Task *pTask = NULL;
4860
// locking: we need the tree lock first because we access parent pointers
4861
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4862
// and we need to write-lock the media involved
4863
AutoMultiWriteLock2 alock(this, aParent COMMA_LOCKVAL_SRC_POS);
4865
if ( m->state != MediumState_NotCreated
4866
&& m->state != MediumState_Created)
4867
throw setStateError();
4869
/* Build the target lock list. */
4870
MediumLockList *pTargetMediumLockList(new MediumLockList());
4871
rc = createMediumLockList(true /* fFailIfInaccessible */,
4872
true /* fMediumLockWrite */,
4874
*pTargetMediumLockList);
4877
delete pTargetMediumLockList;
4881
rc = pTargetMediumLockList->Lock();
4884
delete pTargetMediumLockList;
4886
tr("Failed to lock target media '%s'"),
4887
getLocationFull().c_str());
4890
/* setup task object to carry out the operation asynchronously */
4891
pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,
4892
aVariant, aVDImageIOCallbacks,
4893
aVDImageIOUser, aParent,
4894
pTargetMediumLockList);
4900
if (m->state == MediumState_NotCreated)
4901
m->state = MediumState_Creating;
4903
catch (HRESULT aRC) { rc = aRC; }
4906
rc = startThread(pTask);
4907
else if (pTask != NULL)
4913
////////////////////////////////////////////////////////////////////////////////
4917
////////////////////////////////////////////////////////////////////////////////
4920
* Queries information from the medium.
4922
* As a result of this call, the accessibility state and data members such as
4923
* size and description will be updated with the current information.
4925
* @note This method may block during a system I/O call that checks storage
4928
* @note Locks medium tree for reading and writing (for new diff media checked
4929
* for the first time). Locks mParent for reading. Locks this object for
4932
* @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
4933
* @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs())
4936
HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
4938
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4940
if ( m->state != MediumState_Created
4941
&& m->state != MediumState_Inaccessible
4942
&& m->state != MediumState_LockedRead)
4947
int vrc = VINF_SUCCESS;
4949
/* check if a blocking queryInfo() call is in progress on some other thread,
4950
* and wait for it to finish if so instead of querying data ourselves */
4951
if (m->queryInfoRunning)
4953
Assert( m->state == MediumState_LockedRead
4954
|| m->state == MediumState_LockedWrite);
4957
vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
4965
bool success = false;
4966
Utf8Str lastAccessError;
4968
/* are we dealing with a new medium constructed using the existing
4970
bool isImport = m->id.isEmpty();
4971
unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
4973
/* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
4974
* media because that would prevent necessary modifications
4975
* when opening media of some third-party formats for the first
4976
* time in VirtualBox (such as VMDK for which VDOpen() needs to
4977
* generate an UUID if it is missing) */
4978
if ( (m->hddOpenMode == OpenReadOnly)
4979
|| m->type == MediumType_Readonly
4982
uOpenFlags |= VD_OPEN_FLAGS_READONLY;
4984
/* Open shareable medium with the appropriate flags */
4985
if (m->type == MediumType_Shareable)
4986
uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
4988
/* Lock the medium, which makes the behavior much more consistent */
4989
if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
4990
rc = LockRead(NULL);
4992
rc = LockWrite(NULL);
4993
if (FAILED(rc)) return rc;
4995
/* Copies of the input state fields which are not read-only,
4996
* as we're dropping the lock. CAUTION: be extremely careful what
4997
* you do with the contents of this medium object, as you will
4998
* create races if there are concurrent changes. */
4999
Utf8Str format(m->strFormat);
5000
Utf8Str location(m->strLocationFull);
5001
ComObjPtr<MediumFormat> formatObj = m->formatObj;
5003
/* "Output" values which can't be set because the lock isn't held
5004
* at the time the values are determined. */
5005
Guid mediumId = m->id;
5006
uint64_t mediumSize = 0;
5007
uint64_t mediumLogicalSize = 0;
5009
/* Flag whether a base image has a non-zero parent UUID and thus
5010
* need repairing after it was closed again. */
5011
bool fRepairImageZeroParentUuid = false;
5013
/* leave the lock before a lengthy operation */
5014
vrc = RTSemEventMultiReset(m->queryInfoSem);
5015
AssertRCReturn(vrc, E_FAIL);
5016
m->queryInfoRunning = true;
5021
/* skip accessibility checks for host drives */
5029
vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
5030
ComAssertRCThrow(vrc, E_FAIL);
5034
/** @todo This kind of opening of media is assuming that diff
5035
* media can be opened as base media. Should be documented that
5036
* it must work for all medium format backends. */
5042
if (RT_FAILURE(vrc))
5044
lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
5045
location.c_str(), vdError(vrc).c_str());
5049
if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
5051
/* Modify the UUIDs if necessary. The associated fields are
5052
* not modified by other code, so no need to copy. */
5055
vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
5056
ComAssertRCThrow(vrc, E_FAIL);
5060
vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
5061
ComAssertRCThrow(vrc, E_FAIL);
5063
/* zap the information, these are no long-term members */
5064
unconst(m->uuidImage).clear();
5065
unconst(m->uuidParentImage).clear();
5067
/* check the UUID */
5069
vrc = VDGetUuid(hdd, 0, &uuid);
5070
ComAssertRCThrow(vrc, E_FAIL);
5076
if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
5077
// only when importing a VDMK that has no UUID, create one in memory
5082
Assert(!mediumId.isEmpty());
5084
if (mediumId != uuid)
5086
lastAccessError = Utf8StrFmt(
5087
tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
5091
m->pVirtualBox->settingsFilePath().c_str());
5098
/* the backend does not support storing UUIDs within the
5099
* underlying storage so use what we store in XML */
5101
/* generate an UUID for an imported UUID-less medium */
5105
mediumId = m->uuidImage;
5111
/* get the medium variant */
5112
unsigned uImageFlags;
5113
vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5114
ComAssertRCThrow(vrc, E_FAIL);
5115
m->variant = (MediumVariant_T)uImageFlags;
5117
/* check/get the parent uuid and update corresponding state */
5118
if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
5121
vrc = VDGetParentUuid(hdd, 0, &parentId);
5122
ComAssertRCThrow(vrc, E_FAIL);
5124
/* streamOptimized VMDK images are only accepted as base
5125
* images, as this allows automatic repair of OVF appliances.
5126
* Since such images don't support random writes they will not
5127
* be created for diff images. Only an overly smart user might
5128
* manually create this case. Too bad for him. */
5130
&& !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
5132
/* the parent must be known to us. Note that we freely
5133
* call locking methods of mVirtualBox and parent, as all
5134
* relevant locks must be already held. There may be no
5135
* concurrent access to the just opened medium on other
5136
* threads yet (and init() will fail if this method reports
5137
* MediumState_Inaccessible) */
5140
ComObjPtr<Medium> pParent;
5141
rc = m->pVirtualBox->findHardDiskById(id, false /* aSetError */, &pParent);
5144
lastAccessError = Utf8StrFmt(
5145
tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
5146
&parentId, location.c_str(),
5147
m->pVirtualBox->settingsFilePath().c_str());
5151
/* we set mParent & children() */
5152
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5154
Assert(m->pParent.isNull());
5155
m->pParent = pParent;
5156
m->pParent->m->llChildren.push_back(this);
5160
/* we access mParent */
5161
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5163
/* check that parent UUIDs match. Note that there's no need
5164
* for the parent's AutoCaller (our lifetime is bound to
5167
if (m->pParent.isNull())
5169
/* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
5170
* and 3.1.0-3.1.8 there are base images out there
5171
* which have a non-zero parent UUID. No point in
5172
* complaining about them, instead automatically
5173
* repair the problem. Later we can bring back the
5174
* error message, but we should wait until really
5175
* most users have repaired their images, either with
5176
* VBoxFixHdd or this way. */
5178
fRepairImageZeroParentUuid = true;
5180
lastAccessError = Utf8StrFmt(
5181
tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
5183
m->pVirtualBox->settingsFilePath().c_str());
5188
AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
5189
if ( !fRepairImageZeroParentUuid
5190
&& m->pParent->getState() != MediumState_Inaccessible
5191
&& m->pParent->getId() != parentId)
5193
lastAccessError = Utf8StrFmt(
5194
tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
5195
&parentId, location.c_str(),
5196
m->pParent->getId().raw(),
5197
m->pVirtualBox->settingsFilePath().c_str());
5201
/// @todo NEWMEDIA what to do if the parent is not
5202
/// accessible while the diff is? Probably nothing. The
5203
/// real code will detect the mismatch anyway.
5207
mediumSize = VDGetFileSize(hdd, 0);
5208
mediumLogicalSize = VDGetSize(hdd, 0);
5227
unconst(m->id) = mediumId;
5231
m->size = mediumSize;
5232
m->logicalSize = mediumLogicalSize;
5233
m->strLastAccessError.setNull();
5237
m->strLastAccessError = lastAccessError;
5238
LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
5239
location.c_str(), m->strLastAccessError.c_str(),
5243
/* inform other callers if there are any */
5244
RTSemEventMultiSignal(m->queryInfoSem);
5245
m->queryInfoRunning = false;
5247
/* Set the proper state according to the result of the check */
5249
m->preLockState = MediumState_Created;
5251
m->preLockState = MediumState_Inaccessible;
5254
if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
5255
rc2 = UnlockRead(NULL);
5257
rc2 = UnlockWrite(NULL);
5258
if (SUCCEEDED(rc) && FAILED(rc2))
5260
if (FAILED(rc)) return rc;
5262
/* If this is a base image which incorrectly has a parent UUID set,
5263
* repair the image now by zeroing the parent UUID. This is only done
5264
* when we have structural information from a config file, on import
5265
* this is not possible. If someone would accidentally call openMedium
5266
* with a diff image before the base is registered this would destroy
5267
* the diff. Not acceptable. */
5268
if (fRepairImageZeroParentUuid)
5270
rc = LockWrite(NULL);
5271
if (FAILED(rc)) return rc;
5278
vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
5279
ComAssertRCThrow(vrc, E_FAIL);
5286
uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
5288
if (RT_FAILURE(vrc))
5291
RTUUID zeroParentUuid;
5292
RTUuidClear(&zeroParentUuid);
5293
vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
5294
ComAssertRCThrow(vrc, E_FAIL);
5310
rc = UnlockWrite(NULL);
5311
if (SUCCEEDED(rc) && FAILED(rc2))
5313
if (FAILED(rc)) return rc;
5320
* Performs extra checks if the medium can be closed and returns S_OK in
5321
* this case. Otherwise, returns a respective error message. Called by
5322
* Close() under the medium tree lock and the medium lock.
5324
* @note Also reused by Medium::Reset().
5326
* @note Caller must hold the media tree write lock!
5328
HRESULT Medium::canClose()
5330
Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5332
if (getChildren().size() != 0)
5333
return setError(VBOX_E_OBJECT_IN_USE,
5334
tr("Cannot close medium '%s' because it has %d child media"),
5335
m->strLocationFull.c_str(), getChildren().size());
5341
* Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
5343
* This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending
5344
* on the device type of this medium.
5346
* @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs that will receive the registry IDs that need saving.
5348
* @note Caller must have locked the media tree lock for writing!
5350
HRESULT Medium::unregisterWithVirtualBox(GuidList *pllRegistriesThatNeedSaving)
5352
/* Note that we need to de-associate ourselves from the parent to let
5353
* unregisterHardDisk() properly save the registry */
5355
/* we modify mParent and access children */
5356
Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5358
Medium *pParentBackup = m->pParent;
5359
AssertReturn(getChildren().size() == 0, E_FAIL);
5363
HRESULT rc = E_FAIL;
5366
case DeviceType_DVD:
5367
case DeviceType_Floppy:
5368
rc = m->pVirtualBox->unregisterImage(this,
5370
pllRegistriesThatNeedSaving);
5373
case DeviceType_HardDisk:
5374
rc = m->pVirtualBox->unregisterHardDisk(this, pllRegistriesThatNeedSaving);
5385
// re-associate with the parent as we are still relatives in the registry
5386
m->pParent = pParentBackup;
5387
m->pParent->m->llChildren.push_back(this);
5395
* Sets the extended error info according to the current media state.
5397
* @note Must be called from under this object's write or read lock.
5399
HRESULT Medium::setStateError()
5401
HRESULT rc = E_FAIL;
5405
case MediumState_NotCreated:
5407
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5408
tr("Storage for the medium '%s' is not created"),
5409
m->strLocationFull.c_str());
5412
case MediumState_Created:
5414
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5415
tr("Storage for the medium '%s' is already created"),
5416
m->strLocationFull.c_str());
5419
case MediumState_LockedRead:
5421
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5422
tr("Medium '%s' is locked for reading by another task"),
5423
m->strLocationFull.c_str());
5426
case MediumState_LockedWrite:
5428
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5429
tr("Medium '%s' is locked for writing by another task"),
5430
m->strLocationFull.c_str());
5433
case MediumState_Inaccessible:
5435
/* be in sync with Console::powerUpThread() */
5436
if (!m->strLastAccessError.isEmpty())
5437
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5438
tr("Medium '%s' is not accessible. %s"),
5439
m->strLocationFull.c_str(), m->strLastAccessError.c_str());
5441
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5442
tr("Medium '%s' is not accessible"),
5443
m->strLocationFull.c_str());
5446
case MediumState_Creating:
5448
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5449
tr("Storage for the medium '%s' is being created"),
5450
m->strLocationFull.c_str());
5453
case MediumState_Deleting:
5455
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5456
tr("Storage for the medium '%s' is being deleted"),
5457
m->strLocationFull.c_str());
5471
* Sets the value of m->strLocationFull. The given location must be a fully
5472
* qualified path; relative paths are not supported here.
5474
* As a special exception, if the specified location is a file path that ends with '/'
5475
* then the file name part will be generated by this method automatically in the format
5476
* '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
5477
* and assign to this medium, and <ext> is the default extension for this
5478
* medium's storage format. Note that this procedure requires the media state to
5479
* be NotCreated and will return a failure otherwise.
5481
* @param aLocation Location of the storage unit. If the location is a FS-path,
5482
* then it can be relative to the VirtualBox home directory.
5483
* @param aFormat Optional fallback format if it is an import and the format
5484
* cannot be determined.
5486
* @note Must be called from under this object's write lock.
5488
HRESULT Medium::setLocation(const Utf8Str &aLocation,
5489
const Utf8Str &aFormat /* = Utf8Str::Empty */)
5491
AssertReturn(!aLocation.isEmpty(), E_FAIL);
5493
AutoCaller autoCaller(this);
5494
AssertComRCReturnRC(autoCaller.rc());
5496
/* formatObj may be null only when initializing from an existing path and
5497
* no format is known yet */
5498
AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
5499
|| ( autoCaller.state() == InInit
5500
&& m->state != MediumState_NotCreated
5502
&& m->strFormat.isEmpty()
5503
&& m->formatObj.isNull()),
5506
/* are we dealing with a new medium constructed using the existing
5508
bool isImport = m->strFormat.isEmpty();
5511
|| ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
5516
Utf8Str locationFull(aLocation);
5518
if (m->state == MediumState_NotCreated)
5520
/* must be a file (formatObj must be already known) */
5521
Assert(m->formatObj->getCapabilities() & MediumFormatCapabilities_File);
5523
if (RTPathFilename(aLocation.c_str()) == NULL)
5525
/* no file name is given (either an empty string or ends with a
5526
* slash), generate a new UUID + file name if the state allows
5529
ComAssertMsgRet(!m->formatObj->getFileExtensions().empty(),
5530
("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
5533
Utf8Str strExt = m->formatObj->getFileExtensions().front();
5534
ComAssertMsgRet(!strExt.isEmpty(),
5535
("Default extension must not be empty\n"),
5540
locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
5541
aLocation.c_str(), id.raw(), strExt.c_str());
5545
// we must always have full paths now
5546
if (!RTPathStartsWithRoot(locationFull.c_str()))
5547
return setError(VBOX_E_FILE_ERROR,
5548
tr("The given path '%s' is not fully qualified"),
5549
locationFull.c_str());
5551
/* detect the backend from the storage unit if importing */
5554
VDTYPE enmType = VDTYPE_INVALID;
5555
char *backendName = NULL;
5557
int vrc = VINF_SUCCESS;
5562
vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
5563
if (RT_SUCCESS(vrc))
5566
if (RT_SUCCESS(vrc))
5568
vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
5569
locationFull.c_str(), &backendName, &enmType);
5571
else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
5573
/* assume it's not a file, restore the original location */
5574
locationFull = aLocation;
5575
vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
5576
locationFull.c_str(), &backendName, &enmType);
5579
if (RT_FAILURE(vrc))
5581
if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
5582
return setError(VBOX_E_FILE_ERROR,
5583
tr("Could not find file for the medium '%s' (%Rrc)"),
5584
locationFull.c_str(), vrc);
5585
else if (aFormat.isEmpty())
5586
return setError(VBOX_E_IPRT_ERROR,
5587
tr("Could not get the storage format of the medium '%s' (%Rrc)"),
5588
locationFull.c_str(), vrc);
5591
HRESULT rc = setFormat(aFormat);
5592
/* setFormat() must not fail since we've just used the backend so
5593
* the format object must be there */
5594
AssertComRCReturnRC(rc);
5597
else if ( enmType == VDTYPE_INVALID
5598
|| m->devType != convertToDeviceType(enmType))
5601
* The user tried to use a image as a device which is not supported
5604
return setError(E_FAIL,
5605
tr("The medium '%s' can't be used as the requested device type"),
5606
locationFull.c_str());
5610
ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
5612
HRESULT rc = setFormat(backendName);
5613
RTStrFree(backendName);
5615
/* setFormat() must not fail since we've just used the backend so
5616
* the format object must be there */
5617
AssertComRCReturnRC(rc);
5621
m->strLocationFull = locationFull;
5623
/* is it still a file? */
5624
if ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
5625
&& (m->state == MediumState_NotCreated)
5627
/* assign a new UUID (this UUID will be used when calling
5628
* VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
5629
* also do that if we didn't generate it to make sure it is
5630
* either generated by us or reset to null */
5631
unconst(m->id) = id;
5634
m->strLocationFull = aLocation;
5640
* Checks that the format ID is valid and sets it on success.
5642
* Note that this method will caller-reference the format object on success!
5643
* This reference must be released somewhere to let the MediumFormat object be
5646
* @note Must be called from under this object's write lock.
5648
HRESULT Medium::setFormat(const Utf8Str &aFormat)
5650
/* get the format object first */
5652
SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
5653
AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
5655
unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
5656
if (m->formatObj.isNull())
5657
return setError(E_INVALIDARG,
5658
tr("Invalid medium storage format '%s'"),
5661
/* reference the format permanently to prevent its unexpected
5662
* uninitialization */
5663
HRESULT rc = m->formatObj->addCaller();
5664
AssertComRCReturnRC(rc);
5666
/* get properties (preinsert them as keys in the map). Note that the
5667
* map doesn't grow over the object life time since the set of
5668
* properties is meant to be constant. */
5670
Assert(m->mapProperties.empty());
5672
for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
5673
it != m->formatObj->getProperties().end();
5676
m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
5680
unconst(m->strFormat) = aFormat;
5686
* Converts the Medium device type to the VD type.
5688
VDTYPE Medium::convertDeviceType()
5694
case DeviceType_HardDisk:
5695
enmType = VDTYPE_HDD;
5697
case DeviceType_DVD:
5698
enmType = VDTYPE_DVD;
5700
case DeviceType_Floppy:
5701
enmType = VDTYPE_FLOPPY;
5704
ComAssertFailedRet(VDTYPE_INVALID);
5711
* Converts from the VD type to the medium type.
5713
DeviceType_T Medium::convertToDeviceType(VDTYPE enmType)
5715
DeviceType_T devType;
5720
devType = DeviceType_HardDisk;
5723
devType = DeviceType_DVD;
5726
devType = DeviceType_Floppy;
5729
ComAssertFailedRet(DeviceType_Null);
5736
* Returns the last error message collected by the vdErrorCall callback and
5739
* The error message is returned prepended with a dot and a space, like this:
5741
* ". <error_text> (%Rrc)"
5743
* to make it easily appendable to a more general error message. The @c %Rrc
5744
* format string is given @a aVRC as an argument.
5746
* If there is no last error message collected by vdErrorCall or if it is a
5747
* null or empty string, then this function returns the following text:
5752
* @note Doesn't do any object locking; it is assumed that the caller makes sure
5753
* the callback isn't called by more than one thread at a time.
5755
* @param aVRC VBox error code to use when no error message is provided.
5757
Utf8Str Medium::vdError(int aVRC)
5761
if (m->vdError.isEmpty())
5762
error = Utf8StrFmt(" (%Rrc)", aVRC);
5764
error = Utf8StrFmt(".\n%s", m->vdError.c_str());
5766
m->vdError.setNull();
5772
* Error message callback.
5774
* Puts the reported error message to the m->vdError field.
5776
* @note Doesn't do any object locking; it is assumed that the caller makes sure
5777
* the callback isn't called by more than one thread at a time.
5779
* @param pvUser The opaque data passed on container creation.
5780
* @param rc The VBox error code.
5781
* @param RT_SRC_POS_DECL Use RT_SRC_POS.
5782
* @param pszFormat Error message format string.
5783
* @param va Error message arguments.
5786
DECLCALLBACK(void) Medium::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
5787
const char *pszFormat, va_list va)
5789
NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
5791
Medium *that = static_cast<Medium*>(pvUser);
5792
AssertReturnVoid(that != NULL);
5794
if (that->m->vdError.isEmpty())
5796
Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
5799
Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
5800
Utf8Str(pszFormat, va).c_str(), rc);
5804
DECLCALLBACK(bool) Medium::vdConfigAreKeysValid(void *pvUser,
5805
const char * /* pszzValid */)
5807
Medium *that = static_cast<Medium*>(pvUser);
5808
AssertReturn(that != NULL, false);
5810
/* we always return true since the only keys we have are those found in
5816
DECLCALLBACK(int) Medium::vdConfigQuerySize(void *pvUser,
5817
const char *pszName,
5820
AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
5822
Medium *that = static_cast<Medium*>(pvUser);
5823
AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
5825
settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
5826
if (it == that->m->mapProperties.end())
5827
return VERR_CFGM_VALUE_NOT_FOUND;
5829
/* we interpret null values as "no value" in Medium */
5830
if (it->second.isEmpty())
5831
return VERR_CFGM_VALUE_NOT_FOUND;
5833
*pcbValue = it->second.length() + 1 /* include terminator */;
5835
return VINF_SUCCESS;
5839
DECLCALLBACK(int) Medium::vdConfigQuery(void *pvUser,
5840
const char *pszName,
5844
AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
5846
Medium *that = static_cast<Medium*>(pvUser);
5847
AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
5849
settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
5850
if (it == that->m->mapProperties.end())
5851
return VERR_CFGM_VALUE_NOT_FOUND;
5853
/* we interpret null values as "no value" in Medium */
5854
if (it->second.isEmpty())
5855
return VERR_CFGM_VALUE_NOT_FOUND;
5857
const Utf8Str &value = it->second;
5858
if (value.length() >= cchValue)
5859
return VERR_CFGM_NOT_ENOUGH_SPACE;
5861
memcpy(pszValue, value.c_str(), value.length() + 1);
5863
return VINF_SUCCESS;
5866
DECLCALLBACK(int) Medium::vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
5868
PVDSOCKETINT pSocketInt = NULL;
5870
if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
5871
return VERR_NOT_SUPPORTED;
5873
pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
5875
return VERR_NO_MEMORY;
5877
pSocketInt->hSocket = NIL_RTSOCKET;
5878
*pSock = pSocketInt;
5879
return VINF_SUCCESS;
5882
DECLCALLBACK(int) Medium::vdTcpSocketDestroy(VDSOCKET Sock)
5884
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5886
if (pSocketInt->hSocket != NIL_RTSOCKET)
5887
RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
5889
RTMemFree(pSocketInt);
5891
return VINF_SUCCESS;
5894
DECLCALLBACK(int) Medium::vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
5896
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5898
return RTTcpClientConnect(pszAddress, uPort, &pSocketInt->hSocket);
5901
DECLCALLBACK(int) Medium::vdTcpClientClose(VDSOCKET Sock)
5903
int rc = VINF_SUCCESS;
5904
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5906
rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
5907
pSocketInt->hSocket = NIL_RTSOCKET;
5911
DECLCALLBACK(bool) Medium::vdTcpIsClientConnected(VDSOCKET Sock)
5913
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5914
return pSocketInt->hSocket != NIL_RTSOCKET;
5917
DECLCALLBACK(int) Medium::vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
5919
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5920
return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
5923
DECLCALLBACK(int) Medium::vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
5925
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5926
return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
5929
DECLCALLBACK(int) Medium::vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
5931
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5932
return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
5935
DECLCALLBACK(int) Medium::vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
5937
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5938
return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
5941
DECLCALLBACK(int) Medium::vdTcpFlush(VDSOCKET Sock)
5943
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5944
return RTTcpFlush(pSocketInt->hSocket);
5947
DECLCALLBACK(int) Medium::vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
5949
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5950
return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
5953
DECLCALLBACK(int) Medium::vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
5955
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5956
return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
5959
DECLCALLBACK(int) Medium::vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
5961
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5962
return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
5966
* Starts a new thread driven by the appropriate Medium::Task::handler() method.
5968
* @note When the task is executed by this method, IProgress::notifyComplete()
5969
* is automatically called for the progress object associated with this
5970
* task when the task is finished to signal the operation completion for
5971
* other threads asynchronously waiting for it.
5973
HRESULT Medium::startThread(Medium::Task *pTask)
5975
#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
5976
/* Extreme paranoia: The calling thread should not hold the medium
5977
* tree lock or any medium lock. Since there is no separate lock class
5978
* for medium objects be even more strict: no other object locks. */
5979
Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
5980
Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
5983
/// @todo use a more descriptive task name
5984
int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
5985
0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
5987
if (RT_FAILURE(vrc))
5990
return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n", vrc);
5997
* Runs Medium::Task::handler() on the current thread instead of creating
6000
* This call implies that it is made on another temporary thread created for
6001
* some asynchronous task. Avoid calling it from a normal thread since the task
6002
* operations are potentially lengthy and will block the calling thread in this
6005
* @note When the task is executed by this method, IProgress::notifyComplete()
6006
* is not called for the progress object associated with this task when
6007
* the task is finished. Instead, the result of the operation is returned
6008
* by this method directly and it's the caller's responsibility to
6009
* complete the progress object in this case.
6011
HRESULT Medium::runNow(Medium::Task *pTask,
6012
GuidList *pllRegistriesThatNeedSaving)
6014
#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
6015
/* Extreme paranoia: The calling thread should not hold the medium
6016
* tree lock or any medium lock. Since there is no separate lock class
6017
* for medium objects be even more strict: no other object locks. */
6018
Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
6019
Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
6022
pTask->m_pllRegistriesThatNeedSaving = pllRegistriesThatNeedSaving;
6024
/* NIL_RTTHREAD indicates synchronous call. */
6025
return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
6029
* Implementation code for the "create base" task.
6031
* This only gets started from Medium::CreateBaseStorage() and always runs
6032
* asynchronously. As a result, we always save the VirtualBox.xml file when
6038
HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
6042
/* these parameters we need after creation */
6043
uint64_t size = 0, logicalSize = 0;
6044
MediumVariant_T variant = MediumVariant_Standard;
6045
bool fGenerateUuid = false;
6049
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6051
/* The object may request a specific UUID (through a special form of
6052
* the setLocation() argument). Otherwise we have to generate it */
6054
fGenerateUuid = id.isEmpty();
6058
/* VirtualBox::registerHardDisk() will need UUID */
6059
unconst(m->id) = id;
6062
Utf8Str format(m->strFormat);
6063
Utf8Str location(m->strLocationFull);
6064
uint64_t capabilities = m->formatObj->getCapabilities();
6065
ComAssertThrow(capabilities & ( VD_CAP_CREATE_FIXED
6066
| VD_CAP_CREATE_DYNAMIC), E_FAIL);
6067
Assert(m->state == MediumState_Creating);
6070
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6071
ComAssertRCThrow(vrc, E_FAIL);
6073
/* unlock before the potentially lengthy operation */
6078
/* ensure the directory exists */
6079
rc = VirtualBox::ensureFilePathExists(location);
6083
VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
6085
vrc = VDCreateBase(hdd,
6094
VD_OPEN_FLAGS_NORMAL,
6096
task.mVDOperationIfaces);
6097
if (RT_FAILURE(vrc))
6098
throw setError(VBOX_E_FILE_ERROR,
6099
tr("Could not create the medium storage unit '%s'%s"),
6100
location.c_str(), vdError(vrc).c_str());
6102
size = VDGetFileSize(hdd, 0);
6103
logicalSize = VDGetSize(hdd, 0);
6104
unsigned uImageFlags;
6105
vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6106
if (RT_SUCCESS(vrc))
6107
variant = (MediumVariant_T)uImageFlags;
6109
catch (HRESULT aRC) { rc = aRC; }
6113
catch (HRESULT aRC) { rc = aRC; }
6117
/* register with mVirtualBox as the last step and move to
6118
* Created state only on success (leaving an orphan file is
6119
* better than breaking media registry consistency) */
6120
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6121
rc = m->pVirtualBox->registerHardDisk(this, NULL /* pllRegistriesThatNeedSaving */);
6124
// reenter the lock before changing state
6125
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6129
m->state = MediumState_Created;
6132
m->logicalSize = logicalSize;
6133
m->variant = variant;
6137
/* back to NotCreated on failure */
6138
m->state = MediumState_NotCreated;
6140
/* reset UUID to prevent it from being reused next time */
6142
unconst(m->id).clear();
6149
* Implementation code for the "create diff" task.
6151
* This task always gets started from Medium::createDiffStorage() and can run
6152
* synchronously or asynchronously depending on the "wait" parameter passed to
6153
* that function. If we run synchronously, the caller expects the bool
6154
* *pfNeedsGlobalSaveSettings to be set before returning; otherwise (in asynchronous
6155
* mode), we save the settings ourselves.
6160
HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
6164
const ComObjPtr<Medium> &pTarget = task.mTarget;
6166
uint64_t size = 0, logicalSize = 0;
6167
MediumVariant_T variant = MediumVariant_Standard;
6168
bool fGenerateUuid = false;
6170
GuidList llRegistriesThatNeedSaving;
6174
/* Lock both in {parent,child} order. */
6175
AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6177
/* The object may request a specific UUID (through a special form of
6178
* the setLocation() argument). Otherwise we have to generate it */
6179
Guid targetId = pTarget->m->id;
6180
fGenerateUuid = targetId.isEmpty();
6184
/* VirtualBox::registerHardDisk() will need UUID */
6185
unconst(pTarget->m->id) = targetId;
6190
Utf8Str targetFormat(pTarget->m->strFormat);
6191
Utf8Str targetLocation(pTarget->m->strLocationFull);
6192
uint64_t capabilities = pTarget->m->formatObj->getCapabilities();
6193
ComAssertThrow(capabilities & VD_CAP_CREATE_DYNAMIC, E_FAIL);
6195
Assert(pTarget->m->state == MediumState_Creating);
6196
Assert(m->state == MediumState_LockedRead);
6199
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6200
ComAssertRCThrow(vrc, E_FAIL);
6202
/* the two media are now protected by their non-default states;
6203
* unlock the media before the potentially lengthy operation */
6204
mediaLock.release();
6208
/* Open all media in the target chain but the last. */
6209
MediumLockList::Base::const_iterator targetListBegin =
6210
task.mpMediumLockList->GetBegin();
6211
MediumLockList::Base::const_iterator targetListEnd =
6212
task.mpMediumLockList->GetEnd();
6213
for (MediumLockList::Base::const_iterator it = targetListBegin;
6214
it != targetListEnd;
6217
const MediumLock &mediumLock = *it;
6218
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6220
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6222
/* Skip over the target diff medium */
6223
if (pMedium->m->state == MediumState_Creating)
6227
Assert(pMedium->m->state == MediumState_LockedRead);
6229
/* Open all media in appropriate mode. */
6231
pMedium->m->strFormat.c_str(),
6232
pMedium->m->strLocationFull.c_str(),
6233
VD_OPEN_FLAGS_READONLY,
6234
pMedium->m->vdImageIfaces);
6235
if (RT_FAILURE(vrc))
6236
throw setError(VBOX_E_FILE_ERROR,
6237
tr("Could not open the medium storage unit '%s'%s"),
6238
pMedium->m->strLocationFull.c_str(),
6239
vdError(vrc).c_str());
6242
/* ensure the target directory exists */
6243
rc = VirtualBox::ensureFilePathExists(targetLocation);
6247
vrc = VDCreateDiff(hdd,
6248
targetFormat.c_str(),
6249
targetLocation.c_str(),
6250
task.mVariant | VD_IMAGE_FLAGS_DIFF,
6254
VD_OPEN_FLAGS_NORMAL,
6255
pTarget->m->vdImageIfaces,
6256
task.mVDOperationIfaces);
6257
if (RT_FAILURE(vrc))
6258
throw setError(VBOX_E_FILE_ERROR,
6259
tr("Could not create the differencing medium storage unit '%s'%s"),
6260
targetLocation.c_str(), vdError(vrc).c_str());
6262
size = VDGetFileSize(hdd, VD_LAST_IMAGE);
6263
logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
6264
unsigned uImageFlags;
6265
vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6266
if (RT_SUCCESS(vrc))
6267
variant = (MediumVariant_T)uImageFlags;
6269
catch (HRESULT aRC) { rc = aRC; }
6273
catch (HRESULT aRC) { rc = aRC; }
6277
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6279
Assert(pTarget->m->pParent.isNull());
6281
/* associate the child with the parent */
6282
pTarget->m->pParent = this;
6283
m->llChildren.push_back(pTarget);
6285
/** @todo r=klaus neither target nor base() are locked,
6286
* potential race! */
6287
/* diffs for immutable media are auto-reset by default */
6288
pTarget->m->autoReset = (getBase()->m->type == MediumType_Immutable);
6290
/* register with mVirtualBox as the last step and move to
6291
* Created state only on success (leaving an orphan file is
6292
* better than breaking media registry consistency) */
6293
rc = m->pVirtualBox->registerHardDisk(pTarget, &llRegistriesThatNeedSaving);
6296
/* break the parent association on failure to register */
6300
AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6304
pTarget->m->state = MediumState_Created;
6306
pTarget->m->size = size;
6307
pTarget->m->logicalSize = logicalSize;
6308
pTarget->m->variant = variant;
6312
/* back to NotCreated on failure */
6313
pTarget->m->state = MediumState_NotCreated;
6315
pTarget->m->autoReset = false;
6317
/* reset UUID to prevent it from being reused next time */
6319
unconst(pTarget->m->id).clear();
6322
// deregister the task registered in createDiffStorage()
6323
Assert(m->numCreateDiffTasks != 0);
6324
--m->numCreateDiffTasks;
6328
mediaLock.release();
6329
m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
6332
// synchronous mode: report save settings result to caller
6333
if (task.m_pllRegistriesThatNeedSaving)
6334
*task.m_pllRegistriesThatNeedSaving = llRegistriesThatNeedSaving;
6336
/* Note that in sync mode, it's the caller's responsibility to
6337
* unlock the medium. */
6343
* Implementation code for the "merge" task.
6345
* This task always gets started from Medium::mergeTo() and can run
6346
* synchronously or asynchronously depending on the "wait" parameter passed to
6347
* that function. If we run synchronously, the caller expects the bool
6348
* *pfNeedsGlobalSaveSettings to be set before returning; otherwise (in asynchronous
6349
* mode), we save the settings ourselves.
6354
HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
6358
const ComObjPtr<Medium> &pTarget = task.mTarget;
6363
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6364
ComAssertRCThrow(vrc, E_FAIL);
6368
// Similar code appears in SessionMachine::onlineMergeMedium, so
6369
// if you make any changes below check whether they are applicable
6370
// in that context as well.
6372
unsigned uTargetIdx = VD_LAST_IMAGE;
6373
unsigned uSourceIdx = VD_LAST_IMAGE;
6374
/* Open all media in the chain. */
6375
MediumLockList::Base::iterator lockListBegin =
6376
task.mpMediumLockList->GetBegin();
6377
MediumLockList::Base::iterator lockListEnd =
6378
task.mpMediumLockList->GetEnd();
6380
for (MediumLockList::Base::iterator it = lockListBegin;
6384
MediumLock &mediumLock = *it;
6385
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6387
if (pMedium == this)
6389
else if (pMedium == pTarget)
6392
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6395
* complex sanity (sane complexity)
6397
* The current medium must be in the Deleting (medium is merged)
6398
* or LockedRead (parent medium) state if it is not the target.
6399
* If it is the target it must be in the LockedWrite state.
6401
Assert( ( pMedium != pTarget
6402
&& ( pMedium->m->state == MediumState_Deleting
6403
|| pMedium->m->state == MediumState_LockedRead))
6404
|| ( pMedium == pTarget
6405
&& pMedium->m->state == MediumState_LockedWrite));
6408
* Medium must be the target, in the LockedRead state
6409
* or Deleting state where it is not allowed to be attached
6410
* to a virtual machine.
6412
Assert( pMedium == pTarget
6413
|| pMedium->m->state == MediumState_LockedRead
6414
|| ( pMedium->m->backRefs.size() == 0
6415
&& pMedium->m->state == MediumState_Deleting));
6416
/* The source medium must be in Deleting state. */
6417
Assert( pMedium != this
6418
|| pMedium->m->state == MediumState_Deleting);
6420
unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
6422
if ( pMedium->m->state == MediumState_LockedRead
6423
|| pMedium->m->state == MediumState_Deleting)
6424
uOpenFlags = VD_OPEN_FLAGS_READONLY;
6425
if (pMedium->m->type == MediumType_Shareable)
6426
uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6428
/* Open the medium */
6430
pMedium->m->strFormat.c_str(),
6431
pMedium->m->strLocationFull.c_str(),
6433
pMedium->m->vdImageIfaces);
6434
if (RT_FAILURE(vrc))
6440
ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
6441
&& uTargetIdx != VD_LAST_IMAGE, E_FAIL);
6443
vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
6444
task.mVDOperationIfaces);
6445
if (RT_FAILURE(vrc))
6448
/* update parent UUIDs */
6449
if (!task.mfMergeForward)
6451
/* we need to update UUIDs of all source's children
6452
* which cannot be part of the container at once so
6453
* add each one in there individually */
6454
if (task.mChildrenToReparent.size() > 0)
6456
for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
6457
it != task.mChildrenToReparent.end();
6460
/* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
6462
(*it)->m->strFormat.c_str(),
6463
(*it)->m->strLocationFull.c_str(),
6465
(*it)->m->vdImageIfaces);
6466
if (RT_FAILURE(vrc))
6469
vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
6470
pTarget->m->id.raw());
6471
if (RT_FAILURE(vrc))
6474
vrc = VDClose(hdd, false /* fDelete */);
6475
if (RT_FAILURE(vrc))
6478
(*it)->UnlockWrite(NULL);
6483
catch (HRESULT aRC) { rc = aRC; }
6486
throw setError(VBOX_E_FILE_ERROR,
6487
tr("Could not merge the medium '%s' to '%s'%s"),
6488
m->strLocationFull.c_str(),
6489
pTarget->m->strLocationFull.c_str(),
6490
vdError(aVRC).c_str());
6495
catch (HRESULT aRC) { rc = aRC; }
6501
/* all media but the target were successfully deleted by
6502
* VDMerge; reparent the last one and uninitialize deleted media. */
6504
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6506
if (task.mfMergeForward)
6508
/* first, unregister the target since it may become a base
6509
* medium which needs re-registration */
6510
rc2 = m->pVirtualBox->unregisterHardDisk(pTarget, NULL /*&fNeedsGlobalSaveSettings*/);
6513
/* then, reparent it and disconnect the deleted branch at
6514
* both ends (chain->parent() is source's parent) */
6515
pTarget->deparent();
6516
pTarget->m->pParent = task.mParentForTarget;
6517
if (pTarget->m->pParent)
6519
pTarget->m->pParent->m->llChildren.push_back(pTarget);
6523
/* then, register again */
6524
rc2 = m->pVirtualBox->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */ );
6529
Assert(pTarget->getChildren().size() == 1);
6530
Medium *targetChild = pTarget->getChildren().front();
6532
/* disconnect the deleted branch at the elder end */
6533
targetChild->deparent();
6535
/* reparent source's children and disconnect the deleted
6536
* branch at the younger end */
6537
if (task.mChildrenToReparent.size() > 0)
6539
/* obey {parent,child} lock order */
6540
AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
6542
for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
6543
it != task.mChildrenToReparent.end();
6546
Medium *pMedium = *it;
6547
AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
6549
pMedium->deparent(); // removes pMedium from source
6550
pMedium->setParent(pTarget);
6555
/* unregister and uninitialize all media removed by the merge */
6556
MediumLockList::Base::iterator lockListBegin =
6557
task.mpMediumLockList->GetBegin();
6558
MediumLockList::Base::iterator lockListEnd =
6559
task.mpMediumLockList->GetEnd();
6560
for (MediumLockList::Base::iterator it = lockListBegin;
6564
MediumLock &mediumLock = *it;
6565
/* Create a real copy of the medium pointer, as the medium
6566
* lock deletion below would invalidate the referenced object. */
6567
const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
6569
/* The target and all media not merged (readonly) are skipped */
6570
if ( pMedium == pTarget
6571
|| pMedium->m->state == MediumState_LockedRead)
6577
rc2 = pMedium->m->pVirtualBox->unregisterHardDisk(pMedium,
6578
NULL /*pfNeedsGlobalSaveSettings*/);
6581
/* now, uninitialize the deleted medium (note that
6582
* due to the Deleting state, uninit() will not touch
6583
* the parent-child relationship so we need to
6584
* uninitialize each disk individually) */
6586
/* note that the operation initiator medium (which is
6587
* normally also the source medium) is a special case
6588
* -- there is one more caller added by Task to it which
6589
* we must release. Also, if we are in sync mode, the
6590
* caller may still hold an AutoCaller instance for it
6591
* and therefore we cannot uninit() it (it's therefore
6592
* the caller's responsibility) */
6593
if (pMedium == this)
6595
Assert(getChildren().size() == 0);
6596
Assert(m->backRefs.size() == 0);
6597
task.mMediumCaller.release();
6600
/* Delete the medium lock list entry, which also releases the
6601
* caller added by MergeChain before uninit() and updates the
6602
* iterator to point to the right place. */
6603
rc2 = task.mpMediumLockList->RemoveByIterator(it);
6606
if (task.isAsync() || pMedium != this)
6613
// in asynchronous mode, save settings now
6614
GuidList llRegistriesThatNeedSaving;
6615
addToRegistryIDList(llRegistriesThatNeedSaving);
6616
rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
6619
// synchronous mode: report save settings result to caller
6620
if (task.m_pllRegistriesThatNeedSaving)
6621
pTarget->addToRegistryIDList(*task.m_pllRegistriesThatNeedSaving);
6625
/* Here we come if either VDMerge() failed (in which case we
6626
* assume that it tried to do everything to make a further
6627
* retry possible -- e.g. not deleted intermediate media
6628
* and so on) or VirtualBox::saveSettings() failed (where we
6629
* should have the original tree but with intermediate storage
6630
* units deleted by VDMerge()). We have to only restore states
6631
* (through the MergeChain dtor) unless we are run synchronously
6632
* in which case it's the responsibility of the caller as stated
6633
* in the mergeTo() docs. The latter also implies that we
6634
* don't own the merge chain, so release it in this case. */
6637
Assert(task.mChildrenToReparent.size() == 0);
6638
cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
6646
* Implementation code for the "clone" task.
6648
* This only gets started from Medium::CloneTo() and always runs asynchronously.
6649
* As a result, we always save the VirtualBox.xml file when we're done here.
6654
HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
6658
const ComObjPtr<Medium> &pTarget = task.mTarget;
6659
const ComObjPtr<Medium> &pParent = task.mParent;
6661
bool fCreatingTarget = false;
6663
uint64_t size = 0, logicalSize = 0;
6664
MediumVariant_T variant = MediumVariant_Standard;
6665
bool fGenerateUuid = false;
6669
/* Lock all in {parent,child} order. The lock is also used as a
6670
* signal from the task initiator (which releases it only after
6671
* RTThreadCreate()) that we can start the job. */
6672
AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
6674
fCreatingTarget = pTarget->m->state == MediumState_Creating;
6676
/* The object may request a specific UUID (through a special form of
6677
* the setLocation() argument). Otherwise we have to generate it */
6678
Guid targetId = pTarget->m->id;
6679
fGenerateUuid = targetId.isEmpty();
6683
/* VirtualBox::registerHardDisk() will need UUID */
6684
unconst(pTarget->m->id) = targetId;
6688
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6689
ComAssertRCThrow(vrc, E_FAIL);
6693
/* Open all media in the source chain. */
6694
MediumLockList::Base::const_iterator sourceListBegin =
6695
task.mpSourceMediumLockList->GetBegin();
6696
MediumLockList::Base::const_iterator sourceListEnd =
6697
task.mpSourceMediumLockList->GetEnd();
6698
for (MediumLockList::Base::const_iterator it = sourceListBegin;
6699
it != sourceListEnd;
6702
const MediumLock &mediumLock = *it;
6703
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6704
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6707
Assert(pMedium->m->state == MediumState_LockedRead);
6709
/** Open all media in read-only mode. */
6711
pMedium->m->strFormat.c_str(),
6712
pMedium->m->strLocationFull.c_str(),
6713
VD_OPEN_FLAGS_READONLY,
6714
pMedium->m->vdImageIfaces);
6715
if (RT_FAILURE(vrc))
6716
throw setError(VBOX_E_FILE_ERROR,
6717
tr("Could not open the medium storage unit '%s'%s"),
6718
pMedium->m->strLocationFull.c_str(),
6719
vdError(vrc).c_str());
6722
Utf8Str targetFormat(pTarget->m->strFormat);
6723
Utf8Str targetLocation(pTarget->m->strLocationFull);
6725
Assert( pTarget->m->state == MediumState_Creating
6726
|| pTarget->m->state == MediumState_LockedWrite);
6727
Assert(m->state == MediumState_LockedRead);
6728
Assert( pParent.isNull()
6729
|| pParent->m->state == MediumState_LockedRead);
6731
/* unlock before the potentially lengthy operation */
6734
/* ensure the target directory exists */
6735
rc = VirtualBox::ensureFilePathExists(targetLocation);
6740
vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
6741
ComAssertRCThrow(vrc, E_FAIL);
6745
/* Open all media in the target chain. */
6746
MediumLockList::Base::const_iterator targetListBegin =
6747
task.mpTargetMediumLockList->GetBegin();
6748
MediumLockList::Base::const_iterator targetListEnd =
6749
task.mpTargetMediumLockList->GetEnd();
6750
for (MediumLockList::Base::const_iterator it = targetListBegin;
6751
it != targetListEnd;
6754
const MediumLock &mediumLock = *it;
6755
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6757
/* If the target medium is not created yet there's no
6758
* reason to open it. */
6759
if (pMedium == pTarget && fCreatingTarget)
6762
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6765
Assert( pMedium->m->state == MediumState_LockedRead
6766
|| pMedium->m->state == MediumState_LockedWrite);
6768
unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
6769
if (pMedium->m->state != MediumState_LockedWrite)
6770
uOpenFlags = VD_OPEN_FLAGS_READONLY;
6771
if (pMedium->m->type == MediumType_Shareable)
6772
uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6774
/* Open all media in appropriate mode. */
6775
vrc = VDOpen(targetHdd,
6776
pMedium->m->strFormat.c_str(),
6777
pMedium->m->strLocationFull.c_str(),
6779
pMedium->m->vdImageIfaces);
6780
if (RT_FAILURE(vrc))
6781
throw setError(VBOX_E_FILE_ERROR,
6782
tr("Could not open the medium storage unit '%s'%s"),
6783
pMedium->m->strLocationFull.c_str(),
6784
vdError(vrc).c_str());
6787
/** @todo r=klaus target isn't locked, race getting the state */
6791
targetFormat.c_str(),
6792
(fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
6793
false /* fMoveByRename */,
6797
VD_OPEN_FLAGS_NORMAL,
6798
NULL /* pVDIfsOperation */,
6799
pTarget->m->vdImageIfaces,
6800
task.mVDOperationIfaces);
6801
if (RT_FAILURE(vrc))
6802
throw setError(VBOX_E_FILE_ERROR,
6803
tr("Could not create the clone medium '%s'%s"),
6804
targetLocation.c_str(), vdError(vrc).c_str());
6806
size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
6807
logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
6808
unsigned uImageFlags;
6809
vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
6810
if (RT_SUCCESS(vrc))
6811
variant = (MediumVariant_T)uImageFlags;
6813
catch (HRESULT aRC) { rc = aRC; }
6815
VDDestroy(targetHdd);
6817
catch (HRESULT aRC) { rc = aRC; }
6821
catch (HRESULT aRC) { rc = aRC; }
6823
/* Only do the parent changes for newly created media. */
6824
if (SUCCEEDED(rc) && fCreatingTarget)
6826
/* we set mParent & children() */
6827
AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6829
Assert(pTarget->m->pParent.isNull());
6833
/* associate the clone with the parent and deassociate
6834
* from VirtualBox */
6835
pTarget->m->pParent = pParent;
6836
pParent->m->llChildren.push_back(pTarget);
6838
/* register with mVirtualBox as the last step and move to
6839
* Created state only on success (leaving an orphan file is
6840
* better than breaking media registry consistency) */
6841
rc = pParent->m->pVirtualBox->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */);
6844
/* break parent association on failure to register */
6845
pTarget->deparent(); // removes target from parent
6850
rc = m->pVirtualBox->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */);
6854
if (fCreatingTarget)
6856
AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
6860
pTarget->m->state = MediumState_Created;
6862
pTarget->m->size = size;
6863
pTarget->m->logicalSize = logicalSize;
6864
pTarget->m->variant = variant;
6868
/* back to NotCreated on failure */
6869
pTarget->m->state = MediumState_NotCreated;
6871
/* reset UUID to prevent it from being reused next time */
6873
unconst(pTarget->m->id).clear();
6877
// now, at the end of this task (always asynchronous), save the settings
6879
// save the settings
6880
GuidList llRegistriesThatNeedSaving;
6881
addToRegistryIDList(llRegistriesThatNeedSaving);
6882
rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
6885
/* Everything is explicitly unlocked when the task exits,
6886
* as the task destruction also destroys the source chain. */
6888
/* Make sure the source chain is released early. It could happen
6889
* that we get a deadlock in Appliance::Import when Medium::Close
6890
* is called & the source chain is released at the same time. */
6891
task.mpSourceMediumLockList->Clear();
6897
* Implementation code for the "delete" task.
6899
* This task always gets started from Medium::deleteStorage() and can run
6900
* synchronously or asynchronously depending on the "wait" parameter passed to
6906
HRESULT Medium::taskDeleteHandler(Medium::DeleteTask &task)
6913
/* The lock is also used as a signal from the task initiator (which
6914
* releases it only after RTThreadCreate()) that we can start the job */
6915
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6918
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6919
ComAssertRCThrow(vrc, E_FAIL);
6921
Utf8Str format(m->strFormat);
6922
Utf8Str location(m->strLocationFull);
6924
/* unlock before the potentially lengthy operation */
6925
Assert(m->state == MediumState_Deleting);
6933
VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
6935
if (RT_SUCCESS(vrc))
6936
vrc = VDClose(hdd, true /* fDelete */);
6938
if (RT_FAILURE(vrc))
6939
throw setError(VBOX_E_FILE_ERROR,
6940
tr("Could not delete the medium storage unit '%s'%s"),
6941
location.c_str(), vdError(vrc).c_str());
6944
catch (HRESULT aRC) { rc = aRC; }
6948
catch (HRESULT aRC) { rc = aRC; }
6950
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6952
/* go to the NotCreated state even on failure since the storage
6953
* may have been already partially deleted and cannot be used any
6954
* more. One will be able to manually re-open the storage if really
6955
* needed to re-register it. */
6956
m->state = MediumState_NotCreated;
6958
/* Reset UUID to prevent Create* from reusing it again */
6959
unconst(m->id).clear();
6965
* Implementation code for the "reset" task.
6967
* This always gets started asynchronously from Medium::Reset().
6972
HRESULT Medium::taskResetHandler(Medium::ResetTask &task)
6976
uint64_t size = 0, logicalSize = 0;
6977
MediumVariant_T variant = MediumVariant_Standard;
6981
/* The lock is also used as a signal from the task initiator (which
6982
* releases it only after RTThreadCreate()) that we can start the job */
6983
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6985
/// @todo Below we use a pair of delete/create operations to reset
6986
/// the diff contents but the most efficient way will of course be
6987
/// to add a VDResetDiff() API call
6990
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6991
ComAssertRCThrow(vrc, E_FAIL);
6994
Utf8Str format(m->strFormat);
6995
Utf8Str location(m->strLocationFull);
6997
Medium *pParent = m->pParent;
6998
Guid parentId = pParent->m->id;
6999
Utf8Str parentFormat(pParent->m->strFormat);
7000
Utf8Str parentLocation(pParent->m->strLocationFull);
7002
Assert(m->state == MediumState_LockedWrite);
7004
/* unlock before the potentially lengthy operation */
7009
/* Open all media in the target chain but the last. */
7010
MediumLockList::Base::const_iterator targetListBegin =
7011
task.mpMediumLockList->GetBegin();
7012
MediumLockList::Base::const_iterator targetListEnd =
7013
task.mpMediumLockList->GetEnd();
7014
for (MediumLockList::Base::const_iterator it = targetListBegin;
7015
it != targetListEnd;
7018
const MediumLock &mediumLock = *it;
7019
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7021
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7023
/* sanity check, "this" is checked above */
7024
Assert( pMedium == this
7025
|| pMedium->m->state == MediumState_LockedRead);
7027
/* Open all media in appropriate mode. */
7029
pMedium->m->strFormat.c_str(),
7030
pMedium->m->strLocationFull.c_str(),
7031
VD_OPEN_FLAGS_READONLY,
7032
pMedium->m->vdImageIfaces);
7033
if (RT_FAILURE(vrc))
7034
throw setError(VBOX_E_FILE_ERROR,
7035
tr("Could not open the medium storage unit '%s'%s"),
7036
pMedium->m->strLocationFull.c_str(),
7037
vdError(vrc).c_str());
7039
/* Done when we hit the media which should be reset */
7040
if (pMedium == this)
7044
/* first, delete the storage unit */
7045
vrc = VDClose(hdd, true /* fDelete */);
7046
if (RT_FAILURE(vrc))
7047
throw setError(VBOX_E_FILE_ERROR,
7048
tr("Could not delete the medium storage unit '%s'%s"),
7049
location.c_str(), vdError(vrc).c_str());
7051
/* next, create it again */
7053
parentFormat.c_str(),
7054
parentLocation.c_str(),
7055
VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
7057
if (RT_FAILURE(vrc))
7058
throw setError(VBOX_E_FILE_ERROR,
7059
tr("Could not open the medium storage unit '%s'%s"),
7060
parentLocation.c_str(), vdError(vrc).c_str());
7062
vrc = VDCreateDiff(hdd,
7065
/// @todo use the same medium variant as before
7066
VD_IMAGE_FLAGS_NONE,
7070
VD_OPEN_FLAGS_NORMAL,
7072
task.mVDOperationIfaces);
7073
if (RT_FAILURE(vrc))
7074
throw setError(VBOX_E_FILE_ERROR,
7075
tr("Could not create the differencing medium storage unit '%s'%s"),
7076
location.c_str(), vdError(vrc).c_str());
7078
size = VDGetFileSize(hdd, VD_LAST_IMAGE);
7079
logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
7080
unsigned uImageFlags;
7081
vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7082
if (RT_SUCCESS(vrc))
7083
variant = (MediumVariant_T)uImageFlags;
7085
catch (HRESULT aRC) { rc = aRC; }
7089
catch (HRESULT aRC) { rc = aRC; }
7091
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7094
m->logicalSize = logicalSize;
7095
m->variant = variant;
7099
/* unlock ourselves when done */
7100
HRESULT rc2 = UnlockWrite(NULL);
7104
/* Note that in sync mode, it's the caller's responsibility to
7105
* unlock the medium. */
7111
* Implementation code for the "compact" task.
7116
HRESULT Medium::taskCompactHandler(Medium::CompactTask &task)
7120
/* Lock all in {parent,child} order. The lock is also used as a
7121
* signal from the task initiator (which releases it only after
7122
* RTThreadCreate()) that we can start the job. */
7123
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7128
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7129
ComAssertRCThrow(vrc, E_FAIL);
7133
/* Open all media in the chain. */
7134
MediumLockList::Base::const_iterator mediumListBegin =
7135
task.mpMediumLockList->GetBegin();
7136
MediumLockList::Base::const_iterator mediumListEnd =
7137
task.mpMediumLockList->GetEnd();
7138
MediumLockList::Base::const_iterator mediumListLast =
7141
for (MediumLockList::Base::const_iterator it = mediumListBegin;
7142
it != mediumListEnd;
7145
const MediumLock &mediumLock = *it;
7146
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7147
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7150
if (it == mediumListLast)
7151
Assert(pMedium->m->state == MediumState_LockedWrite);
7153
Assert(pMedium->m->state == MediumState_LockedRead);
7155
/* Open all media but last in read-only mode. Do not handle
7156
* shareable media, as compaction and sharing are mutually
7159
pMedium->m->strFormat.c_str(),
7160
pMedium->m->strLocationFull.c_str(),
7161
(it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
7162
pMedium->m->vdImageIfaces);
7163
if (RT_FAILURE(vrc))
7164
throw setError(VBOX_E_FILE_ERROR,
7165
tr("Could not open the medium storage unit '%s'%s"),
7166
pMedium->m->strLocationFull.c_str(),
7167
vdError(vrc).c_str());
7170
Assert(m->state == MediumState_LockedWrite);
7172
Utf8Str location(m->strLocationFull);
7174
/* unlock before the potentially lengthy operation */
7177
vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
7178
if (RT_FAILURE(vrc))
7180
if (vrc == VERR_NOT_SUPPORTED)
7181
throw setError(VBOX_E_NOT_SUPPORTED,
7182
tr("Compacting is not yet supported for medium '%s'"),
7184
else if (vrc == VERR_NOT_IMPLEMENTED)
7185
throw setError(E_NOTIMPL,
7186
tr("Compacting is not implemented, medium '%s'"),
7189
throw setError(VBOX_E_FILE_ERROR,
7190
tr("Could not compact medium '%s'%s"),
7192
vdError(vrc).c_str());
7195
catch (HRESULT aRC) { rc = aRC; }
7199
catch (HRESULT aRC) { rc = aRC; }
7201
/* Everything is explicitly unlocked when the task exits,
7202
* as the task destruction also destroys the media chain. */
7208
* Implementation code for the "resize" task.
7213
HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task)
7217
/* Lock all in {parent,child} order. The lock is also used as a
7218
* signal from the task initiator (which releases it only after
7219
* RTThreadCreate()) that we can start the job. */
7220
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7225
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7226
ComAssertRCThrow(vrc, E_FAIL);
7230
/* Open all media in the chain. */
7231
MediumLockList::Base::const_iterator mediumListBegin =
7232
task.mpMediumLockList->GetBegin();
7233
MediumLockList::Base::const_iterator mediumListEnd =
7234
task.mpMediumLockList->GetEnd();
7235
MediumLockList::Base::const_iterator mediumListLast =
7238
for (MediumLockList::Base::const_iterator it = mediumListBegin;
7239
it != mediumListEnd;
7242
const MediumLock &mediumLock = *it;
7243
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7244
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7247
if (it == mediumListLast)
7248
Assert(pMedium->m->state == MediumState_LockedWrite);
7250
Assert(pMedium->m->state == MediumState_LockedRead);
7252
/* Open all media but last in read-only mode. Do not handle
7253
* shareable media, as compaction and sharing are mutually
7256
pMedium->m->strFormat.c_str(),
7257
pMedium->m->strLocationFull.c_str(),
7258
(it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
7259
pMedium->m->vdImageIfaces);
7260
if (RT_FAILURE(vrc))
7261
throw setError(VBOX_E_FILE_ERROR,
7262
tr("Could not open the medium storage unit '%s'%s"),
7263
pMedium->m->strLocationFull.c_str(),
7264
vdError(vrc).c_str());
7267
Assert(m->state == MediumState_LockedWrite);
7269
Utf8Str location(m->strLocationFull);
7271
/* unlock before the potentially lengthy operation */
7274
VDGEOMETRY geo = {0, 0, 0}; /* auto */
7275
vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
7276
if (RT_FAILURE(vrc))
7278
if (vrc == VERR_NOT_SUPPORTED)
7279
throw setError(VBOX_E_NOT_SUPPORTED,
7280
tr("Compacting is not yet supported for medium '%s'"),
7282
else if (vrc == VERR_NOT_IMPLEMENTED)
7283
throw setError(E_NOTIMPL,
7284
tr("Compacting is not implemented, medium '%s'"),
7287
throw setError(VBOX_E_FILE_ERROR,
7288
tr("Could not compact medium '%s'%s"),
7290
vdError(vrc).c_str());
7293
catch (HRESULT aRC) { rc = aRC; }
7297
catch (HRESULT aRC) { rc = aRC; }
7299
/* Everything is explicitly unlocked when the task exits,
7300
* as the task destruction also destroys the media chain. */
7306
* Implementation code for the "export" task.
7308
* This only gets started from Medium::exportFile() and always runs
7309
* asynchronously. It doesn't touch anything configuration related, so
7310
* we never save the VirtualBox.xml file here.
7315
HRESULT Medium::taskExportHandler(Medium::ExportTask &task)
7321
/* Lock all in {parent,child} order. The lock is also used as a
7322
* signal from the task initiator (which releases it only after
7323
* RTThreadCreate()) that we can start the job. */
7324
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7327
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7328
ComAssertRCThrow(vrc, E_FAIL);
7332
/* Open all media in the source chain. */
7333
MediumLockList::Base::const_iterator sourceListBegin =
7334
task.mpSourceMediumLockList->GetBegin();
7335
MediumLockList::Base::const_iterator sourceListEnd =
7336
task.mpSourceMediumLockList->GetEnd();
7337
for (MediumLockList::Base::const_iterator it = sourceListBegin;
7338
it != sourceListEnd;
7341
const MediumLock &mediumLock = *it;
7342
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7343
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7346
Assert(pMedium->m->state == MediumState_LockedRead);
7348
/* Open all media in read-only mode. */
7350
pMedium->m->strFormat.c_str(),
7351
pMedium->m->strLocationFull.c_str(),
7352
VD_OPEN_FLAGS_READONLY,
7353
pMedium->m->vdImageIfaces);
7354
if (RT_FAILURE(vrc))
7355
throw setError(VBOX_E_FILE_ERROR,
7356
tr("Could not open the medium storage unit '%s'%s"),
7357
pMedium->m->strLocationFull.c_str(),
7358
vdError(vrc).c_str());
7361
Utf8Str targetFormat(task.mFormat->getId());
7362
Utf8Str targetLocation(task.mFilename);
7364
Assert(m->state == MediumState_LockedRead);
7366
/* unlock before the potentially lengthy operation */
7369
/* ensure the target directory exists */
7370
rc = VirtualBox::ensureFilePathExists(targetLocation);
7375
vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
7376
ComAssertRCThrow(vrc, E_FAIL);
7383
targetFormat.c_str(),
7384
targetLocation.c_str(),
7385
false /* fMoveByRename */,
7388
NULL /* pDstUuid */,
7389
VD_OPEN_FLAGS_NORMAL,
7390
NULL /* pVDIfsOperation */,
7391
task.mVDImageIfaces,
7392
task.mVDOperationIfaces);
7393
if (RT_FAILURE(vrc))
7394
throw setError(VBOX_E_FILE_ERROR,
7395
tr("Could not create the clone medium '%s'%s"),
7396
targetLocation.c_str(), vdError(vrc).c_str());
7398
catch (HRESULT aRC) { rc = aRC; }
7400
VDDestroy(targetHdd);
7402
catch (HRESULT aRC) { rc = aRC; }
7406
catch (HRESULT aRC) { rc = aRC; }
7408
/* Everything is explicitly unlocked when the task exits,
7409
* as the task destruction also destroys the source chain. */
7411
/* Make sure the source chain is released early, otherwise it can
7412
* lead to deadlocks with concurrent IAppliance activities. */
7413
task.mpSourceMediumLockList->Clear();
7419
* Implementation code for the "import" task.
7421
* This only gets started from Medium::importFile() and always runs
7422
* asynchronously. It potentially touches the media registry, so we
7423
* always save the VirtualBox.xml file when we're done here.
7428
HRESULT Medium::taskImportHandler(Medium::ImportTask &task)
7432
const ComObjPtr<Medium> &pParent = task.mParent;
7434
bool fCreatingTarget = false;
7436
uint64_t size = 0, logicalSize = 0;
7437
MediumVariant_T variant = MediumVariant_Standard;
7438
bool fGenerateUuid = false;
7442
/* Lock all in {parent,child} order. The lock is also used as a
7443
* signal from the task initiator (which releases it only after
7444
* RTThreadCreate()) that we can start the job. */
7445
AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
7447
fCreatingTarget = m->state == MediumState_Creating;
7449
/* The object may request a specific UUID (through a special form of
7450
* the setLocation() argument). Otherwise we have to generate it */
7451
Guid targetId = m->id;
7452
fGenerateUuid = targetId.isEmpty();
7456
/* VirtualBox::registerHardDisk() will need UUID */
7457
unconst(m->id) = targetId;
7462
int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7463
ComAssertRCThrow(vrc, E_FAIL);
7467
/* Open source medium. */
7469
task.mFormat->getId().c_str(),
7470
task.mFilename.c_str(),
7471
VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL,
7472
task.mVDImageIfaces);
7473
if (RT_FAILURE(vrc))
7474
throw setError(VBOX_E_FILE_ERROR,
7475
tr("Could not open the medium storage unit '%s'%s"),
7476
task.mFilename.c_str(),
7477
vdError(vrc).c_str());
7479
Utf8Str targetFormat(m->strFormat);
7480
Utf8Str targetLocation(m->strLocationFull);
7482
Assert( m->state == MediumState_Creating
7483
|| m->state == MediumState_LockedWrite);
7484
Assert( pParent.isNull()
7485
|| pParent->m->state == MediumState_LockedRead);
7487
/* unlock before the potentially lengthy operation */
7490
/* ensure the target directory exists */
7491
rc = VirtualBox::ensureFilePathExists(targetLocation);
7496
vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
7497
ComAssertRCThrow(vrc, E_FAIL);
7501
/* Open all media in the target chain. */
7502
MediumLockList::Base::const_iterator targetListBegin =
7503
task.mpTargetMediumLockList->GetBegin();
7504
MediumLockList::Base::const_iterator targetListEnd =
7505
task.mpTargetMediumLockList->GetEnd();
7506
for (MediumLockList::Base::const_iterator it = targetListBegin;
7507
it != targetListEnd;
7510
const MediumLock &mediumLock = *it;
7511
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7513
/* If the target medium is not created yet there's no
7514
* reason to open it. */
7515
if (pMedium == this && fCreatingTarget)
7518
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7521
Assert( pMedium->m->state == MediumState_LockedRead
7522
|| pMedium->m->state == MediumState_LockedWrite);
7524
unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7525
if (pMedium->m->state != MediumState_LockedWrite)
7526
uOpenFlags = VD_OPEN_FLAGS_READONLY;
7527
if (pMedium->m->type == MediumType_Shareable)
7528
uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7530
/* Open all media in appropriate mode. */
7531
vrc = VDOpen(targetHdd,
7532
pMedium->m->strFormat.c_str(),
7533
pMedium->m->strLocationFull.c_str(),
7535
pMedium->m->vdImageIfaces);
7536
if (RT_FAILURE(vrc))
7537
throw setError(VBOX_E_FILE_ERROR,
7538
tr("Could not open the medium storage unit '%s'%s"),
7539
pMedium->m->strLocationFull.c_str(),
7540
vdError(vrc).c_str());
7543
/** @todo r=klaus target isn't locked, race getting the state */
7547
targetFormat.c_str(),
7548
(fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
7549
false /* fMoveByRename */,
7553
VD_OPEN_FLAGS_NORMAL,
7554
NULL /* pVDIfsOperation */,
7556
task.mVDOperationIfaces);
7557
if (RT_FAILURE(vrc))
7558
throw setError(VBOX_E_FILE_ERROR,
7559
tr("Could not create the clone medium '%s'%s"),
7560
targetLocation.c_str(), vdError(vrc).c_str());
7562
size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
7563
logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
7564
unsigned uImageFlags;
7565
vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
7566
if (RT_SUCCESS(vrc))
7567
variant = (MediumVariant_T)uImageFlags;
7569
catch (HRESULT aRC) { rc = aRC; }
7571
VDDestroy(targetHdd);
7573
catch (HRESULT aRC) { rc = aRC; }
7577
catch (HRESULT aRC) { rc = aRC; }
7579
/* Only do the parent changes for newly created media. */
7580
if (SUCCEEDED(rc) && fCreatingTarget)
7582
/* we set mParent & children() */
7583
AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7585
Assert(m->pParent.isNull());
7589
/* associate the clone with the parent and deassociate
7590
* from VirtualBox */
7591
m->pParent = pParent;
7592
pParent->m->llChildren.push_back(this);
7594
/* register with mVirtualBox as the last step and move to
7595
* Created state only on success (leaving an orphan file is
7596
* better than breaking media registry consistency) */
7597
rc = pParent->m->pVirtualBox->registerHardDisk(this, NULL /* llRegistriesThatNeedSaving */);
7600
/* break parent association on failure to register */
7601
this->deparent(); // removes target from parent
7606
rc = m->pVirtualBox->registerHardDisk(this, NULL /* pllRegistriesThatNeedSaving */);
7610
if (fCreatingTarget)
7612
AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
7616
m->state = MediumState_Created;
7619
m->logicalSize = logicalSize;
7620
m->variant = variant;
7624
/* back to NotCreated on failure */
7625
m->state = MediumState_NotCreated;
7627
/* reset UUID to prevent it from being reused next time */
7629
unconst(m->id).clear();
7633
// now, at the end of this task (always asynchronous), save the settings
7635
// save the settings
7636
GuidList llRegistriesThatNeedSaving;
7637
addToRegistryIDList(llRegistriesThatNeedSaving);
7638
rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
7641
/* Everything is explicitly unlocked when the task exits,
7642
* as the task destruction also destroys the target chain. */
7644
/* Make sure the target chain is released early, otherwise it can
7645
* lead to deadlocks with concurrent IAppliance activities. */
7646
task.mpTargetMediumLockList->Clear();
7651
/* vi: set tabstop=4 shiftwidth=4 expandtab: */