1
/* $Id: MediumImpl.cpp $ */
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/SupportErrorInfo.h>
30
#include <VBox/settings.h>
32
#include <iprt/param.h>
33
#include <iprt/path.h>
34
#include <iprt/file.h>
37
#include <VBox/VBoxHDD.h>
41
////////////////////////////////////////////////////////////////////////////////
43
// Medium data definition
45
////////////////////////////////////////////////////////////////////////////////
47
/** Describes how a machine refers to this medium. */
50
/** Equality predicate for stdc++. */
51
struct EqualsTo : public std::unary_function <BackRef, bool>
53
explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
55
bool operator()(const argument_type &aThat) const
57
return aThat.machineId == machineId;
63
typedef std::list<Guid> GuidList;
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),
101
numCreateDiffTasks(0),
105
/** weak VirtualBox parent */
106
VirtualBox * const pVirtualBox;
109
Utf8Str strDescription;
111
MediumVariant_T variant;
113
Utf8Str strLocationFull;
115
Utf8Str strLastAccessError;
117
// pParent and llChildren are protected by VirtualBox::getMediaTreeLockHandle()
118
ComObjPtr<Medium> pParent;
119
MediaList llChildren; // to add a child, just call push_back; to remove a child, call child->deparent() which does a lookup
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; /*< In MBytes. */
136
HDDOpenMode hddOpenMode;
140
/** the following members are invalid after changing UUID on open */
142
bool setParentId : 1;
148
typedef std::map <Bstr, Bstr> PropertyMap;
149
PropertyMap properties;
153
uint32_t numCreateDiffTasks;
155
Utf8Str vdError; /*< Error remembered by the VD error callback. */
157
VDINTERFACE vdIfError;
158
VDINTERFACEERROR vdIfCallsError;
160
VDINTERFACE vdIfConfig;
161
VDINTERFACECONFIG vdIfCallsConfig;
163
VDINTERFACE vdIfTcpNet;
164
VDINTERFACETCPNET vdIfCallsTcpNet;
166
PVDINTERFACE vdDiskIfaces;
169
typedef struct VDSOCKETINT
171
/** Socket handle. */
173
} VDSOCKETINT, *PVDSOCKETINT;
175
////////////////////////////////////////////////////////////////////////////////
179
////////////////////////////////////////////////////////////////////////////////
182
* Medium::Task class for asynchronous operations.
184
* @note Instances of this class must be created using new() because the
185
* task thread function will delete them when the task is complete.
187
* @note The constructor of this class adds a caller on the managed Medium
188
* object which is automatically released upon destruction.
193
Task(Medium *aMedium, Progress *aProgress)
194
: mVDOperationIfaces(NULL),
195
m_pfNeedsSaveSettings(NULL),
197
mMediumCaller(aMedium),
198
mThread(NIL_RTTHREAD),
201
AssertReturnVoidStmt(aMedium, mRC = E_FAIL);
202
mRC = mMediumCaller.rc();
206
/* Set up a per-operation progress interface, can be used freely (for
207
* binary operations you can use it either on the source or target). */
208
mVDIfCallsProgress.cbSize = sizeof(VDINTERFACEPROGRESS);
209
mVDIfCallsProgress.enmInterface = VDINTERFACETYPE_PROGRESS;
210
mVDIfCallsProgress.pfnProgress = vdProgressCall;
211
int vrc = VDInterfaceAdd(&mVDIfProgress,
212
"Medium::Task::vdInterfaceProgress",
213
VDINTERFACETYPE_PROGRESS,
216
&mVDOperationIfaces);
222
// Make all destructors virtual. Just in case.
226
HRESULT rc() const { return mRC; }
227
bool isOk() const { return SUCCEEDED(rc()); }
229
static int fntMediumTask(RTTHREAD aThread, void *pvUser);
231
bool isAsync() { return mThread != NIL_RTTHREAD; }
233
PVDINTERFACE mVDOperationIfaces;
235
// Whether the caller needs to call VirtualBox::saveSettings() after
236
// the task function returns. Only used in synchronous (wait) mode;
237
// otherwise the task will save the settings itself.
238
bool *m_pfNeedsSaveSettings;
240
const ComObjPtr<Medium> mMedium;
241
AutoCaller mMediumCaller;
243
friend HRESULT Medium::runNow(Medium::Task*, bool*);
250
virtual HRESULT handler() = 0;
252
const ComObjPtr<Progress> mProgress;
254
static DECLCALLBACK(int) vdProgressCall(void *pvUser, unsigned uPercent);
256
VDINTERFACE mVDIfProgress;
257
VDINTERFACEPROGRESS mVDIfCallsProgress;
260
class Medium::CreateBaseTask : public Medium::Task
263
CreateBaseTask(Medium *aMedium,
266
MediumVariant_T aVariant)
267
: Medium::Task(aMedium, aProgress),
273
MediumVariant_T mVariant;
276
virtual HRESULT handler();
279
class Medium::CreateDiffTask : public Medium::Task
282
CreateDiffTask(Medium *aMedium,
285
MediumVariant_T aVariant,
286
MediumLockList *aMediumLockList,
287
bool fKeepMediumLockList = false)
288
: Medium::Task(aMedium, aProgress),
289
mpMediumLockList(aMediumLockList),
292
mTargetCaller(aTarget),
293
mfKeepMediumLockList(fKeepMediumLockList)
295
AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
296
mRC = mTargetCaller.rc();
303
if (!mfKeepMediumLockList && mpMediumLockList)
304
delete mpMediumLockList;
307
MediumLockList *mpMediumLockList;
309
const ComObjPtr<Medium> mTarget;
310
MediumVariant_T mVariant;
313
virtual HRESULT handler();
315
AutoCaller mTargetCaller;
316
bool mfKeepMediumLockList;
319
class Medium::CloneTask : public Medium::Task
322
CloneTask(Medium *aMedium,
325
MediumVariant_T aVariant,
327
MediumLockList *aSourceMediumLockList,
328
MediumLockList *aTargetMediumLockList,
329
bool fKeepSourceMediumLockList = false,
330
bool fKeepTargetMediumLockList = false)
331
: Medium::Task(aMedium, aProgress),
334
mpSourceMediumLockList(aSourceMediumLockList),
335
mpTargetMediumLockList(aTargetMediumLockList),
337
mTargetCaller(aTarget),
338
mParentCaller(aParent),
339
mfKeepSourceMediumLockList(fKeepSourceMediumLockList),
340
mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
342
AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
343
mRC = mTargetCaller.rc();
346
/* aParent may be NULL */
347
mRC = mParentCaller.rc();
350
AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
351
AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
356
if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
357
delete mpSourceMediumLockList;
358
if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
359
delete mpTargetMediumLockList;
362
const ComObjPtr<Medium> mTarget;
363
const ComObjPtr<Medium> mParent;
364
MediumLockList *mpSourceMediumLockList;
365
MediumLockList *mpTargetMediumLockList;
366
MediumVariant_T mVariant;
369
virtual HRESULT handler();
371
AutoCaller mTargetCaller;
372
AutoCaller mParentCaller;
373
bool mfKeepSourceMediumLockList;
374
bool mfKeepTargetMediumLockList;
377
class Medium::CompactTask : public Medium::Task
380
CompactTask(Medium *aMedium,
382
MediumLockList *aMediumLockList,
383
bool fKeepMediumLockList = false)
384
: Medium::Task(aMedium, aProgress),
385
mpMediumLockList(aMediumLockList),
386
mfKeepMediumLockList(fKeepMediumLockList)
388
AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
393
if (!mfKeepMediumLockList && mpMediumLockList)
394
delete mpMediumLockList;
397
MediumLockList *mpMediumLockList;
400
virtual HRESULT handler();
402
bool mfKeepMediumLockList;
405
class Medium::ResetTask : public Medium::Task
408
ResetTask(Medium *aMedium,
410
MediumLockList *aMediumLockList,
411
bool fKeepMediumLockList = false)
412
: Medium::Task(aMedium, aProgress),
413
mpMediumLockList(aMediumLockList),
414
mfKeepMediumLockList(fKeepMediumLockList)
419
if (!mfKeepMediumLockList && mpMediumLockList)
420
delete mpMediumLockList;
423
MediumLockList *mpMediumLockList;
426
virtual HRESULT handler();
428
bool mfKeepMediumLockList;
431
class Medium::DeleteTask : public Medium::Task
434
DeleteTask(Medium *aMedium,
436
MediumLockList *aMediumLockList,
437
bool fKeepMediumLockList = false)
438
: Medium::Task(aMedium, aProgress),
439
mpMediumLockList(aMediumLockList),
440
mfKeepMediumLockList(fKeepMediumLockList)
445
if (!mfKeepMediumLockList && mpMediumLockList)
446
delete mpMediumLockList;
449
MediumLockList *mpMediumLockList;
452
virtual HRESULT handler();
454
bool mfKeepMediumLockList;
457
class Medium::MergeTask : public Medium::Task
460
MergeTask(Medium *aMedium,
463
Medium *aParentForTarget,
464
const MediaList &aChildrenToReparent,
466
MediumLockList *aMediumLockList,
467
bool fKeepMediumLockList = false)
468
: Medium::Task(aMedium, aProgress),
470
mfMergeForward(fMergeForward),
471
mParentForTarget(aParentForTarget),
472
mChildrenToReparent(aChildrenToReparent),
473
mpMediumLockList(aMediumLockList),
474
mTargetCaller(aTarget),
475
mParentForTargetCaller(aParentForTarget),
476
mfChildrenCaller(false),
477
mfKeepMediumLockList(fKeepMediumLockList)
479
AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
480
for (MediaList::const_iterator it = mChildrenToReparent.begin();
481
it != mChildrenToReparent.end();
484
HRESULT rc2 = (*it)->addCaller();
488
for (MediaList::const_iterator it2 = mChildrenToReparent.begin();
492
(*it2)->releaseCaller();
497
mfChildrenCaller = true;
502
if (!mfKeepMediumLockList && mpMediumLockList)
503
delete mpMediumLockList;
504
if (mfChildrenCaller)
506
for (MediaList::const_iterator it = mChildrenToReparent.begin();
507
it != mChildrenToReparent.end();
510
(*it)->releaseCaller();
515
const ComObjPtr<Medium> mTarget;
517
/* When mChildrenToReparent is empty then mParentForTarget is non-null.
518
* In other words: they are used in different cases. */
519
const ComObjPtr<Medium> mParentForTarget;
520
MediaList mChildrenToReparent;
521
MediumLockList *mpMediumLockList;
524
virtual HRESULT handler();
526
AutoCaller mTargetCaller;
527
AutoCaller mParentForTargetCaller;
528
bool mfChildrenCaller;
529
bool mfKeepMediumLockList;
533
* Thread function for time-consuming medium tasks.
535
* @param pvUser Pointer to the Medium::Task instance.
538
DECLCALLBACK(int) Medium::Task::fntMediumTask(RTTHREAD aThread, void *pvUser)
541
AssertReturn(pvUser, (int)E_INVALIDARG);
542
Medium::Task *pTask = static_cast<Medium::Task *>(pvUser);
544
pTask->mThread = aThread;
546
HRESULT rc = pTask->handler();
548
/* complete the progress if run asynchronously */
549
if (pTask->isAsync())
551
if (!pTask->mProgress.isNull())
552
pTask->mProgress->notifyComplete(rc);
555
/* pTask is no longer needed, delete it. */
558
LogFlowFunc(("rc=%Rhrc\n", rc));
565
* PFNVDPROGRESS callback handler for Task operations.
567
* @param pvUser Pointer to the Progress instance.
568
* @param uPercent Completetion precentage (0-100).
571
DECLCALLBACK(int) Medium::Task::vdProgressCall(void *pvUser, unsigned uPercent)
573
Progress *that = static_cast<Progress *>(pvUser);
577
/* update the progress object, capping it at 99% as the final percent
578
* is used for additional operations like setting the UUIDs and similar. */
579
HRESULT rc = that->SetCurrentOperationProgress(uPercent * 99 / 100);
583
return VERR_CANCELLED;
585
return VERR_INVALID_STATE;
593
* Implementation code for the "create base" task.
595
HRESULT Medium::CreateBaseTask::handler()
597
return mMedium->taskCreateBaseHandler(*this);
601
* Implementation code for the "create diff" task.
603
HRESULT Medium::CreateDiffTask::handler()
605
return mMedium->taskCreateDiffHandler(*this);
609
* Implementation code for the "clone" task.
611
HRESULT Medium::CloneTask::handler()
613
return mMedium->taskCloneHandler(*this);
617
* Implementation code for the "compact" task.
619
HRESULT Medium::CompactTask::handler()
621
return mMedium->taskCompactHandler(*this);
625
* Implementation code for the "reset" task.
627
HRESULT Medium::ResetTask::handler()
629
return mMedium->taskResetHandler(*this);
633
* Implementation code for the "delete" task.
635
HRESULT Medium::DeleteTask::handler()
637
return mMedium->taskDeleteHandler(*this);
641
* Implementation code for the "merge" task.
643
HRESULT Medium::MergeTask::handler()
645
return mMedium->taskMergeHandler(*this);
649
////////////////////////////////////////////////////////////////////////////////
651
// Medium constructor / destructor
653
////////////////////////////////////////////////////////////////////////////////
655
DEFINE_EMPTY_CTOR_DTOR(Medium)
657
HRESULT Medium::FinalConstruct()
661
/* Initialize the callbacks of the VD error interface */
662
m->vdIfCallsError.cbSize = sizeof(VDINTERFACEERROR);
663
m->vdIfCallsError.enmInterface = VDINTERFACETYPE_ERROR;
664
m->vdIfCallsError.pfnError = vdErrorCall;
665
m->vdIfCallsError.pfnMessage = NULL;
667
/* Initialize the callbacks of the VD config interface */
668
m->vdIfCallsConfig.cbSize = sizeof(VDINTERFACECONFIG);
669
m->vdIfCallsConfig.enmInterface = VDINTERFACETYPE_CONFIG;
670
m->vdIfCallsConfig.pfnAreKeysValid = vdConfigAreKeysValid;
671
m->vdIfCallsConfig.pfnQuerySize = vdConfigQuerySize;
672
m->vdIfCallsConfig.pfnQuery = vdConfigQuery;
674
/* Initialize the callbacks of the VD TCP interface (we always use the host
675
* IP stack for now) */
676
m->vdIfCallsTcpNet.cbSize = sizeof(VDINTERFACETCPNET);
677
m->vdIfCallsTcpNet.enmInterface = VDINTERFACETYPE_TCPNET;
678
m->vdIfCallsTcpNet.pfnSocketCreate = vdTcpSocketCreate;
679
m->vdIfCallsTcpNet.pfnSocketDestroy = vdTcpSocketDestroy;
680
m->vdIfCallsTcpNet.pfnClientConnect = vdTcpClientConnect;
681
m->vdIfCallsTcpNet.pfnClientClose = vdTcpClientClose;
682
m->vdIfCallsTcpNet.pfnIsClientConnected = vdTcpIsClientConnected;
683
m->vdIfCallsTcpNet.pfnSelectOne = vdTcpSelectOne;
684
m->vdIfCallsTcpNet.pfnRead = vdTcpRead;
685
m->vdIfCallsTcpNet.pfnWrite = vdTcpWrite;
686
m->vdIfCallsTcpNet.pfnSgWrite = vdTcpSgWrite;
687
m->vdIfCallsTcpNet.pfnFlush = vdTcpFlush;
688
m->vdIfCallsTcpNet.pfnSetSendCoalescing = vdTcpSetSendCoalescing;
689
m->vdIfCallsTcpNet.pfnGetLocalAddress = vdTcpGetLocalAddress;
690
m->vdIfCallsTcpNet.pfnGetPeerAddress = vdTcpGetPeerAddress;
691
m->vdIfCallsTcpNet.pfnSelectOneEx = NULL;
692
m->vdIfCallsTcpNet.pfnPoke = NULL;
694
/* Initialize the per-disk interface chain */
696
vrc = VDInterfaceAdd(&m->vdIfError,
697
"Medium::vdInterfaceError",
698
VDINTERFACETYPE_ERROR,
699
&m->vdIfCallsError, this, &m->vdDiskIfaces);
700
AssertRCReturn(vrc, E_FAIL);
702
vrc = VDInterfaceAdd(&m->vdIfConfig,
703
"Medium::vdInterfaceConfig",
704
VDINTERFACETYPE_CONFIG,
705
&m->vdIfCallsConfig, this, &m->vdDiskIfaces);
706
AssertRCReturn(vrc, E_FAIL);
708
vrc = VDInterfaceAdd(&m->vdIfTcpNet,
709
"Medium::vdInterfaceTcpNet",
710
VDINTERFACETYPE_TCPNET,
711
&m->vdIfCallsTcpNet, this, &m->vdDiskIfaces);
712
AssertRCReturn(vrc, E_FAIL);
714
vrc = RTSemEventMultiCreate(&m->queryInfoSem);
715
AssertRCReturn(vrc, E_FAIL);
716
vrc = RTSemEventMultiSignal(m->queryInfoSem);
717
AssertRCReturn(vrc, E_FAIL);
722
void Medium::FinalRelease()
730
* Initializes the hard disk object without creating or opening an associated
733
* For hard disks that don't have the VD_CAP_CREATE_FIXED or
734
* VD_CAP_CREATE_DYNAMIC capability (and therefore cannot be created or deleted
735
* with the means of VirtualBox) the associated storage unit is assumed to be
736
* ready for use so the state of the hard disk object will be set to Created.
738
* @param aVirtualBox VirtualBox object.
739
* @param aLocation Storage unit location.
740
* @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
741
* by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
743
HRESULT Medium::init(VirtualBox *aVirtualBox,
746
bool *pfNeedsSaveSettings)
748
AssertReturn(aVirtualBox != NULL, E_FAIL);
749
AssertReturn(aFormat != NULL && *aFormat != '\0', E_FAIL);
751
/* Enclose the state transition NotReady->InInit->Ready */
752
AutoInitSpan autoInitSpan(this);
753
AssertReturn(autoInitSpan.isOk(), E_FAIL);
757
/* share VirtualBox weakly (parent remains NULL so far) */
758
unconst(m->pVirtualBox) = aVirtualBox;
761
m->state = MediumState_NotCreated;
763
/* cannot be a host drive */
764
m->hostDrive = false;
766
/* No storage unit is created yet, no need to queryInfo() */
768
rc = setFormat(aFormat);
769
if (FAILED(rc)) return rc;
771
if (m->formatObj->capabilities() & MediumFormatCapabilities_File)
773
rc = setLocation(aLocation);
774
if (FAILED(rc)) return rc;
778
rc = setLocation(aLocation);
779
if (FAILED(rc)) return rc;
782
if (!(m->formatObj->capabilities() & ( MediumFormatCapabilities_CreateFixed
783
| MediumFormatCapabilities_CreateDynamic))
786
/* storage for hard disks of this format can neither be explicitly
787
* created by VirtualBox nor deleted, so we place the hard disk to
788
* Created state here and also add it to the registry */
789
m->state = MediumState_Created;
790
unconst(m->id).create();
792
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
793
rc = m->pVirtualBox->registerHardDisk(this, pfNeedsSaveSettings);
796
/* Confirm a successful initialization when it's the case */
798
autoInitSpan.setSucceeded();
804
* Initializes the medium object by opening the storage unit at the specified
805
* location. The enOpenMode parameter defines whether the medium will be opened
806
* read/write or read-only.
808
* Note that the UUID, format and the parent of this medium will be
809
* determined when reading the medium storage unit, unless new values are
810
* specified by the parameters. If the detected or set parent is
811
* not known to VirtualBox, then this method will fail.
813
* @param aVirtualBox VirtualBox object.
814
* @param aLocation Storage unit location.
815
* @param enOpenMode Whether to open the medium read/write or read-only.
816
* @param aDeviceType Device type of medium.
817
* @param aSetImageId Whether to set the medium UUID or not.
818
* @param aImageId New medium UUID if @aSetId is true. Empty string means
819
* create a new UUID, and a zero UUID is invalid.
820
* @param aSetParentId Whether to set the parent UUID or not.
821
* @param aParentId New parent UUID if @aSetParentId is true. Empty string
822
* means create a new UUID, and a zero UUID is valid.
824
HRESULT Medium::init(VirtualBox *aVirtualBox,
826
HDDOpenMode enOpenMode,
827
DeviceType_T aDeviceType,
829
const Guid &aImageId,
831
const Guid &aParentId)
833
AssertReturn(aVirtualBox, E_INVALIDARG);
834
AssertReturn(aLocation, E_INVALIDARG);
836
/* Enclose the state transition NotReady->InInit->Ready */
837
AutoInitSpan autoInitSpan(this);
838
AssertReturn(autoInitSpan.isOk(), E_FAIL);
842
/* share VirtualBox weakly (parent remains NULL so far) */
843
unconst(m->pVirtualBox) = aVirtualBox;
845
/* there must be a storage unit */
846
m->state = MediumState_Created;
848
/* remember device type for correct unregistering later */
849
m->devType = aDeviceType;
851
/* cannot be a host drive */
852
m->hostDrive = false;
854
/* remember the open mode (defaults to ReadWrite) */
855
m->hddOpenMode = enOpenMode;
857
if (aDeviceType == DeviceType_HardDisk)
858
rc = setLocation(aLocation);
860
rc = setLocation(aLocation, "RAW");
861
if (FAILED(rc)) return rc;
863
/* save the new uuid values, will be used by queryInfo() */
864
m->setImageId = !!aSetImageId;
865
unconst(m->imageId) = aImageId;
866
m->setParentId = !!aSetParentId;
867
unconst(m->parentId) = aParentId;
869
/* get all the information about the medium from the storage unit */
874
/* if the storage unit is not accessible, it's not acceptable for the
875
* newly opened media so convert this into an error */
876
if (m->state == MediumState_Inaccessible)
878
Assert(!m->strLastAccessError.isEmpty());
879
rc = setError(E_FAIL, m->strLastAccessError.c_str());
883
AssertReturn(!m->id.isEmpty(), E_FAIL);
885
/* storage format must be detected by queryInfo() if the medium is accessible */
886
AssertReturn(!m->strFormat.isEmpty(), E_FAIL);
890
/* Confirm a successful initialization when it's the case */
892
autoInitSpan.setSucceeded();
898
* Initializes the medium object by loading its data from the given settings
899
* node. In this mode, the medium will always be opened read/write.
901
* @param aVirtualBox VirtualBox object.
902
* @param aParent Parent medium disk or NULL for a root (base) medium.
903
* @param aDeviceType Device type of the medium.
904
* @param aNode Configuration settings.
906
* @note Locks VirtualBox for writing, the medium tree for writing.
908
HRESULT Medium::init(VirtualBox *aVirtualBox,
910
DeviceType_T aDeviceType,
911
const settings::Medium &data)
913
using namespace settings;
915
AssertReturn(aVirtualBox, E_INVALIDARG);
917
/* Enclose the state transition NotReady->InInit->Ready */
918
AutoInitSpan autoInitSpan(this);
919
AssertReturn(autoInitSpan.isOk(), E_FAIL);
923
/* share VirtualBox and parent weakly */
924
unconst(m->pVirtualBox) = aVirtualBox;
926
/* register with VirtualBox/parent early, since uninit() will
927
* unconditionally unregister on failure */
930
// differencing medium: add to parent
931
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
932
m->pParent = aParent;
933
aParent->m->llChildren.push_back(this);
936
/* see below why we don't call queryInfo() (and therefore treat the medium
937
* as inaccessible for now */
938
m->state = MediumState_Inaccessible;
939
m->strLastAccessError = tr("Accessibility check was not yet performed");
942
unconst(m->id) = data.uuid;
944
/* assume not a host drive */
945
m->hostDrive = false;
948
m->strDescription = data.strDescription;
951
if (aDeviceType == DeviceType_HardDisk)
953
AssertReturn(!data.strFormat.isEmpty(), E_FAIL);
954
rc = setFormat(Bstr(data.strFormat));
955
if (FAILED(rc)) return rc;
959
/// @todo handle host drive settings here as well?
960
if (!data.strFormat.isEmpty())
961
rc = setFormat(Bstr(data.strFormat));
963
rc = setFormat(Bstr("RAW"));
964
if (FAILED(rc)) return rc;
967
/* optional, only for diffs, default is false; we can only auto-reset
968
* diff media so they must have a parent */
970
m->autoReset = data.fAutoReset;
972
m->autoReset = false;
974
/* properties (after setting the format as it populates the map). Note that
975
* if some properties are not supported but preseint in the settings file,
976
* they will still be read and accessible (for possible backward
977
* compatibility; we can also clean them up from the XML upon next
978
* XML format version change if we wish) */
979
for (settings::PropertiesMap::const_iterator it = data.properties.begin();
980
it != data.properties.end(); ++it)
982
const Utf8Str &name = it->first;
983
const Utf8Str &value = it->second;
984
m->properties[Bstr(name)] = Bstr(value);
988
rc = setLocation(data.strLocation);
989
if (FAILED(rc)) return rc;
991
if (aDeviceType == DeviceType_HardDisk)
993
/* type is only for base hard disks */
994
if (m->pParent.isNull())
995
m->type = data.hdType;
998
m->type = MediumType_Writethrough;
1000
/* remember device type for correct unregistering later */
1001
m->devType = aDeviceType;
1003
LogFlowThisFunc(("m->strLocationFull='%s', m->strFormat=%s, m->id={%RTuuid}\n",
1004
m->strLocationFull.raw(), m->strFormat.raw(), m->id.raw()));
1006
/* Don't call queryInfo() for registered media to prevent the calling
1007
* thread (i.e. the VirtualBox server startup thread) from an unexpected
1008
* freeze but mark it as initially inaccessible instead. The vital UUID,
1009
* location and format properties are read from the registry file above; to
1010
* get the actual state and the rest of the data, the user will have to call
1011
* COMGETTER(State). */
1013
AutoWriteLock treeLock(aVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1015
/* load all children */
1016
for (settings::MediaList::const_iterator it = data.llChildren.begin();
1017
it != data.llChildren.end();
1020
const settings::Medium &med = *it;
1022
ComObjPtr<Medium> pHD;
1024
rc = pHD->init(aVirtualBox,
1028
if (FAILED(rc)) break;
1030
rc = m->pVirtualBox->registerHardDisk(pHD, NULL /*pfNeedsSaveSettings*/);
1031
if (FAILED(rc)) break;
1034
/* Confirm a successful initialization when it's the case */
1036
autoInitSpan.setSucceeded();
1042
* Initializes the medium object by providing the host drive information.
1043
* Not used for anything but the host floppy/host DVD case.
1045
* @todo optimize all callers to avoid reconstructing objects with the same
1046
* information over and over again - in the typical case each VM referring to
1047
* a particular host drive has its own instance.
1049
* @param aVirtualBox VirtualBox object.
1050
* @param aDeviceType Device type of the medium.
1051
* @param aLocation Location of the host drive.
1052
* @param aDescription Comment for this host drive.
1054
* @note Locks VirtualBox lock for writing.
1056
HRESULT Medium::init(VirtualBox *aVirtualBox,
1057
DeviceType_T aDeviceType,
1061
ComAssertRet(aDeviceType == DeviceType_DVD || aDeviceType == DeviceType_Floppy, E_INVALIDARG);
1062
ComAssertRet(aLocation, E_INVALIDARG);
1064
/* Enclose the state transition NotReady->InInit->Ready */
1065
AutoInitSpan autoInitSpan(this);
1066
AssertReturn(autoInitSpan.isOk(), E_FAIL);
1068
/* share VirtualBox weakly (parent remains NULL so far) */
1069
unconst(m->pVirtualBox) = aVirtualBox;
1071
/* fake up a UUID which is unique, but also reproducible */
1074
if (aDeviceType == DeviceType_DVD)
1075
memcpy(&uuid.au8[0], "DVD", 3);
1077
memcpy(&uuid.au8[0], "FD", 2);
1078
/* use device name, adjusted to the end of uuid, shortened if necessary */
1079
Utf8Str loc(aLocation);
1080
size_t cbLocation = strlen(loc.raw());
1081
if (cbLocation > 12)
1082
memcpy(&uuid.au8[4], loc.raw() + (cbLocation - 12), 12);
1084
memcpy(&uuid.au8[4 + 12 - cbLocation], loc.raw(), cbLocation);
1085
unconst(m->id) = uuid;
1087
m->type = MediumType_Writethrough;
1088
m->devType = aDeviceType;
1089
m->state = MediumState_Created;
1090
m->hostDrive = true;
1091
HRESULT rc = setFormat(Bstr("RAW"));
1092
if (FAILED(rc)) return rc;
1093
rc = setLocation(aLocation);
1094
if (FAILED(rc)) return rc;
1095
m->strDescription = aDescription;
1097
/// @todo generate uuid (similarly to host network interface uuid) from location and device type
1099
autoInitSpan.setSucceeded();
1104
* Uninitializes the instance.
1106
* Called either from FinalRelease() or by the parent when it gets destroyed.
1108
* @note All children of this medium get uninitialized by calling their
1111
* @note Caller must hold the tree lock of the medium tree this medium is on.
1113
void Medium::uninit()
1115
/* Enclose the state transition Ready->InUninit->NotReady */
1116
AutoUninitSpan autoUninitSpan(this);
1117
if (autoUninitSpan.uninitDone())
1120
if (!m->formatObj.isNull())
1122
/* remove the caller reference we added in setFormat() */
1123
m->formatObj->releaseCaller();
1124
m->formatObj.setNull();
1127
if (m->state == MediumState_Deleting)
1129
/* we are being uninitialized after've been deleted by merge.
1130
* Reparenting has already been done so don't touch it here (we are
1131
* now orphans and removeDependentChild() will assert) */
1132
Assert(m->pParent.isNull());
1136
MediaList::iterator it;
1137
for (it = m->llChildren.begin();
1138
it != m->llChildren.end();
1141
Medium *pChild = *it;
1142
pChild->m->pParent.setNull();
1145
m->llChildren.clear(); // this unsets all the ComPtrs and probably calls delete
1149
// this is a differencing disk: then remove it from the parent's children list
1154
RTSemEventMultiSignal(m->queryInfoSem);
1155
RTSemEventMultiDestroy(m->queryInfoSem);
1156
m->queryInfoSem = NIL_RTSEMEVENTMULTI;
1158
unconst(m->pVirtualBox) = NULL;
1162
* Internal helper that removes "this" from the list of children of its
1163
* parent. Used in uninit() and other places when reparenting is necessary.
1165
* The caller must hold the medium tree lock!
1167
void Medium::deparent()
1169
MediaList &llParent = m->pParent->m->llChildren;
1170
for (MediaList::iterator it = llParent.begin();
1171
it != llParent.end();
1174
Medium *pParentsChild = *it;
1175
if (this == pParentsChild)
1181
m->pParent.setNull();
1185
* Internal helper that removes "this" from the list of children of its
1186
* parent. Used in uninit() and other places when reparenting is necessary.
1188
* The caller must hold the medium tree lock!
1190
void Medium::setParent(const ComObjPtr<Medium> &pParent)
1192
m->pParent = pParent;
1194
pParent->m->llChildren.push_back(this);
1198
////////////////////////////////////////////////////////////////////////////////
1200
// IMedium public methods
1202
////////////////////////////////////////////////////////////////////////////////
1204
STDMETHODIMP Medium::COMGETTER(Id)(BSTR *aId)
1206
CheckComArgOutPointerValid(aId);
1208
AutoCaller autoCaller(this);
1209
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1211
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1213
m->id.toUtf16().cloneTo(aId);
1218
STDMETHODIMP Medium::COMGETTER(Description)(BSTR *aDescription)
1220
CheckComArgOutPointerValid(aDescription);
1222
AutoCaller autoCaller(this);
1223
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1225
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1227
m->strDescription.cloneTo(aDescription);
1232
STDMETHODIMP Medium::COMSETTER(Description)(IN_BSTR aDescription)
1234
AutoCaller autoCaller(this);
1235
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1237
// AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1239
/// @todo update m->description and save the global registry (and local
1240
/// registries of portable VMs referring to this medium), this will also
1241
/// require to add the mRegistered flag to data
1243
NOREF(aDescription);
1245
ReturnComNotImplemented();
1248
STDMETHODIMP Medium::COMGETTER(State)(MediumState_T *aState)
1250
CheckComArgOutPointerValid(aState);
1252
AutoCaller autoCaller(this);
1253
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1255
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
STDMETHODIMP Medium::COMGETTER(Location)(BSTR *aLocation)
1263
CheckComArgOutPointerValid(aLocation);
1265
AutoCaller autoCaller(this);
1266
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1268
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1270
m->strLocationFull.cloneTo(aLocation);
1275
STDMETHODIMP Medium::COMSETTER(Location)(IN_BSTR aLocation)
1277
CheckComArgStrNotEmptyOrNull(aLocation);
1279
AutoCaller autoCaller(this);
1280
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1282
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
/// @todo NEWMEDIA for file names, add the default extension if no extension
1285
/// is present (using the information from the VD backend which also implies
1286
/// that one more parameter should be passed to setLocation() requesting
1287
/// that functionality since it is only allwed when called from this method
1289
/// @todo NEWMEDIA rename the file and set m->location on success, then save
1290
/// the global registry (and local registries of portable VMs referring to
1291
/// this medium), this will also require to add the mRegistered flag to data
1293
ReturnComNotImplemented();
1296
STDMETHODIMP Medium::COMGETTER(Name)(BSTR *aName)
1298
CheckComArgOutPointerValid(aName);
1300
AutoCaller autoCaller(this);
1301
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1303
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1305
getName().cloneTo(aName);
1310
STDMETHODIMP Medium::COMGETTER(DeviceType)(DeviceType_T *aDeviceType)
1312
CheckComArgOutPointerValid(aDeviceType);
1314
AutoCaller autoCaller(this);
1315
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1317
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1319
*aDeviceType = m->devType;
1324
STDMETHODIMP Medium::COMGETTER(HostDrive)(BOOL *aHostDrive)
1326
CheckComArgOutPointerValid(aHostDrive);
1328
AutoCaller autoCaller(this);
1329
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1331
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1333
*aHostDrive = m->hostDrive;
1338
STDMETHODIMP Medium::COMGETTER(Size)(ULONG64 *aSize)
1340
CheckComArgOutPointerValid(aSize);
1342
AutoCaller autoCaller(this);
1343
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1345
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
STDMETHODIMP Medium::COMGETTER(Format)(BSTR *aFormat)
1354
CheckComArgOutPointerValid(aFormat);
1356
AutoCaller autoCaller(this);
1357
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1359
/* no need to lock, m->strFormat is const */
1360
m->strFormat.cloneTo(aFormat);
1365
STDMETHODIMP Medium::COMGETTER(MediumFormat)(IMediumFormat **aMediumFormat)
1367
CheckComArgOutPointerValid(aMediumFormat);
1369
AutoCaller autoCaller(this);
1370
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1372
/* no need to lock, m->formatObj is const */
1373
m->formatObj.queryInterfaceTo(aMediumFormat);
1378
STDMETHODIMP Medium::COMGETTER(Type)(MediumType_T *aType)
1380
CheckComArgOutPointerValid(aType);
1382
AutoCaller autoCaller(this);
1383
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1385
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
STDMETHODIMP Medium::COMSETTER(Type)(MediumType_T aType)
1394
AutoCaller autoCaller(this);
1395
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1397
// we access mParent and members
1398
AutoMultiWriteLock2 mlock(&m->pVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
1402
case MediumState_Created:
1403
case MediumState_Inaccessible:
1406
return setStateError();
1409
if (m->type == aType)
1415
/* cannot change the type of a differencing medium */
1417
return setError(E_FAIL,
1418
tr("Cannot change the type of medium '%s' because it is a differencing medium"),
1419
m->strLocationFull.raw());
1421
/* cannot change the type of a medium being in use by more than one VM */
1422
if (m->backRefs.size() > 1)
1423
return setError(E_FAIL,
1424
tr("Cannot change the type of medium '%s' because it is attached to %d virtual machines"),
1425
m->strLocationFull.raw(), m->backRefs.size());
1429
case MediumType_Normal:
1430
case MediumType_Immutable:
1432
/* normal can be easily converted to immutable and vice versa even
1433
* if they have children as long as they are not attached to any
1434
* machine themselves */
1437
case MediumType_Writethrough:
1438
case MediumType_Shareable:
1440
/* cannot change to writethrough or shareable if there are children */
1441
if (getChildren().size() != 0)
1442
return setError(E_FAIL,
1443
tr("Cannot change type for medium '%s' since it has %d child media"),
1444
m->strLocationFull.raw(), getChildren().size());
1445
if (aType == MediumType_Shareable)
1447
if (m->state == MediumState_Inaccessible)
1449
HRESULT rc = queryInfo();
1452
tr("Cannot change type for medium '%s' to 'Shareable' because the medium is inaccessible"),
1453
m->strLocationFull.c_str());
1456
MediumVariant_T variant = getVariant();
1457
if (!(variant & MediumVariant_Fixed))
1458
return setError(E_FAIL,
1459
tr("Cannot change type for medium '%s' to 'Shareable' since it is a dynamic medium storage unit"),
1460
m->strLocationFull.raw());
1466
AssertFailedReturn(E_FAIL);
1471
// save the global settings; for that we should hold only the VirtualBox lock
1473
AutoWriteLock alock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
1474
HRESULT rc = m->pVirtualBox->saveSettings();
1479
STDMETHODIMP Medium::COMGETTER(Parent)(IMedium **aParent)
1481
CheckComArgOutPointerValid(aParent);
1483
AutoCaller autoCaller(this);
1484
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1486
/* we access mParent */
1487
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1489
m->pParent.queryInterfaceTo(aParent);
1494
STDMETHODIMP Medium::COMGETTER(Children)(ComSafeArrayOut(IMedium *, aChildren))
1496
CheckComArgOutSafeArrayPointerValid(aChildren);
1498
AutoCaller autoCaller(this);
1499
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1501
/* we access children */
1502
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1504
SafeIfaceArray<IMedium> children(this->getChildren());
1505
children.detachTo(ComSafeArrayOutArg(aChildren));
1510
STDMETHODIMP Medium::COMGETTER(Base)(IMedium **aBase)
1512
CheckComArgOutPointerValid(aBase);
1514
/* base() will do callers/locking */
1516
getBase().queryInterfaceTo(aBase);
1521
STDMETHODIMP Medium::COMGETTER(ReadOnly)(BOOL *aReadOnly)
1523
CheckComArgOutPointerValid(aReadOnly);
1525
AutoCaller autoCaller(this);
1526
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1528
/* isRadOnly() will do locking */
1530
*aReadOnly = isReadOnly();
1535
STDMETHODIMP Medium::COMGETTER(LogicalSize)(ULONG64 *aLogicalSize)
1537
CheckComArgOutPointerValid(aLogicalSize);
1540
AutoCaller autoCaller(this);
1541
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1543
/* we access mParent */
1544
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1546
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1548
if (m->pParent.isNull())
1550
*aLogicalSize = m->logicalSize;
1556
/* We assume that some backend may decide to return a meaningless value in
1557
* response to VDGetSize() for differencing media and therefore always
1558
* ask the base medium ourselves. */
1560
/* base() will do callers/locking */
1562
return getBase()->COMGETTER(LogicalSize)(aLogicalSize);
1565
STDMETHODIMP Medium::COMGETTER(AutoReset)(BOOL *aAutoReset)
1567
CheckComArgOutPointerValid(aAutoReset);
1569
AutoCaller autoCaller(this);
1570
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1572
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1574
if (m->pParent.isNull())
1575
*aAutoReset = FALSE;
1577
*aAutoReset = m->autoReset;
1582
STDMETHODIMP Medium::COMSETTER(AutoReset)(BOOL aAutoReset)
1584
AutoCaller autoCaller(this);
1585
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1587
AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1589
if (m->pParent.isNull())
1590
return setError(VBOX_E_NOT_SUPPORTED,
1591
tr("Medium '%s' is not differencing"),
1592
m->strLocationFull.raw());
1594
if (m->autoReset != !!aAutoReset)
1596
m->autoReset = !!aAutoReset;
1598
// save the global settings; for that we should hold only the VirtualBox lock
1600
AutoWriteLock alock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
1601
return m->pVirtualBox->saveSettings();
1606
STDMETHODIMP Medium::COMGETTER(LastAccessError)(BSTR *aLastAccessError)
1608
CheckComArgOutPointerValid(aLastAccessError);
1610
AutoCaller autoCaller(this);
1611
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1613
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1615
m->strLastAccessError.cloneTo(aLastAccessError);
1620
STDMETHODIMP Medium::COMGETTER(MachineIds)(ComSafeArrayOut(BSTR,aMachineIds))
1622
CheckComArgOutSafeArrayPointerValid(aMachineIds);
1624
AutoCaller autoCaller(this);
1625
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1627
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
com::SafeArray<BSTR> machineIds;
1631
if (m->backRefs.size() != 0)
1633
machineIds.reset(m->backRefs.size());
1636
for (BackRefList::const_iterator it = m->backRefs.begin();
1637
it != m->backRefs.end(); ++it, ++i)
1639
it->machineId.toUtf16().detachTo(&machineIds[i]);
1643
machineIds.detachTo(ComSafeArrayOutArg(aMachineIds));
1648
STDMETHODIMP Medium::RefreshState(MediumState_T *aState)
1650
CheckComArgOutPointerValid(aState);
1652
AutoCaller autoCaller(this);
1653
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1655
/* queryInfo() locks this for writing. */
1656
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1662
case MediumState_Created:
1663
case MediumState_Inaccessible:
1664
case MediumState_LockedRead:
1678
STDMETHODIMP Medium::GetSnapshotIds(IN_BSTR aMachineId,
1679
ComSafeArrayOut(BSTR, aSnapshotIds))
1681
CheckComArgExpr(aMachineId, Guid(aMachineId).isEmpty() == false);
1682
CheckComArgOutSafeArrayPointerValid(aSnapshotIds);
1684
AutoCaller autoCaller(this);
1685
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1687
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1689
com::SafeArray<BSTR> snapshotIds;
1691
Guid id(aMachineId);
1692
for (BackRefList::const_iterator it = m->backRefs.begin();
1693
it != m->backRefs.end(); ++it)
1695
if (it->machineId == id)
1697
size_t size = it->llSnapshotIds.size();
1699
/* if the medium is attached to the machine in the current state, we
1700
* return its ID as the first element of the array */
1701
if (it->fInCurState)
1706
snapshotIds.reset(size);
1709
if (it->fInCurState)
1710
it->machineId.toUtf16().detachTo(&snapshotIds[j++]);
1712
for (BackRef::GuidList::const_iterator jt = it->llSnapshotIds.begin();
1713
jt != it->llSnapshotIds.end();
1716
(*jt).toUtf16().detachTo(&snapshotIds[j]);
1724
snapshotIds.detachTo(ComSafeArrayOutArg(aSnapshotIds));
1730
* @note @a aState may be NULL if the state value is not needed (only for
1731
* in-process calls).
1733
STDMETHODIMP Medium::LockRead(MediumState_T *aState)
1735
AutoCaller autoCaller(this);
1736
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1738
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1740
/* Wait for a concurrently running queryInfo() to complete */
1741
while (m->queryInfoRunning)
1744
RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
1748
/* return the current state before */
1756
case MediumState_Created:
1757
case MediumState_Inaccessible:
1758
case MediumState_LockedRead:
1762
ComAssertMsgBreak(m->readers != 0, ("Counter overflow"), rc = E_FAIL);
1764
/* Remember pre-lock state */
1765
if (m->state != MediumState_LockedRead)
1766
m->preLockState = m->state;
1768
LogFlowThisFunc(("Okay - prev state=%d readers=%d\n", m->state, m->readers));
1769
m->state = MediumState_LockedRead;
1775
LogFlowThisFunc(("Failing - state=%d\n", m->state));
1776
rc = setStateError();
1785
* @note @a aState may be NULL if the state value is not needed (only for
1786
* in-process calls).
1788
STDMETHODIMP Medium::UnlockRead(MediumState_T *aState)
1790
AutoCaller autoCaller(this);
1791
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1793
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1799
case MediumState_LockedRead:
1801
Assert(m->readers != 0);
1804
/* Reset the state after the last reader */
1805
if (m->readers == 0)
1807
m->state = m->preLockState;
1808
/* There are cases where we inject the deleting state into
1809
* a medium locked for reading. Make sure #unmarkForDeletion()
1810
* gets the right state afterwards. */
1811
if (m->preLockState == MediumState_Deleting)
1812
m->preLockState = MediumState_Created;
1815
LogFlowThisFunc(("new state=%d\n", m->state));
1820
LogFlowThisFunc(("Failing - state=%d\n", m->state));
1821
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
1822
tr("Medium '%s' is not locked for reading"),
1823
m->strLocationFull.raw());
1828
/* return the current state after */
1836
* @note @a aState may be NULL if the state value is not needed (only for
1837
* in-process calls).
1839
STDMETHODIMP Medium::LockWrite(MediumState_T *aState)
1841
AutoCaller autoCaller(this);
1842
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1844
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1846
/* Wait for a concurrently running queryInfo() to complete */
1847
while (m->queryInfoRunning)
1850
RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
1854
/* return the current state before */
1862
case MediumState_Created:
1863
case MediumState_Inaccessible:
1865
m->preLockState = m->state;
1867
LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
1868
m->state = MediumState_LockedWrite;
1873
LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
1874
rc = setStateError();
1883
* @note @a aState may be NULL if the state value is not needed (only for
1884
* in-process calls).
1886
STDMETHODIMP Medium::UnlockWrite(MediumState_T *aState)
1888
AutoCaller autoCaller(this);
1889
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1891
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1897
case MediumState_LockedWrite:
1899
m->state = m->preLockState;
1900
/* There are cases where we inject the deleting state into
1901
* a medium locked for writing. Make sure #unmarkForDeletion()
1902
* gets the right state afterwards. */
1903
if (m->preLockState == MediumState_Deleting)
1904
m->preLockState = MediumState_Created;
1905
LogFlowThisFunc(("new state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
1910
LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
1911
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
1912
tr("Medium '%s' is not locked for writing"),
1913
m->strLocationFull.raw());
1918
/* return the current state after */
1925
STDMETHODIMP Medium::Close()
1927
AutoCaller autoCaller(this);
1928
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1930
// we're accessing parent/child and backrefs, so lock the tree first, then ourselves
1931
AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
1933
COMMA_LOCKVAL_SRC_POS);
1935
bool wasCreated = true;
1936
bool fNeedsSaveSettings = false;
1940
case MediumState_NotCreated:
1943
case MediumState_Created:
1944
case MediumState_Inaccessible:
1947
return setStateError();
1950
if (m->backRefs.size() != 0)
1951
return setError(VBOX_E_OBJECT_IN_USE,
1952
tr("Medium '%s' is attached to %d virtual machines"),
1953
m->strLocationFull.raw(), m->backRefs.size());
1955
/* perform extra media-dependent close checks */
1956
HRESULT rc = canClose();
1957
if (FAILED(rc)) return rc;
1961
/* remove from the list of known media before performing actual
1962
* uninitialization (to keep the media registry consistent on
1963
* failure to do so) */
1964
rc = unregisterWithVirtualBox(&fNeedsSaveSettings);
1965
if (FAILED(rc)) return rc;
1968
// make a copy of VirtualBox pointer which gets nulled by uninit()
1969
ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1971
// leave the AutoCaller, as otherwise uninit() will simply hang
1972
autoCaller.release();
1974
/* Keep the locks held until after uninit, as otherwise the consistency
1975
* of the medium tree cannot be guaranteed. */
1978
multilock.release();
1980
if (fNeedsSaveSettings)
1982
AutoWriteLock vboxlock(pVirtualBox COMMA_LOCKVAL_SRC_POS);
1983
pVirtualBox->saveSettings();
1989
STDMETHODIMP Medium::GetProperty(IN_BSTR aName, BSTR *aValue)
1991
CheckComArgStrNotEmptyOrNull(aName);
1992
CheckComArgOutPointerValid(aValue);
1994
AutoCaller autoCaller(this);
1995
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1997
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1999
Data::PropertyMap::const_iterator it = m->properties.find(Bstr(aName));
2000
if (it == m->properties.end())
2001
return setError(VBOX_E_OBJECT_NOT_FOUND,
2002
tr("Property '%ls' does not exist"), aName);
2004
it->second.cloneTo(aValue);
2009
STDMETHODIMP Medium::SetProperty(IN_BSTR aName, IN_BSTR aValue)
2011
CheckComArgStrNotEmptyOrNull(aName);
2013
AutoCaller autoCaller(this);
2014
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2016
AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2020
case MediumState_Created:
2021
case MediumState_Inaccessible:
2024
return setStateError();
2027
Data::PropertyMap::iterator it = m->properties.find(Bstr(aName));
2028
if (it == m->properties.end())
2029
return setError(VBOX_E_OBJECT_NOT_FOUND,
2030
tr("Property '%ls' does not exist"),
2033
if (aValue && !*aValue)
2034
it->second = (const char *)NULL;
2036
it->second = aValue;
2038
// save the global settings; for that we should hold only the VirtualBox lock
2040
AutoWriteLock alock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
2041
HRESULT rc = m->pVirtualBox->saveSettings();
2046
STDMETHODIMP Medium::GetProperties(IN_BSTR aNames,
2047
ComSafeArrayOut(BSTR, aReturnNames),
2048
ComSafeArrayOut(BSTR, aReturnValues))
2050
CheckComArgOutSafeArrayPointerValid(aReturnNames);
2051
CheckComArgOutSafeArrayPointerValid(aReturnValues);
2053
AutoCaller autoCaller(this);
2054
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2056
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
/// @todo make use of aNames according to the documentation
2061
com::SafeArray<BSTR> names(m->properties.size());
2062
com::SafeArray<BSTR> values(m->properties.size());
2065
for (Data::PropertyMap::const_iterator it = m->properties.begin();
2066
it != m->properties.end();
2069
it->first.cloneTo(&names[i]);
2070
it->second.cloneTo(&values[i]);
2074
names.detachTo(ComSafeArrayOutArg(aReturnNames));
2075
values.detachTo(ComSafeArrayOutArg(aReturnValues));
2080
STDMETHODIMP Medium::SetProperties(ComSafeArrayIn(IN_BSTR, aNames),
2081
ComSafeArrayIn(IN_BSTR, aValues))
2083
CheckComArgSafeArrayNotNull(aNames);
2084
CheckComArgSafeArrayNotNull(aValues);
2086
AutoCaller autoCaller(this);
2087
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2089
AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2091
com::SafeArray<IN_BSTR> names(ComSafeArrayInArg(aNames));
2092
com::SafeArray<IN_BSTR> values(ComSafeArrayInArg(aValues));
2094
/* first pass: validate names */
2099
if (m->properties.find(Bstr(names[i])) == m->properties.end())
2100
return setError(VBOX_E_OBJECT_NOT_FOUND,
2101
tr("Property '%ls' does not exist"), names[i]);
2104
/* second pass: assign */
2109
Data::PropertyMap::iterator it = m->properties.find(Bstr(names[i]));
2110
AssertReturn(it != m->properties.end(), E_FAIL);
2112
if (values[i] && !*values[i])
2113
it->second = (const char *)NULL;
2115
it->second = values[i];
2120
// saveSettings needs vbox lock
2121
AutoWriteLock alock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
2122
HRESULT rc = m->pVirtualBox->saveSettings();
2127
STDMETHODIMP Medium::CreateBaseStorage(ULONG64 aLogicalSize,
2128
MediumVariant_T aVariant,
2129
IProgress **aProgress)
2131
CheckComArgOutPointerValid(aProgress);
2133
AutoCaller autoCaller(this);
2134
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2137
ComObjPtr <Progress> pProgress;
2138
Medium::Task *pTask = NULL;
2142
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2144
aVariant = (MediumVariant_T)((unsigned)aVariant & (unsigned)~MediumVariant_Diff);
2145
if ( !(aVariant & MediumVariant_Fixed)
2146
&& !(m->formatObj->capabilities() & MediumFormatCapabilities_CreateDynamic))
2147
throw setError(VBOX_E_NOT_SUPPORTED,
2148
tr("Medium format '%s' does not support dynamic storage creation"),
2149
m->strFormat.raw());
2150
if ( (aVariant & MediumVariant_Fixed)
2151
&& !(m->formatObj->capabilities() & MediumFormatCapabilities_CreateDynamic))
2152
throw setError(VBOX_E_NOT_SUPPORTED,
2153
tr("Medium format '%s' does not support fixed storage creation"),
2154
m->strFormat.raw());
2156
if (m->state != MediumState_NotCreated)
2157
throw setStateError();
2159
pProgress.createObject();
2160
rc = pProgress->init(m->pVirtualBox,
2161
static_cast<IMedium*>(this),
2162
(aVariant & MediumVariant_Fixed)
2163
? BstrFmt(tr("Creating fixed medium storage unit '%s'"), m->strLocationFull.raw())
2164
: BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.raw()),
2165
TRUE /* aCancelable */);
2169
/* setup task object to carry out the operation asynchronously */
2170
pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize,
2177
m->state = MediumState_Creating;
2179
catch (HRESULT aRC) { rc = aRC; }
2183
rc = startThread(pTask);
2186
pProgress.queryInterfaceTo(aProgress);
2188
else if (pTask != NULL)
2194
STDMETHODIMP Medium::DeleteStorage(IProgress **aProgress)
2196
CheckComArgOutPointerValid(aProgress);
2198
AutoCaller autoCaller(this);
2199
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2201
bool fNeedsSaveSettings = false;
2202
ComObjPtr<Progress> pProgress;
2204
HRESULT rc = deleteStorage(&pProgress,
2206
&fNeedsSaveSettings);
2207
if (fNeedsSaveSettings)
2209
AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
2210
m->pVirtualBox->saveSettings();
2214
pProgress.queryInterfaceTo(aProgress);
2219
STDMETHODIMP Medium::CreateDiffStorage(IMedium *aTarget,
2220
MediumVariant_T aVariant,
2221
IProgress **aProgress)
2223
CheckComArgNotNull(aTarget);
2224
CheckComArgOutPointerValid(aProgress);
2226
AutoCaller autoCaller(this);
2227
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2229
ComObjPtr<Medium> diff = static_cast<Medium*>(aTarget);
2231
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2233
if (m->type == MediumType_Writethrough)
2234
return setError(E_FAIL,
2235
tr("Medium type of '%s' is Writethrough"),
2236
m->strLocationFull.raw());
2237
else if (m->type == MediumType_Shareable)
2238
return setError(E_FAIL,
2239
tr("Medium type of '%s' is Shareable"),
2240
m->strLocationFull.raw());
2242
/* Apply the normal locking logic to the entire chain. */
2243
MediumLockList *pMediumLockList(new MediumLockList());
2244
HRESULT rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
2245
true /* fMediumLockWrite */,
2250
delete pMediumLockList;
2254
ComObjPtr <Progress> pProgress;
2256
rc = createDiffStorage(diff, aVariant, pMediumLockList, &pProgress,
2257
false /* aWait */, NULL /* pfNeedsSaveSettings*/);
2259
delete pMediumLockList;
2261
pProgress.queryInterfaceTo(aProgress);
2266
STDMETHODIMP Medium::MergeTo(IMedium *aTarget, IProgress **aProgress)
2268
CheckComArgNotNull(aTarget);
2269
CheckComArgOutPointerValid(aProgress);
2270
ComAssertRet(aTarget != this, E_INVALIDARG);
2272
AutoCaller autoCaller(this);
2273
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2275
ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget);
2277
bool fMergeForward = false;
2278
ComObjPtr<Medium> pParentForTarget;
2279
MediaList childrenToReparent;
2280
MediumLockList *pMediumLockList = NULL;
2284
rc = prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward,
2285
pParentForTarget, childrenToReparent, pMediumLockList);
2286
if (FAILED(rc)) return rc;
2288
ComObjPtr <Progress> pProgress;
2290
rc = mergeTo(pTarget, fMergeForward, pParentForTarget, childrenToReparent,
2291
pMediumLockList, &pProgress, false /* aWait */,
2292
NULL /* pfNeedsSaveSettings */);
2294
cancelMergeTo(childrenToReparent, pMediumLockList);
2296
pProgress.queryInterfaceTo(aProgress);
2301
STDMETHODIMP Medium::CloneTo(IMedium *aTarget,
2302
MediumVariant_T aVariant,
2304
IProgress **aProgress)
2306
CheckComArgNotNull(aTarget);
2307
CheckComArgOutPointerValid(aProgress);
2308
ComAssertRet(aTarget != this, E_INVALIDARG);
2310
AutoCaller autoCaller(this);
2311
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2313
ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget);
2314
ComObjPtr<Medium> pParent;
2316
pParent = static_cast<Medium*>(aParent);
2319
ComObjPtr<Progress> pProgress;
2320
Medium::Task *pTask = NULL;
2324
// locking: we need the tree lock first because we access parent pointers
2325
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2326
// and we need to write-lock the media involved
2327
AutoMultiWriteLock3 alock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
2329
if ( pTarget->m->state != MediumState_NotCreated
2330
&& pTarget->m->state != MediumState_Created)
2331
throw pTarget->setStateError();
2333
/* Build the source lock list. */
2334
MediumLockList *pSourceMediumLockList(new MediumLockList());
2335
rc = createMediumLockList(true /* fFailIfInaccessible */,
2336
false /* fMediumLockWrite */,
2338
*pSourceMediumLockList);
2341
delete pSourceMediumLockList;
2345
/* Build the target lock list (including the to-be parent chain). */
2346
MediumLockList *pTargetMediumLockList(new MediumLockList());
2347
rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
2348
true /* fMediumLockWrite */,
2350
*pTargetMediumLockList);
2353
delete pSourceMediumLockList;
2354
delete pTargetMediumLockList;
2358
rc = pSourceMediumLockList->Lock();
2361
delete pSourceMediumLockList;
2362
delete pTargetMediumLockList;
2364
tr("Failed to lock source media '%s'"),
2365
getLocationFull().raw());
2367
rc = pTargetMediumLockList->Lock();
2370
delete pSourceMediumLockList;
2371
delete pTargetMediumLockList;
2373
tr("Failed to lock target media '%s'"),
2374
pTarget->getLocationFull().raw());
2377
pProgress.createObject();
2378
rc = pProgress->init(m->pVirtualBox,
2379
static_cast <IMedium *>(this),
2380
BstrFmt(tr("Creating clone medium '%s'"), pTarget->m->strLocationFull.raw()),
2381
TRUE /* aCancelable */);
2384
delete pSourceMediumLockList;
2385
delete pTargetMediumLockList;
2389
/* setup task object to carry out the operation asynchronously */
2390
pTask = new Medium::CloneTask(this, pProgress, pTarget, aVariant,
2391
pParent, pSourceMediumLockList,
2392
pTargetMediumLockList);
2398
if (pTarget->m->state == MediumState_NotCreated)
2399
pTarget->m->state = MediumState_Creating;
2401
catch (HRESULT aRC) { rc = aRC; }
2405
rc = startThread(pTask);
2408
pProgress.queryInterfaceTo(aProgress);
2410
else if (pTask != NULL)
2416
STDMETHODIMP Medium::Compact(IProgress **aProgress)
2418
CheckComArgOutPointerValid(aProgress);
2420
AutoCaller autoCaller(this);
2421
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2424
ComObjPtr <Progress> pProgress;
2425
Medium::Task *pTask = NULL;
2429
/* We need to lock both the current object, and the tree lock (would
2430
* cause a lock order violation otherwise) for createMediumLockList. */
2431
AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
2433
COMMA_LOCKVAL_SRC_POS);
2435
/* Build the medium lock list. */
2436
MediumLockList *pMediumLockList(new MediumLockList());
2437
rc = createMediumLockList(true /* fFailIfInaccessible */ ,
2438
true /* fMediumLockWrite */,
2443
delete pMediumLockList;
2447
rc = pMediumLockList->Lock();
2450
delete pMediumLockList;
2452
tr("Failed to lock media when compacting '%s'"),
2453
getLocationFull().raw());
2456
pProgress.createObject();
2457
rc = pProgress->init(m->pVirtualBox,
2458
static_cast <IMedium *>(this),
2459
BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.raw()),
2460
TRUE /* aCancelable */);
2463
delete pMediumLockList;
2467
/* setup task object to carry out the operation asynchronously */
2468
pTask = new Medium::CompactTask(this, pProgress, pMediumLockList);
2474
catch (HRESULT aRC) { rc = aRC; }
2478
rc = startThread(pTask);
2481
pProgress.queryInterfaceTo(aProgress);
2483
else if (pTask != NULL)
2489
STDMETHODIMP Medium::Resize(ULONG64 aLogicalSize, IProgress **aProgress)
2491
CheckComArgOutPointerValid(aProgress);
2493
AutoCaller autoCaller(this);
2494
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2496
NOREF(aLogicalSize);
2498
ReturnComNotImplemented();
2501
STDMETHODIMP Medium::Reset(IProgress **aProgress)
2503
CheckComArgOutPointerValid(aProgress);
2505
AutoCaller autoCaller(this);
2506
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2509
ComObjPtr <Progress> pProgress;
2510
Medium::Task *pTask = NULL;
2514
/* canClose() needs the tree lock */
2515
AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
2517
COMMA_LOCKVAL_SRC_POS);
2519
LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
2521
if (m->pParent.isNull())
2522
throw setError(VBOX_E_NOT_SUPPORTED,
2523
tr("Medium type of '%s' is not differencing"),
2524
m->strLocationFull.raw());
2530
/* Build the medium lock list. */
2531
MediumLockList *pMediumLockList(new MediumLockList());
2532
rc = createMediumLockList(true /* fFailIfInaccessible */,
2533
true /* fMediumLockWrite */,
2538
delete pMediumLockList;
2542
rc = pMediumLockList->Lock();
2545
delete pMediumLockList;
2547
tr("Failed to lock media when resetting '%s'"),
2548
getLocationFull().raw());
2551
pProgress.createObject();
2552
rc = pProgress->init(m->pVirtualBox,
2553
static_cast<IMedium*>(this),
2554
BstrFmt(tr("Resetting differencing medium '%s'"), m->strLocationFull.raw()),
2555
FALSE /* aCancelable */);
2559
/* setup task object to carry out the operation asynchronously */
2560
pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
2566
catch (HRESULT aRC) { rc = aRC; }
2570
rc = startThread(pTask);
2573
pProgress.queryInterfaceTo(aProgress);
2577
/* Note: on success, the task will unlock this */
2579
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
HRESULT rc2 = UnlockWrite(NULL);
2587
LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
2592
////////////////////////////////////////////////////////////////////////////////
2594
// Medium internal methods
2596
////////////////////////////////////////////////////////////////////////////////
2599
* Internal method to return the medium's parent medium. Must have caller + locking!
2602
const ComObjPtr<Medium>& Medium::getParent() const
2608
* Internal method to return the medium's list of child media. Must have caller + locking!
2611
const MediaList& Medium::getChildren() const
2613
return m->llChildren;
2617
* Internal method to return the medium's GUID. Must have caller + locking!
2620
const Guid& Medium::getId() const
2626
* Internal method to return the medium's state. Must have caller + locking!
2629
MediumState_T Medium::getState() const
2635
* Internal method to return the medium's variant. Must have caller + locking!
2638
MediumVariant_T Medium::getVariant() const
2644
* Internal method to return the medium's location. Must have caller + locking!
2647
const Utf8Str& Medium::getLocation() const
2649
return m->strLocation;
2653
* Internal method to return the medium's full location. Must have caller + locking!
2656
const Utf8Str& Medium::getLocationFull() const
2658
return m->strLocationFull;
2662
* Internal method to return the medium's format string. Must have caller + locking!
2665
const Utf8Str& Medium::getFormat() const
2667
return m->strFormat;
2671
* Internal method to return the medium's format object. Must have caller + locking!
2674
const ComObjPtr<MediumFormat> & Medium::getMediumFormat() const
2676
return m->formatObj;
2680
* Internal method to return the medium's size. Must have caller + locking!
2683
uint64_t Medium::getSize() const
2689
* Adds the given machine and optionally the snapshot to the list of the objects
2690
* this medium is attached to.
2692
* @param aMachineId Machine ID.
2693
* @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
2695
HRESULT Medium::attachTo(const Guid &aMachineId,
2696
const Guid &aSnapshotId /*= Guid::Empty*/)
2698
AssertReturn(!aMachineId.isEmpty(), E_FAIL);
2700
LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
2702
AutoCaller autoCaller(this);
2703
AssertComRCReturnRC(autoCaller.rc());
2705
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2709
case MediumState_Created:
2710
case MediumState_Inaccessible:
2711
case MediumState_LockedRead:
2712
case MediumState_LockedWrite:
2716
return setStateError();
2719
if (m->numCreateDiffTasks > 0)
2720
return setError(E_FAIL,
2721
tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created"),
2722
m->strLocationFull.raw(),
2724
m->numCreateDiffTasks);
2726
BackRefList::iterator it = std::find_if(m->backRefs.begin(),
2728
BackRef::EqualsTo(aMachineId));
2729
if (it == m->backRefs.end())
2731
BackRef ref(aMachineId, aSnapshotId);
2732
m->backRefs.push_back(ref);
2737
// if the caller has not supplied a snapshot ID, then we're attaching
2738
// to a machine a medium which represents the machine's current state,
2740
if (aSnapshotId.isEmpty())
2742
/* sanity: no duplicate attachments */
2743
AssertReturn(!it->fInCurState, E_FAIL);
2744
it->fInCurState = true;
2749
// otherwise: a snapshot medium is being attached
2751
/* sanity: no duplicate attachments */
2752
for (BackRef::GuidList::const_iterator jt = it->llSnapshotIds.begin();
2753
jt != it->llSnapshotIds.end();
2756
const Guid &idOldSnapshot = *jt;
2758
if (idOldSnapshot == aSnapshotId)
2763
return setError(E_FAIL,
2764
tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
2765
m->strLocationFull.raw(),
2768
idOldSnapshot.raw());
2772
it->llSnapshotIds.push_back(aSnapshotId);
2773
it->fInCurState = false;
2775
LogFlowThisFuncLeave();
2781
* Removes the given machine and optionally the snapshot from the list of the
2782
* objects this medium is attached to.
2784
* @param aMachineId Machine ID.
2785
* @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
2788
HRESULT Medium::detachFrom(const Guid &aMachineId,
2789
const Guid &aSnapshotId /*= Guid::Empty*/)
2791
AssertReturn(!aMachineId.isEmpty(), E_FAIL);
2793
AutoCaller autoCaller(this);
2794
AssertComRCReturnRC(autoCaller.rc());
2796
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
BackRefList::iterator it =
2799
std::find_if(m->backRefs.begin(), m->backRefs.end(),
2800
BackRef::EqualsTo(aMachineId));
2801
AssertReturn(it != m->backRefs.end(), E_FAIL);
2803
if (aSnapshotId.isEmpty())
2805
/* remove the current state attachment */
2806
it->fInCurState = false;
2810
/* remove the snapshot attachment */
2811
BackRef::GuidList::iterator jt =
2812
std::find(it->llSnapshotIds.begin(), it->llSnapshotIds.end(), aSnapshotId);
2814
AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
2815
it->llSnapshotIds.erase(jt);
2818
/* if the backref becomes empty, remove it */
2819
if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
2820
m->backRefs.erase(it);
2826
* Internal method to return the medium's list of backrefs. Must have caller + locking!
2829
const Guid* Medium::getFirstMachineBackrefId() const
2831
if (!m->backRefs.size())
2834
return &m->backRefs.front().machineId;
2837
const Guid* Medium::getFirstMachineBackrefSnapshotId() const
2839
if (!m->backRefs.size())
2842
const BackRef &ref = m->backRefs.front();
2843
if (!ref.llSnapshotIds.size())
2846
return &ref.llSnapshotIds.front();
2851
* Debugging helper that gets called after VirtualBox initialization that writes all
2852
* machine backreferences to the debug log.
2854
void Medium::dumpBackRefs()
2856
AutoCaller autoCaller(this);
2857
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2859
LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.raw()));
2861
for (BackRefList::iterator it2 = m->backRefs.begin();
2862
it2 != m->backRefs.end();
2865
const BackRef &ref = *it2;
2866
LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d)\n", ref.machineId.raw(), ref.fInCurState));
2868
for (BackRef::GuidList::const_iterator jt2 = it2->llSnapshotIds.begin();
2869
jt2 != it2->llSnapshotIds.end();
2872
const Guid &id = *jt2;
2873
LogFlowThisFunc((" Backref from snapshot {%RTuuid}\n", id.raw()));
2880
* Checks if the given change of \a aOldPath to \a aNewPath affects the location
2881
* of this media and updates it if necessary to reflect the new location.
2883
* @param aOldPath Old path (full).
2884
* @param aNewPath New path (full).
2886
* @note Locks this object for writing.
2888
HRESULT Medium::updatePath(const char *aOldPath, const char *aNewPath)
2890
AssertReturn(aOldPath, E_FAIL);
2891
AssertReturn(aNewPath, E_FAIL);
2893
AutoCaller autoCaller(this);
2894
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2896
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.raw()));
2900
const char *pcszMediumPath = m->strLocationFull.c_str();
2902
if (RTPathStartsWith(pcszMediumPath, aOldPath))
2904
Utf8Str newPath = Utf8StrFmt("%s%s",
2906
pcszMediumPath + strlen(aOldPath));
2907
Utf8Str path = newPath;
2908
m->pVirtualBox->calculateRelativePath(path, path);
2909
unconst(m->strLocationFull) = newPath;
2910
unconst(m->strLocation) = path;
2912
LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.raw()));
2919
* Checks if the given change of \a aOldPath to \a aNewPath affects the location
2920
* of this medium or any its child and updates the paths if necessary to
2921
* reflect the new location.
2923
* @param aOldPath Old path (full).
2924
* @param aNewPath New path (full).
2926
* @note Locks the medium tree for reading, this object and all children for writing.
2928
void Medium::updatePaths(const char *aOldPath, const char *aNewPath)
2930
AssertReturnVoid(aOldPath);
2931
AssertReturnVoid(aNewPath);
2933
AutoCaller autoCaller(this);
2934
AssertComRCReturnVoid(autoCaller.rc());
2936
/* we access children() */
2937
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2939
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2941
updatePath(aOldPath, aNewPath);
2943
/* update paths of all children */
2944
for (MediaList::const_iterator it = getChildren().begin();
2945
it != getChildren().end();
2948
(*it)->updatePaths(aOldPath, aNewPath);
2953
* Returns the base medium of the media chain this medium is part of.
2955
* The base medium is found by walking up the parent-child relationship axis.
2956
* If the medium doesn't have a parent (i.e. it's a base medium), it
2957
* returns itself in response to this method.
2959
* @param aLevel Where to store the number of ancestors of this medium
2960
* (zero for the base), may be @c NULL.
2962
* @note Locks medium tree for reading.
2964
ComObjPtr<Medium> Medium::getBase(uint32_t *aLevel /*= NULL*/)
2966
ComObjPtr<Medium> pBase;
2969
AutoCaller autoCaller(this);
2970
AssertReturn(autoCaller.isOk(), pBase);
2972
/* we access mParent */
2973
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2982
AutoCaller baseCaller(pBase);
2983
AssertReturn(baseCaller.isOk(), pBase);
2985
if (pBase->m->pParent.isNull())
2988
pBase = pBase->m->pParent;
3000
* Returns @c true if this medium cannot be modified because it has
3001
* dependants (children) or is part of the snapshot. Related to the medium
3002
* type and posterity, not to the current media state.
3004
* @note Locks this object and medium tree for reading.
3006
bool Medium::isReadOnly()
3008
AutoCaller autoCaller(this);
3009
AssertComRCReturn(autoCaller.rc(), false);
3011
/* we access children */
3012
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3014
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3018
case MediumType_Normal:
3020
if (getChildren().size() != 0)
3023
for (BackRefList::const_iterator it = m->backRefs.begin();
3024
it != m->backRefs.end(); ++it)
3025
if (it->llSnapshotIds.size() != 0)
3030
case MediumType_Immutable:
3032
case MediumType_Writethrough:
3033
case MediumType_Shareable:
3039
AssertFailedReturn(false);
3043
* Saves medium data by appending a new child node to the given
3044
* parent XML settings node.
3046
* @param data Settings struct to be updated.
3048
* @note Locks this object, medium tree and children for reading.
3050
HRESULT Medium::saveSettings(settings::Medium &data)
3052
AutoCaller autoCaller(this);
3053
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3055
/* we access mParent */
3056
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3058
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3061
data.strLocation = m->strLocation;
3062
data.strFormat = m->strFormat;
3064
/* optional, only for diffs, default is false */
3066
data.fAutoReset = m->autoReset;
3068
data.fAutoReset = false;
3071
data.strDescription = m->strDescription;
3073
/* optional properties */
3074
data.properties.clear();
3075
for (Data::PropertyMap::const_iterator it = m->properties.begin();
3076
it != m->properties.end();
3079
/* only save properties that have non-default values */
3080
if (!it->second.isEmpty())
3082
Utf8Str name = it->first;
3083
Utf8Str value = it->second;
3084
data.properties[name] = value;
3088
/* only for base media */
3089
if (m->pParent.isNull())
3090
data.hdType = m->type;
3092
/* save all children */
3093
for (MediaList::const_iterator it = getChildren().begin();
3094
it != getChildren().end();
3097
settings::Medium med;
3098
HRESULT rc = (*it)->saveSettings(med);
3099
AssertComRCReturnRC(rc);
3100
data.llChildren.push_back(med);
3107
* Compares the location of this medium to the given location.
3109
* The comparison takes the location details into account. For example, if the
3110
* location is a file in the host's filesystem, a case insensitive comparison
3111
* will be performed for case insensitive filesystems.
3113
* @param aLocation Location to compare to (as is).
3114
* @param aResult Where to store the result of comparison: 0 if locations
3115
* are equal, 1 if this object's location is greater than
3116
* the specified location, and -1 otherwise.
3118
HRESULT Medium::compareLocationTo(const char *aLocation, int &aResult)
3120
AutoCaller autoCaller(this);
3121
AssertComRCReturnRC(autoCaller.rc());
3123
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3125
Utf8Str locationFull(m->strLocationFull);
3127
/// @todo NEWMEDIA delegate the comparison to the backend?
3129
if (m->formatObj->capabilities() & MediumFormatCapabilities_File)
3131
Utf8Str location(aLocation);
3133
/* For locations represented by files, append the default path if
3134
* only the name is given, and then get the full path. */
3135
if (!RTPathHavePath(aLocation))
3137
location = Utf8StrFmt("%s%c%s",
3138
m->pVirtualBox->getDefaultHardDiskFolder().raw(),
3143
int vrc = m->pVirtualBox->calculateFullPath(location, location);
3144
if (RT_FAILURE(vrc))
3145
return setError(E_FAIL,
3146
tr("Invalid medium storage file location '%s' (%Rrc)"),
3150
aResult = RTPathCompare(locationFull.c_str(), location.c_str());
3153
aResult = locationFull.compare(aLocation);
3159
* Constructs a medium lock list for this medium. The lock is not taken.
3161
* @note Locks the medium tree for reading.
3163
* @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
3164
* inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
3165
* this is necessary for a VM's removable media VM startup for which we do not want to fail.
3166
* @param fMediumLockWrite Whether to associate a write lock with this medium.
3167
* @param pToBeParent Medium which will become the parent of this medium.
3168
* @param mediumLockList Where to store the resulting list.
3170
HRESULT Medium::createMediumLockList(bool fFailIfInaccessible,
3171
bool fMediumLockWrite,
3172
Medium *pToBeParent,
3173
MediumLockList &mediumLockList)
3175
AutoCaller autoCaller(this);
3176
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3180
/* we access parent medium objects */
3181
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3183
/* paranoid sanity checking if the medium has a to-be parent medium */
3186
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3187
ComAssertRet(getParent().isNull(), E_FAIL);
3188
ComAssertRet(getChildren().size() == 0, E_FAIL);
3191
ErrorInfoKeeper eik;
3192
MultiResult mrc(S_OK);
3194
ComObjPtr<Medium> pMedium = this;
3195
while (!pMedium.isNull())
3197
// need write lock for RefreshState if medium is inaccessible
3198
AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
3200
/* Accessibility check must be first, otherwise locking interferes
3201
* with getting the medium state. Lock lists are not created for
3202
* fun, and thus getting the medium status is no luxury. */
3203
MediumState_T mediumState = pMedium->getState();
3204
if (mediumState == MediumState_Inaccessible)
3206
rc = pMedium->RefreshState(&mediumState);
3207
if (FAILED(rc)) return rc;
3209
if (mediumState == MediumState_Inaccessible)
3211
// ignore inaccessible ISO media and silently return S_OK,
3212
// otherwise VM startup (esp. restore) may fail without good reason
3213
if (!fFailIfInaccessible)
3216
// otherwise report an error
3218
rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
3219
if (FAILED(rc)) return rc;
3221
/* collect multiple errors */
3223
Assert(!error.isEmpty());
3224
mrc = setError(E_FAIL,
3227
// error message will be something like
3228
// "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
3233
if (pMedium == this)
3234
mediumLockList.Prepend(pMedium, fMediumLockWrite);
3236
mediumLockList.Prepend(pMedium, false);
3238
pMedium = pMedium->getParent();
3239
if (pMedium.isNull() && pToBeParent)
3241
pMedium = pToBeParent;
3250
* Returns a preferred format for differencing media.
3252
Bstr Medium::preferredDiffFormat()
3256
AutoCaller autoCaller(this);
3257
AssertComRCReturn(autoCaller.rc(), strFormat);
3259
/* m->strFormat is const, no need to lock */
3260
strFormat = m->strFormat;
3262
/* check that our own format supports diffs */
3263
if (!(m->formatObj->capabilities() & MediumFormatCapabilities_Differencing))
3265
/* use the default format if not */
3266
AutoReadLock propsLock(m->pVirtualBox->systemProperties() COMMA_LOCKVAL_SRC_POS);
3267
strFormat = m->pVirtualBox->getDefaultHardDiskFormat();
3274
* Returns the medium type. Must have caller + locking!
3277
MediumType_T Medium::getType() const
3283
////////////////////////////////////////////////////////////////////////////////
3286
* Returns a short version of the location attribute.
3288
* @note Must be called from under this object's read or write lock.
3290
Utf8Str Medium::getName()
3292
Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
3297
* Sets the value of m->strLocation and calculates the value of m->strLocationFull.
3299
* Treats non-FS-path locations specially, and prepends the default medium
3300
* folder if the given location string does not contain any path information
3303
* Also, if the specified location is a file path that ends with '/' then the
3304
* file name part will be generated by this method automatically in the format
3305
* '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
3306
* and assign to this medium, and <ext> is the default extension for this
3307
* medium's storage format. Note that this procedure requires the media state to
3308
* be NotCreated and will return a failure otherwise.
3310
* @param aLocation Location of the storage unit. If the location is a FS-path,
3311
* then it can be relative to the VirtualBox home directory.
3312
* @param aFormat Optional fallback format if it is an import and the format
3313
* cannot be determined.
3315
* @note Must be called from under this object's write lock.
3317
HRESULT Medium::setLocation(const Utf8Str &aLocation, const Utf8Str &aFormat)
3319
AssertReturn(!aLocation.isEmpty(), E_FAIL);
3321
AutoCaller autoCaller(this);
3322
AssertComRCReturnRC(autoCaller.rc());
3324
/* formatObj may be null only when initializing from an existing path and
3325
* no format is known yet */
3326
AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
3327
|| ( autoCaller.state() == InInit
3328
&& m->state != MediumState_NotCreated
3330
&& m->strFormat.isEmpty()
3331
&& m->formatObj.isNull()),
3334
/* are we dealing with a new medium constructed using the existing
3336
bool isImport = m->strFormat.isEmpty();
3339
|| ( (m->formatObj->capabilities() & MediumFormatCapabilities_File)
3344
Utf8Str location(aLocation);
3346
if (m->state == MediumState_NotCreated)
3348
/* must be a file (formatObj must be already known) */
3349
Assert(m->formatObj->capabilities() & MediumFormatCapabilities_File);
3351
if (RTPathFilename(location.c_str()) == NULL)
3353
/* no file name is given (either an empty string or ends with a
3354
* slash), generate a new UUID + file name if the state allows
3357
ComAssertMsgRet(!m->formatObj->fileExtensions().empty(),
3358
("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
3361
Bstr ext = m->formatObj->fileExtensions().front();
3362
ComAssertMsgRet(!ext.isEmpty(),
3363
("Default extension must not be empty\n"),
3368
location = Utf8StrFmt("%s{%RTuuid}.%ls",
3369
location.raw(), id.raw(), ext.raw());
3373
/* append the default folder if no path is given */
3374
if (!RTPathHavePath(location.c_str()))
3375
location = Utf8StrFmt("%s%c%s",
3376
m->pVirtualBox->getDefaultHardDiskFolder().raw(),
3380
/* get the full file name */
3381
Utf8Str locationFull;
3382
int vrc = m->pVirtualBox->calculateFullPath(location, locationFull);
3383
if (RT_FAILURE(vrc))
3384
return setError(VBOX_E_FILE_ERROR,
3385
tr("Invalid medium storage file location '%s' (%Rrc)"),
3386
location.raw(), vrc);
3388
/* detect the backend from the storage unit if importing */
3391
char *backendName = NULL;
3396
vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
3397
if (RT_SUCCESS(vrc))
3400
if (RT_SUCCESS(vrc))
3402
vrc = VDGetFormat(NULL, locationFull.c_str(), &backendName);
3404
else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
3406
/* assume it's not a file, restore the original location */
3407
location = locationFull = aLocation;
3408
vrc = VDGetFormat(NULL, locationFull.c_str(), &backendName);
3411
if (RT_FAILURE(vrc))
3413
if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
3414
return setError(VBOX_E_FILE_ERROR,
3415
tr("Could not find file for the medium '%s' (%Rrc)"),
3416
locationFull.raw(), vrc);
3417
else if (aFormat.isEmpty())
3418
return setError(VBOX_E_IPRT_ERROR,
3419
tr("Could not get the storage format of the medium '%s' (%Rrc)"),
3420
locationFull.raw(), vrc);
3423
HRESULT rc = setFormat(Bstr(aFormat));
3424
/* setFormat() must not fail since we've just used the backend so
3425
* the format object must be there */
3426
AssertComRCReturnRC(rc);
3431
ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
3433
HRESULT rc = setFormat(Bstr(backendName));
3434
RTStrFree(backendName);
3436
/* setFormat() must not fail since we've just used the backend so
3437
* the format object must be there */
3438
AssertComRCReturnRC(rc);
3442
/* is it still a file? */
3443
if (m->formatObj->capabilities() & MediumFormatCapabilities_File)
3445
m->strLocation = location;
3446
m->strLocationFull = locationFull;
3448
if (m->state == MediumState_NotCreated)
3450
/* assign a new UUID (this UUID will be used when calling
3451
* VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
3452
* also do that if we didn't generate it to make sure it is
3453
* either generated by us or reset to null */
3454
unconst(m->id) = id;
3459
m->strLocation = locationFull;
3460
m->strLocationFull = locationFull;
3465
m->strLocation = aLocation;
3466
m->strLocationFull = aLocation;
3473
* Queries information from the medium.
3475
* As a result of this call, the accessibility state and data members such as
3476
* size and description will be updated with the current information.
3478
* @note This method may block during a system I/O call that checks storage
3481
* @note Locks medium tree for reading and writing (for new diff media checked
3482
* for the first time). Locks mParent for reading. Locks this object for
3485
HRESULT Medium::queryInfo()
3487
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3489
if ( m->state != MediumState_Created
3490
&& m->state != MediumState_Inaccessible
3491
&& m->state != MediumState_LockedRead)
3496
int vrc = VINF_SUCCESS;
3498
/* check if a blocking queryInfo() call is in progress on some other thread,
3499
* and wait for it to finish if so instead of querying data ourselves */
3500
if (m->queryInfoRunning)
3502
Assert( m->state == MediumState_LockedRead
3503
|| m->state == MediumState_LockedWrite);
3506
vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
3514
bool success = false;
3515
Utf8Str lastAccessError;
3517
/* are we dealing with a new medium constructed using the existing
3519
bool isImport = m->id.isEmpty();
3520
unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
3522
/* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
3523
* media because that would prevent necessary modifications
3524
* when opening media of some third-party formats for the first
3525
* time in VirtualBox (such as VMDK for which VDOpen() needs to
3526
* generate an UUID if it is missing) */
3527
if ( (m->hddOpenMode == OpenReadOnly)
3530
uOpenFlags |= VD_OPEN_FLAGS_READONLY;
3532
/* Open shareable medium with the appropriate flags */
3533
if (m->type == MediumType_Shareable)
3534
uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
3536
/* Lock the medium, which makes the behavior much more consistent */
3537
if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
3538
rc = LockRead(NULL);
3540
rc = LockWrite(NULL);
3541
if (FAILED(rc)) return rc;
3543
/* Copies of the input state fields which are not read-only,
3544
* as we're dropping the lock. CAUTION: be extremely careful what
3545
* you do with the contents of this medium object, as you will
3546
* create races if there are concurrent changes. */
3547
Utf8Str format(m->strFormat);
3548
Utf8Str location(m->strLocationFull);
3549
ComObjPtr<MediumFormat> formatObj = m->formatObj;
3551
/* "Output" values which can't be set because the lock isn't held
3552
* at the time the values are determined. */
3553
Guid mediumId = m->id;
3554
uint64_t mediumSize = 0;
3555
uint64_t mediumLogicalSize = 0;
3557
/* Flag whether a base image has a non-zero parent UUID and thus
3558
* need repairing after it was closed again. */
3559
bool fRepairImageZeroParentUuid = false;
3561
/* leave the lock before a lengthy operation */
3562
vrc = RTSemEventMultiReset(m->queryInfoSem);
3563
AssertRCReturn(vrc, E_FAIL);
3564
m->queryInfoRunning = true;
3569
/* skip accessibility checks for host drives */
3577
vrc = VDCreate(m->vdDiskIfaces, &hdd);
3578
ComAssertRCThrow(vrc, E_FAIL);
3582
/** @todo This kind of opening of media is assuming that diff
3583
* media can be opened as base media. Should be documented that
3584
* it must work for all medium format backends. */
3590
if (RT_FAILURE(vrc))
3592
lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
3593
location.c_str(), vdError(vrc).c_str());
3597
if (formatObj->capabilities() & MediumFormatCapabilities_Uuid)
3599
/* Modify the UUIDs if necessary. The associated fields are
3600
* not modified by other code, so no need to copy. */
3603
vrc = VDSetUuid(hdd, 0, m->imageId);
3604
ComAssertRCThrow(vrc, E_FAIL);
3608
vrc = VDSetParentUuid(hdd, 0, m->parentId);
3609
ComAssertRCThrow(vrc, E_FAIL);
3611
/* zap the information, these are no long-term members */
3612
m->setImageId = false;
3613
unconst(m->imageId).clear();
3614
m->setParentId = false;
3615
unconst(m->parentId).clear();
3617
/* check the UUID */
3619
vrc = VDGetUuid(hdd, 0, &uuid);
3620
ComAssertRCThrow(vrc, E_FAIL);
3626
if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
3627
// only when importing a VDMK that has no UUID, create one in memory
3632
Assert(!mediumId.isEmpty());
3634
if (mediumId != uuid)
3636
lastAccessError = Utf8StrFmt(
3637
tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
3641
m->pVirtualBox->settingsFilePath().c_str());
3648
/* the backend does not support storing UUIDs within the
3649
* underlying storage so use what we store in XML */
3651
/* generate an UUID for an imported UUID-less medium */
3655
mediumId = m->imageId;
3661
/* get the medium variant */
3662
unsigned uImageFlags;
3663
vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
3664
ComAssertRCThrow(vrc, E_FAIL);
3665
m->variant = (MediumVariant_T)uImageFlags;
3667
/* check/get the parent uuid and update corresponding state */
3668
if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
3671
vrc = VDGetParentUuid(hdd, 0, &parentId);
3672
ComAssertRCThrow(vrc, E_FAIL);
3676
/* the parent must be known to us. Note that we freely
3677
* call locking methods of mVirtualBox and parent, as all
3678
* relevant locks must be already held. There may be no
3679
* concurrent access to the just opened medium on other
3680
* threads yet (and init() will fail if this method reports
3681
* MediumState_Inaccessible) */
3684
ComObjPtr<Medium> pParent;
3685
rc = m->pVirtualBox->findHardDisk(&id, NULL,
3686
false /* aSetError */,
3690
lastAccessError = Utf8StrFmt(
3691
tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
3692
&parentId, location.c_str(),
3693
m->pVirtualBox->settingsFilePath().c_str());
3697
/* we set mParent & children() */
3698
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3700
Assert(m->pParent.isNull());
3701
m->pParent = pParent;
3702
m->pParent->m->llChildren.push_back(this);
3706
/* we access mParent */
3707
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3709
/* check that parent UUIDs match. Note that there's no need
3710
* for the parent's AutoCaller (our lifetime is bound to
3713
if (m->pParent.isNull())
3715
/* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
3716
* and 3.1.0-3.1.8 there are base images out there
3717
* which have a non-zero parent UUID. No point in
3718
* complaining about them, instead automatically
3719
* repair the problem. Later we can bring back the
3720
* error message, but we should wait until really
3721
* most users have repaired their images, either with
3722
* VBoxFixHdd or this way. */
3724
fRepairImageZeroParentUuid = true;
3726
lastAccessError = Utf8StrFmt(
3727
tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
3729
m->pVirtualBox->settingsFilePath().c_str());
3734
AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
3735
if ( !fRepairImageZeroParentUuid
3736
&& m->pParent->getState() != MediumState_Inaccessible
3737
&& m->pParent->getId() != parentId)
3739
lastAccessError = Utf8StrFmt(
3740
tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
3741
&parentId, location.c_str(),
3742
m->pParent->getId().raw(),
3743
m->pVirtualBox->settingsFilePath().c_str());
3747
/// @todo NEWMEDIA what to do if the parent is not
3748
/// accessible while the diff is? Probably nothing. The
3749
/// real code will detect the mismatch anyway.
3753
mediumSize = VDGetFileSize(hdd, 0);
3754
mediumLogicalSize = VDGetSize(hdd, 0) / _1M;
3773
unconst(m->id) = mediumId;
3777
m->size = mediumSize;
3778
m->logicalSize = mediumLogicalSize;
3779
m->strLastAccessError.setNull();
3783
m->strLastAccessError = lastAccessError;
3784
LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
3785
location.c_str(), m->strLastAccessError.c_str(),
3789
/* inform other callers if there are any */
3790
RTSemEventMultiSignal(m->queryInfoSem);
3791
m->queryInfoRunning = false;
3793
/* Set the proper state according to the result of the check */
3795
m->preLockState = MediumState_Created;
3797
m->preLockState = MediumState_Inaccessible;
3800
if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
3801
rc2 = UnlockRead(NULL);
3803
rc2 = UnlockWrite(NULL);
3804
if (SUCCEEDED(rc) && FAILED(rc2))
3806
if (FAILED(rc)) return rc;
3808
/* If this is a base image which incorrectly has a parent UUID set,
3809
* repair the image now by zeroing the parent UUID. This is only done
3810
* when we have structural information from a config file, on import
3811
* this is not possible. If someone would accidentally call openMedium
3812
* with a diff image before the base is registered this would destroy
3813
* the diff. Not acceptable. */
3814
if (fRepairImageZeroParentUuid)
3816
rc = LockWrite(NULL);
3817
if (FAILED(rc)) return rc;
3824
vrc = VDCreate(m->vdDiskIfaces, &hdd);
3825
ComAssertRCThrow(vrc, E_FAIL);
3832
uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
3834
if (RT_FAILURE(vrc))
3837
RTUUID zeroParentUuid;
3838
RTUuidClear(&zeroParentUuid);
3839
vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
3840
ComAssertRCThrow(vrc, E_FAIL);
3856
rc = UnlockWrite(NULL);
3857
if (SUCCEEDED(rc) && FAILED(rc2))
3859
if (FAILED(rc)) return rc;
3866
* Sets the extended error info according to the current media state.
3868
* @note Must be called from under this object's write or read lock.
3870
HRESULT Medium::setStateError()
3872
HRESULT rc = E_FAIL;
3876
case MediumState_NotCreated:
3878
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3879
tr("Storage for the medium '%s' is not created"),
3880
m->strLocationFull.raw());
3883
case MediumState_Created:
3885
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3886
tr("Storage for the medium '%s' is already created"),
3887
m->strLocationFull.raw());
3890
case MediumState_LockedRead:
3892
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3893
tr("Medium '%s' is locked for reading by another task"),
3894
m->strLocationFull.raw());
3897
case MediumState_LockedWrite:
3899
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3900
tr("Medium '%s' is locked for writing by another task"),
3901
m->strLocationFull.raw());
3904
case MediumState_Inaccessible:
3906
/* be in sync with Console::powerUpThread() */
3907
if (!m->strLastAccessError.isEmpty())
3908
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3909
tr("Medium '%s' is not accessible. %s"),
3910
m->strLocationFull.raw(), m->strLastAccessError.c_str());
3912
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3913
tr("Medium '%s' is not accessible"),
3914
m->strLocationFull.raw());
3917
case MediumState_Creating:
3919
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3920
tr("Storage for the medium '%s' is being created"),
3921
m->strLocationFull.raw());
3924
case MediumState_Deleting:
3926
rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3927
tr("Storage for the medium '%s' is being deleted"),
3928
m->strLocationFull.raw());
3942
* Deletes the medium storage unit.
3944
* If @a aProgress is not NULL but the object it points to is @c null then a new
3945
* progress object will be created and assigned to @a *aProgress on success,
3946
* otherwise the existing progress object is used. If Progress is NULL, then no
3947
* progress object is created/used at all.
3949
* When @a aWait is @c false, this method will create a thread to perform the
3950
* delete operation asynchronously and will return immediately. Otherwise, it
3951
* will perform the operation on the calling thread and will not return to the
3952
* caller until the operation is completed. Note that @a aProgress cannot be
3953
* NULL when @a aWait is @c false (this method will assert in this case).
3955
* @param aProgress Where to find/store a Progress object to track operation
3957
* @param aWait @c true if this method should block instead of creating
3958
* an asynchronous thread.
3959
* @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3960
* by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3961
* This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
3962
* and this parameter is ignored.
3964
* @note Locks mVirtualBox and this object for writing. Locks medium tree for
3967
HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
3969
bool *pfNeedsSaveSettings)
3971
AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
3973
AutoCaller autoCaller(this);
3974
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3977
ComObjPtr<Progress> pProgress;
3978
Medium::Task *pTask = NULL;
3982
/* we're accessing the media tree, and canClose() needs it too */
3983
AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
3985
COMMA_LOCKVAL_SRC_POS);
3986
LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
3988
if ( !(m->formatObj->capabilities() & ( MediumFormatCapabilities_CreateDynamic
3989
| MediumFormatCapabilities_CreateFixed)))
3990
throw setError(VBOX_E_NOT_SUPPORTED,
3991
tr("Medium format '%s' does not support storage deletion"),
3992
m->strFormat.raw());
3994
/* Note that we are fine with Inaccessible state too: a) for symmetry
3995
* with create calls and b) because it doesn't really harm to try, if
3996
* it is really inaccessible, the delete operation will fail anyway.
3997
* Accepting Inaccessible state is especially important because all
3998
* registered media are initially Inaccessible upon VBoxSVC startup
3999
* until COMGETTER(RefreshState) is called. Accept Deleting state
4000
* because some callers need to put the medium in this state early
4001
* to prevent races. */
4004
case MediumState_Created:
4005
case MediumState_Deleting:
4006
case MediumState_Inaccessible:
4009
throw setStateError();
4012
if (m->backRefs.size() != 0)
4014
Utf8Str strMachines;
4015
for (BackRefList::const_iterator it = m->backRefs.begin();
4016
it != m->backRefs.end();
4019
const BackRef &b = *it;
4020
if (strMachines.length())
4021
strMachines.append(", ");
4022
strMachines.append(b.machineId.toString().c_str());
4027
throw setError(VBOX_E_OBJECT_IN_USE,
4028
tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
4029
m->strLocationFull.c_str(),
4031
strMachines.c_str());
4038
/* go to Deleting state, so that the medium is not actually locked */
4039
if (m->state != MediumState_Deleting)
4041
rc = markForDeletion();
4046
/* Build the medium lock list. */
4047
MediumLockList *pMediumLockList(new MediumLockList());
4048
rc = createMediumLockList(true /* fFailIfInaccessible */,
4049
true /* fMediumLockWrite */,
4054
delete pMediumLockList;
4058
rc = pMediumLockList->Lock();
4061
delete pMediumLockList;
4063
tr("Failed to lock media when deleting '%s'"),
4064
getLocationFull().raw());
4067
/* try to remove from the list of known media before performing
4068
* actual deletion (we favor the consistency of the media registry
4069
* which would have been broken if unregisterWithVirtualBox() failed
4070
* after we successfully deleted the storage) */
4071
rc = unregisterWithVirtualBox(pfNeedsSaveSettings);
4074
// no longer need lock
4075
multilock.release();
4077
if (aProgress != NULL)
4079
/* use the existing progress object... */
4080
pProgress = *aProgress;
4082
/* ...but create a new one if it is null */
4083
if (pProgress.isNull())
4085
pProgress.createObject();
4086
rc = pProgress->init(m->pVirtualBox,
4087
static_cast<IMedium*>(this),
4088
BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.raw()),
4089
FALSE /* aCancelable */);
4095
/* setup task object to carry out the operation sync/async */
4096
pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
4102
catch (HRESULT aRC) { rc = aRC; }
4107
rc = runNow(pTask, NULL /* pfNeedsSaveSettings*/);
4109
rc = startThread(pTask);
4111
if (SUCCEEDED(rc) && aProgress != NULL)
4112
*aProgress = pProgress;
4120
/* Undo deleting state if necessary. */
4121
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4122
unmarkForDeletion();
4129
* Mark a medium for deletion.
4131
* @note Caller must hold the write lock on this medium!
4133
HRESULT Medium::markForDeletion()
4135
ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4138
case MediumState_Created:
4139
case MediumState_Inaccessible:
4140
m->preLockState = m->state;
4141
m->state = MediumState_Deleting;
4144
return setStateError();
4149
* Removes the "mark for deletion".
4151
* @note Caller must hold the write lock on this medium!
4153
HRESULT Medium::unmarkForDeletion()
4155
ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4158
case MediumState_Deleting:
4159
m->state = m->preLockState;
4162
return setStateError();
4167
* Mark a medium for deletion which is in locked state.
4169
* @note Caller must hold the write lock on this medium!
4171
HRESULT Medium::markLockedForDeletion()
4173
ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4174
if ( ( m->state == MediumState_LockedRead
4175
|| m->state == MediumState_LockedWrite)
4176
&& m->preLockState == MediumState_Created)
4178
m->preLockState = MediumState_Deleting;
4182
return setStateError();
4186
* Removes the "mark for deletion" for a medium in locked state.
4188
* @note Caller must hold the write lock on this medium!
4190
HRESULT Medium::unmarkLockedForDeletion()
4192
ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4193
if ( ( m->state == MediumState_LockedRead
4194
|| m->state == MediumState_LockedWrite)
4195
&& m->preLockState == MediumState_Deleting)
4197
m->preLockState = MediumState_Created;
4201
return setStateError();
4205
* Creates a new differencing storage unit using the format of the given target
4206
* medium and the location. Note that @c aTarget must be NotCreated.
4208
* The @a aMediumLockList parameter contains the associated medium lock list,
4209
* which must be in locked state. If @a aWait is @c true then the caller is
4210
* responsible for unlocking.
4212
* If @a aProgress is not NULL but the object it points to is @c null then a
4213
* new progress object will be created and assigned to @a *aProgress on
4214
* success, otherwise the existing progress object is used. If @a aProgress is
4215
* NULL, then no progress object is created/used at all.
4217
* When @a aWait is @c false, this method will create a thread to perform the
4218
* create operation asynchronously and will return immediately. Otherwise, it
4219
* will perform the operation on the calling thread and will not return to the
4220
* caller until the operation is completed. Note that @a aProgress cannot be
4221
* NULL when @a aWait is @c false (this method will assert in this case).
4223
* @param aTarget Target medium.
4224
* @param aVariant Precise medium variant to create.
4225
* @param aMediumLockList List of media which should be locked.
4226
* @param aProgress Where to find/store a Progress object to track
4227
* operation completion.
4228
* @param aWait @c true if this method should block instead of
4229
* creating an asynchronous thread.
4230
* @param pfNeedsSaveSettings Optional pointer to a bool that must have been
4231
* initialized to false and that will be set to true
4232
* by this function if the caller should invoke
4233
* VirtualBox::saveSettings() because the global
4234
* settings have changed. This only works in "wait"
4235
* mode; otherwise saveSettings is called
4236
* automatically by the thread that was created,
4237
* and this parameter is ignored.
4239
* @note Locks this object and @a aTarget for writing.
4241
HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
4242
MediumVariant_T aVariant,
4243
MediumLockList *aMediumLockList,
4244
ComObjPtr<Progress> *aProgress,
4246
bool *pfNeedsSaveSettings)
4248
AssertReturn(!aTarget.isNull(), E_FAIL);
4249
AssertReturn(aMediumLockList, E_FAIL);
4250
AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4252
AutoCaller autoCaller(this);
4253
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4255
AutoCaller targetCaller(aTarget);
4256
if (FAILED(targetCaller.rc())) return targetCaller.rc();
4259
ComObjPtr<Progress> pProgress;
4260
Medium::Task *pTask = NULL;
4264
AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
4266
ComAssertThrow( m->type != MediumType_Writethrough
4267
&& m->type != MediumType_Shareable, E_FAIL);
4268
ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
4270
if (aTarget->m->state != MediumState_NotCreated)
4271
throw aTarget->setStateError();
4273
/* Check that the medium is not attached to the current state of
4274
* any VM referring to it. */
4275
for (BackRefList::const_iterator it = m->backRefs.begin();
4276
it != m->backRefs.end();
4279
if (it->fInCurState)
4281
/* Note: when a VM snapshot is being taken, all normal media
4282
* attached to the VM in the current state will be, as an
4283
* exception, also associated with the snapshot which is about
4284
* to create (see SnapshotMachine::init()) before deassociating
4285
* them from the current state (which takes place only on
4286
* success in Machine::fixupHardDisks()), so that the size of
4287
* snapshotIds will be 1 in this case. The extra condition is
4288
* used to filter out this legal situation. */
4289
if (it->llSnapshotIds.size() == 0)
4290
throw setError(VBOX_E_INVALID_OBJECT_STATE,
4291
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"),
4292
m->strLocationFull.raw(), it->machineId.raw());
4294
Assert(it->llSnapshotIds.size() == 1);
4298
if (aProgress != NULL)
4300
/* use the existing progress object... */
4301
pProgress = *aProgress;
4303
/* ...but create a new one if it is null */
4304
if (pProgress.isNull())
4306
pProgress.createObject();
4307
rc = pProgress->init(m->pVirtualBox,
4308
static_cast<IMedium*>(this),
4309
BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.raw()),
4310
TRUE /* aCancelable */);
4316
/* setup task object to carry out the operation sync/async */
4317
pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
4319
aWait /* fKeepMediumLockList */);
4325
/* register a task (it will deregister itself when done) */
4326
++m->numCreateDiffTasks;
4327
Assert(m->numCreateDiffTasks != 0); /* overflow? */
4329
aTarget->m->state = MediumState_Creating;
4331
catch (HRESULT aRC) { rc = aRC; }
4336
rc = runNow(pTask, pfNeedsSaveSettings);
4338
rc = startThread(pTask);
4340
if (SUCCEEDED(rc) && aProgress != NULL)
4341
*aProgress = pProgress;
4343
else if (pTask != NULL)
4350
* Prepares this (source) medium, target medium and all intermediate media
4351
* for the merge operation.
4353
* This method is to be called prior to calling the #mergeTo() to perform
4354
* necessary consistency checks and place involved media to appropriate
4355
* states. If #mergeTo() is not called or fails, the state modifications
4356
* performed by this method must be undone by #cancelMergeTo().
4358
* See #mergeTo() for more information about merging.
4360
* @param pTarget Target medium.
4361
* @param aMachineId Allowed machine attachment. NULL means do not check.
4362
* @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
4364
* @param fLockMedia Flag whether to lock the medium lock list or not.
4365
* If set to false and the medium lock list locking fails
4366
* later you must call #cancelMergeTo().
4367
* @param fMergeForward Resulting merge direction (out).
4368
* @param pParentForTarget New parent for target medium after merge (out).
4369
* @param aChildrenToReparent List of children of the source which will have
4370
* to be reparented to the target after merge (out).
4371
* @param aMediumLockList Medium locking information (out).
4373
* @note Locks medium tree for reading. Locks this object, aTarget and all
4374
* intermediate media for writing.
4376
HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
4377
const Guid *aMachineId,
4378
const Guid *aSnapshotId,
4380
bool &fMergeForward,
4381
ComObjPtr<Medium> &pParentForTarget,
4382
MediaList &aChildrenToReparent,
4383
MediumLockList * &aMediumLockList)
4385
AssertReturn(pTarget != NULL, E_FAIL);
4386
AssertReturn(pTarget != this, E_FAIL);
4388
AutoCaller autoCaller(this);
4389
AssertComRCReturnRC(autoCaller.rc());
4391
AutoCaller targetCaller(pTarget);
4392
AssertComRCReturnRC(targetCaller.rc());
4395
fMergeForward = false;
4396
pParentForTarget.setNull();
4397
aChildrenToReparent.clear();
4398
Assert(aMediumLockList == NULL);
4399
aMediumLockList = NULL;
4403
// locking: we need the tree lock first because we access parent pointers
4404
AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4406
/* more sanity checking and figuring out the merge direction */
4407
ComObjPtr<Medium> pMedium = getParent();
4408
while (!pMedium.isNull() && pMedium != pTarget)
4409
pMedium = pMedium->getParent();
4410
if (pMedium == pTarget)
4411
fMergeForward = false;
4414
pMedium = pTarget->getParent();
4415
while (!pMedium.isNull() && pMedium != this)
4416
pMedium = pMedium->getParent();
4417
if (pMedium == this)
4418
fMergeForward = true;
4423
AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4424
tgtLoc = pTarget->getLocationFull();
4427
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4428
throw setError(E_FAIL,
4429
tr("Media '%s' and '%s' are unrelated"),
4430
m->strLocationFull.raw(), tgtLoc.raw());
4434
/* Build the lock list. */
4435
aMediumLockList = new MediumLockList();
4437
rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
4438
true /* fMediumLockWrite */,
4442
rc = createMediumLockList(true /* fFailIfInaccessible */,
4443
false /* fMediumLockWrite */,
4449
/* Sanity checking, must be after lock list creation as it depends on
4450
* valid medium states. The medium objects must be accessible. Only
4451
* do this if immediate locking is requested, otherwise it fails when
4452
* we construct a medium lock list for an already running VM. Snapshot
4453
* deletion uses this to simplify its life. */
4457
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4458
if (m->state != MediumState_Created)
4459
throw setStateError();
4462
AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4463
if (pTarget->m->state != MediumState_Created)
4464
throw pTarget->setStateError();
4468
/* check medium attachment and other sanity conditions */
4471
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4472
if (getChildren().size() > 1)
4474
throw setError(E_FAIL,
4475
tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4476
m->strLocationFull.raw(), getChildren().size());
4478
/* One backreference is only allowed if the machine ID is not empty
4479
* and it matches the machine the medium is attached to (including
4480
* the snapshot ID if not empty). */
4481
if ( m->backRefs.size() != 0
4483
|| m->backRefs.size() != 1
4484
|| aMachineId->isEmpty()
4485
|| *getFirstMachineBackrefId() != *aMachineId
4486
|| ( (!aSnapshotId || !aSnapshotId->isEmpty())
4487
&& *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
4488
throw setError(E_FAIL,
4489
tr("Medium '%s' is attached to %d virtual machines"),
4490
m->strLocationFull.raw(), m->backRefs.size());
4491
if (m->type == MediumType_Immutable)
4492
throw setError(E_FAIL,
4493
tr("Medium '%s' is immutable"),
4494
m->strLocationFull.raw());
4498
AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4499
if (pTarget->getChildren().size() > 1)
4501
throw setError(E_FAIL,
4502
tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4503
pTarget->m->strLocationFull.raw(),
4504
pTarget->getChildren().size());
4506
if (pTarget->m->type == MediumType_Immutable)
4507
throw setError(E_FAIL,
4508
tr("Medium '%s' is immutable"),
4509
pTarget->m->strLocationFull.raw());
4511
ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
4512
ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
4513
for (pLast = pLastIntermediate;
4514
!pLast.isNull() && pLast != pTarget && pLast != this;
4515
pLast = pLast->getParent())
4517
AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4518
if (pLast->getChildren().size() > 1)
4520
throw setError(E_FAIL,
4521
tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4522
pLast->m->strLocationFull.raw(),
4523
pLast->getChildren().size());
4525
if (pLast->m->backRefs.size() != 0)
4526
throw setError(E_FAIL,
4527
tr("Medium '%s' is attached to %d virtual machines"),
4528
pLast->m->strLocationFull.raw(),
4529
pLast->m->backRefs.size());
4533
/* Update medium states appropriately */
4534
if (m->state == MediumState_Created)
4536
rc = markForDeletion();
4543
throw setStateError();
4544
else if ( m->state == MediumState_LockedWrite
4545
|| m->state == MediumState_LockedRead)
4547
/* Either mark it for deletiion in locked state or allow
4548
* others to have done so. */
4549
if (m->preLockState == MediumState_Created)
4550
markLockedForDeletion();
4551
else if (m->preLockState != MediumState_Deleting)
4552
throw setStateError();
4555
throw setStateError();
4560
/* we will need parent to reparent target */
4561
pParentForTarget = m->pParent;
4565
/* we will need to reparent children of the source */
4566
for (MediaList::const_iterator it = getChildren().begin();
4567
it != getChildren().end();
4573
rc = pMedium->LockWrite(NULL);
4578
aChildrenToReparent.push_back(pMedium);
4581
for (pLast = pLastIntermediate;
4582
!pLast.isNull() && pLast != pTarget && pLast != this;
4583
pLast = pLast->getParent())
4585
AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4586
if (pLast->m->state == MediumState_Created)
4588
rc = pLast->markForDeletion();
4593
throw pLast->setStateError();
4596
/* Tweak the lock list in the backward merge case, as the target
4597
* isn't marked to be locked for writing yet. */
4600
MediumLockList::Base::iterator lockListBegin =
4601
aMediumLockList->GetBegin();
4602
MediumLockList::Base::iterator lockListEnd =
4603
aMediumLockList->GetEnd();
4605
for (MediumLockList::Base::iterator it = lockListBegin;
4609
MediumLock &mediumLock = *it;
4610
if (mediumLock.GetMedium() == pTarget)
4612
HRESULT rc2 = mediumLock.UpdateLock(true);
4621
rc = aMediumLockList->Lock();
4624
AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4626
tr("Failed to lock media when merging to '%s'"),
4627
pTarget->getLocationFull().raw());
4631
catch (HRESULT aRC) { rc = aRC; }
4635
delete aMediumLockList;
4636
aMediumLockList = NULL;
4643
* Merges this medium to the specified medium which must be either its
4644
* direct ancestor or descendant.
4646
* Given this medium is SOURCE and the specified medium is TARGET, we will
4647
* get two variants of the merge operation:
4650
* ------------------------->
4651
* [Extra] <- SOURCE <- Intermediate <- TARGET
4652
* Any Del Del LockWr
4656
* <-------------------------
4657
* TARGET <- Intermediate <- SOURCE <- [Extra]
4658
* LockWr Del Del LockWr
4660
* Each diagram shows the involved media on the media chain where
4661
* SOURCE and TARGET belong. Under each medium there is a state value which
4662
* the medium must have at a time of the mergeTo() call.
4664
* The media in the square braces may be absent (e.g. when the forward
4665
* operation takes place and SOURCE is the base medium, or when the backward
4666
* merge operation takes place and TARGET is the last child in the chain) but if
4667
* they present they are involved too as shown.
4669
* Neither the source medium nor intermediate media may be attached to
4670
* any VM directly or in the snapshot, otherwise this method will assert.
4672
* The #prepareMergeTo() method must be called prior to this method to place all
4673
* involved to necessary states and perform other consistency checks.
4675
* If @a aWait is @c true then this method will perform the operation on the
4676
* calling thread and will not return to the caller until the operation is
4677
* completed. When this method succeeds, all intermediate medium objects in
4678
* the chain will be uninitialized, the state of the target medium (and all
4679
* involved extra media) will be restored. @a aMediumLockList will not be
4680
* deleted, whether the operation is successful or not. The caller has to do
4681
* this if appropriate. Note that this (source) medium is not uninitialized
4682
* because of possible AutoCaller instances held by the caller of this method
4683
* on the current thread. It's therefore the responsibility of the caller to
4684
* call Medium::uninit() after releasing all callers.
4686
* If @a aWait is @c false then this method will create a thread to perform the
4687
* operation asynchronously and will return immediately. If the operation
4688
* succeeds, the thread will uninitialize the source medium object and all
4689
* intermediate medium objects in the chain, reset the state of the target
4690
* medium (and all involved extra media) and delete @a aMediumLockList.
4691
* If the operation fails, the thread will only reset the states of all
4692
* involved media and delete @a aMediumLockList.
4694
* When this method fails (regardless of the @a aWait mode), it is a caller's
4695
* responsiblity to undo state changes and delete @a aMediumLockList using
4698
* If @a aProgress is not NULL but the object it points to is @c null then a new
4699
* progress object will be created and assigned to @a *aProgress on success,
4700
* otherwise the existing progress object is used. If Progress is NULL, then no
4701
* progress object is created/used at all. Note that @a aProgress cannot be
4702
* NULL when @a aWait is @c false (this method will assert in this case).
4704
* @param pTarget Target medium.
4705
* @param fMergeForward Merge direction.
4706
* @param pParentForTarget New parent for target medium after merge.
4707
* @param aChildrenToReparent List of children of the source which will have
4708
* to be reparented to the target after merge.
4709
* @param aMediumLockList Medium locking information.
4710
* @param aProgress Where to find/store a Progress object to track operation
4712
* @param aWait @c true if this method should block instead of creating
4713
* an asynchronous thread.
4714
* @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
4715
* by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
4716
* This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
4717
* and this parameter is ignored.
4719
* @note Locks the tree lock for writing. Locks the media from the chain
4722
HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
4724
const ComObjPtr<Medium> &pParentForTarget,
4725
const MediaList &aChildrenToReparent,
4726
MediumLockList *aMediumLockList,
4727
ComObjPtr <Progress> *aProgress,
4729
bool *pfNeedsSaveSettings)
4731
AssertReturn(pTarget != NULL, E_FAIL);
4732
AssertReturn(pTarget != this, E_FAIL);
4733
AssertReturn(aMediumLockList != NULL, E_FAIL);
4734
AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4736
AutoCaller autoCaller(this);
4737
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4739
AutoCaller targetCaller(pTarget);
4740
AssertComRCReturnRC(targetCaller.rc());
4743
ComObjPtr <Progress> pProgress;
4744
Medium::Task *pTask = NULL;
4748
if (aProgress != NULL)
4750
/* use the existing progress object... */
4751
pProgress = *aProgress;
4753
/* ...but create a new one if it is null */
4754
if (pProgress.isNull())
4758
AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4759
tgtName = pTarget->getName();
4762
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4764
pProgress.createObject();
4765
rc = pProgress->init(m->pVirtualBox,
4766
static_cast<IMedium*>(this),
4767
BstrFmt(tr("Merging medium '%s' to '%s'"),
4770
TRUE /* aCancelable */);
4776
/* setup task object to carry out the operation sync/async */
4777
pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
4778
pParentForTarget, aChildrenToReparent,
4779
pProgress, aMediumLockList,
4780
aWait /* fKeepMediumLockList */);
4786
catch (HRESULT aRC) { rc = aRC; }
4791
rc = runNow(pTask, pfNeedsSaveSettings);
4793
rc = startThread(pTask);
4795
if (SUCCEEDED(rc) && aProgress != NULL)
4796
*aProgress = pProgress;
4798
else if (pTask != NULL)
4805
* Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
4806
* called or fails. Frees memory occupied by @a aMediumLockList and unlocks
4807
* the medium objects in @a aChildrenToReparent.
4809
* @param aChildrenToReparent List of children of the source which will have
4810
* to be reparented to the target after merge.
4811
* @param aMediumLockList Medium locking information.
4813
* @note Locks the media from the chain for writing.
4815
void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
4816
MediumLockList *aMediumLockList)
4818
AutoCaller autoCaller(this);
4819
AssertComRCReturnVoid(autoCaller.rc());
4821
AssertReturnVoid(aMediumLockList != NULL);
4823
/* Revert media marked for deletion to previous state. */
4825
MediumLockList::Base::const_iterator mediumListBegin =
4826
aMediumLockList->GetBegin();
4827
MediumLockList::Base::const_iterator mediumListEnd =
4828
aMediumLockList->GetEnd();
4829
for (MediumLockList::Base::const_iterator it = mediumListBegin;
4830
it != mediumListEnd;
4833
const MediumLock &mediumLock = *it;
4834
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4835
AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4837
if (pMedium->m->state == MediumState_Deleting)
4839
rc = pMedium->unmarkForDeletion();
4844
/* the destructor will do the work */
4845
delete aMediumLockList;
4847
/* unlock the children which had to be reparented */
4848
for (MediaList::const_iterator it = aChildrenToReparent.begin();
4849
it != aChildrenToReparent.end();
4852
const ComObjPtr<Medium> &pMedium = *it;
4854
AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4855
pMedium->UnlockWrite(NULL);
4860
* Checks that the format ID is valid and sets it on success.
4862
* Note that this method will caller-reference the format object on success!
4863
* This reference must be released somewhere to let the MediumFormat object be
4866
* @note Must be called from under this object's write lock.
4868
HRESULT Medium::setFormat(CBSTR aFormat)
4870
/* get the format object first */
4872
AutoReadLock propsLock(m->pVirtualBox->systemProperties() COMMA_LOCKVAL_SRC_POS);
4874
unconst(m->formatObj)
4875
= m->pVirtualBox->systemProperties()->mediumFormat(aFormat);
4876
if (m->formatObj.isNull())
4877
return setError(E_INVALIDARG,
4878
tr("Invalid medium storage format '%ls'"),
4881
/* reference the format permanently to prevent its unexpected
4882
* uninitialization */
4883
HRESULT rc = m->formatObj->addCaller();
4884
AssertComRCReturnRC(rc);
4886
/* get properties (preinsert them as keys in the map). Note that the
4887
* map doesn't grow over the object life time since the set of
4888
* properties is meant to be constant. */
4890
Assert(m->properties.empty());
4892
for (MediumFormat::PropertyList::const_iterator it =
4893
m->formatObj->properties().begin();
4894
it != m->formatObj->properties().end();
4897
m->properties.insert(std::make_pair(it->name, Bstr::Null));
4901
unconst(m->strFormat) = aFormat;
4907
* @note Also reused by Medium::Reset().
4909
* @note Caller must hold the media tree write lock!
4911
HRESULT Medium::canClose()
4913
Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4915
if (getChildren().size() != 0)
4916
return setError(E_FAIL,
4917
tr("Cannot close medium '%s' because it has %d child media"),
4918
m->strLocationFull.raw(), getChildren().size());
4924
* Calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending
4925
* on the device type of this medium.
4927
* @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
4928
* by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
4930
* @note Caller must have locked the media tree lock for writing!
4932
HRESULT Medium::unregisterWithVirtualBox(bool *pfNeedsSaveSettings)
4934
/* Note that we need to de-associate ourselves from the parent to let
4935
* unregisterHardDisk() properly save the registry */
4937
/* we modify mParent and access children */
4938
Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4940
Medium *pParentBackup = m->pParent;
4941
AssertReturn(getChildren().size() == 0, E_FAIL);
4945
HRESULT rc = E_FAIL;
4948
case DeviceType_DVD:
4949
rc = m->pVirtualBox->unregisterImage(this, DeviceType_DVD, pfNeedsSaveSettings);
4952
case DeviceType_Floppy:
4953
rc = m->pVirtualBox->unregisterImage(this, DeviceType_Floppy, pfNeedsSaveSettings);
4956
case DeviceType_HardDisk:
4957
rc = m->pVirtualBox->unregisterHardDisk(this, pfNeedsSaveSettings);
4968
/* re-associate with the parent as we are still relatives in the
4970
m->pParent = pParentBackup;
4971
m->pParent->m->llChildren.push_back(this);
4979
* Returns the last error message collected by the vdErrorCall callback and
4982
* The error message is returned prepended with a dot and a space, like this:
4984
* ". <error_text> (%Rrc)"
4986
* to make it easily appendable to a more general error message. The @c %Rrc
4987
* format string is given @a aVRC as an argument.
4989
* If there is no last error message collected by vdErrorCall or if it is a
4990
* null or empty string, then this function returns the following text:
4995
* @note Doesn't do any object locking; it is assumed that the caller makes sure
4996
* the callback isn't called by more than one thread at a time.
4998
* @param aVRC VBox error code to use when no error message is provided.
5000
Utf8Str Medium::vdError(int aVRC)
5004
if (m->vdError.isEmpty())
5005
error = Utf8StrFmt(" (%Rrc)", aVRC);
5007
error = Utf8StrFmt(".\n%s", m->vdError.raw());
5009
m->vdError.setNull();
5015
* Error message callback.
5017
* Puts the reported error message to the m->vdError field.
5019
* @note Doesn't do any object locking; it is assumed that the caller makes sure
5020
* the callback isn't called by more than one thread at a time.
5022
* @param pvUser The opaque data passed on container creation.
5023
* @param rc The VBox error code.
5024
* @param RT_SRC_POS_DECL Use RT_SRC_POS.
5025
* @param pszFormat Error message format string.
5026
* @param va Error message arguments.
5029
DECLCALLBACK(void) Medium::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
5030
const char *pszFormat, va_list va)
5032
NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
5034
Medium *that = static_cast<Medium*>(pvUser);
5035
AssertReturnVoid(that != NULL);
5037
if (that->m->vdError.isEmpty())
5039
Utf8StrFmt("%s (%Rrc)", Utf8StrFmtVA(pszFormat, va).raw(), rc);
5042
Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.raw(),
5043
Utf8StrFmtVA(pszFormat, va).raw(), rc);
5047
DECLCALLBACK(bool) Medium::vdConfigAreKeysValid(void *pvUser,
5048
const char * /* pszzValid */)
5050
Medium *that = static_cast<Medium*>(pvUser);
5051
AssertReturn(that != NULL, false);
5053
/* we always return true since the only keys we have are those found in
5059
DECLCALLBACK(int) Medium::vdConfigQuerySize(void *pvUser, const char *pszName,
5062
AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
5064
Medium *that = static_cast<Medium*>(pvUser);
5065
AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
5067
Data::PropertyMap::const_iterator it =
5068
that->m->properties.find(Bstr(pszName));
5069
if (it == that->m->properties.end())
5070
return VERR_CFGM_VALUE_NOT_FOUND;
5072
/* we interpret null values as "no value" in Medium */
5073
if (it->second.isEmpty())
5074
return VERR_CFGM_VALUE_NOT_FOUND;
5076
*pcbValue = RTUtf16CalcUtf8Len(it->second.raw()) + 1 /* include terminator */;
5078
return VINF_SUCCESS;
5082
DECLCALLBACK(int) Medium::vdConfigQuery(void *pvUser, const char *pszName,
5083
char *pszValue, size_t cchValue)
5085
AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
5087
Medium *that = static_cast<Medium*>(pvUser);
5088
AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
5090
Data::PropertyMap::const_iterator it =
5091
that->m->properties.find(Bstr(pszName));
5092
if (it == that->m->properties.end())
5093
return VERR_CFGM_VALUE_NOT_FOUND;
5095
Utf8Str value = it->second;
5096
if (value.length() >= cchValue)
5097
return VERR_CFGM_NOT_ENOUGH_SPACE;
5099
/* we interpret null values as "no value" in Medium */
5100
if (it->second.isEmpty())
5101
return VERR_CFGM_VALUE_NOT_FOUND;
5103
memcpy(pszValue, value.c_str(), value.length() + 1);
5105
return VINF_SUCCESS;
5108
/** Wrapper for closing a connection as we always want a forceful shutdown. */
5109
DECLCALLBACK(int) Medium::vdTcpClientClose(RTSOCKET Sock)
5111
return RTTcpClientCloseEx(Sock, false /* fGracefulShutdown */);
5115
DECLCALLBACK(int) Medium::vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
5117
PVDSOCKETINT pSocketInt = NULL;
5119
if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
5120
return VERR_NOT_SUPPORTED;
5122
pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
5124
return VERR_NO_MEMORY;
5126
pSocketInt->hSocket = NIL_RTSOCKET;
5127
*pSock = pSocketInt;
5128
return VINF_SUCCESS;
5131
DECLCALLBACK(int) Medium::vdTcpSocketDestroy(VDSOCKET Sock)
5133
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5135
if (pSocketInt->hSocket != NIL_RTSOCKET)
5136
RTTcpClientCloseEx(pSocketInt->hSocket, false /* fGracefulShutdown */);
5138
RTMemFree(pSocketInt);
5140
return VINF_SUCCESS;
5143
DECLCALLBACK(int) Medium::vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
5145
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5147
return RTTcpClientConnect(pszAddress, uPort, &pSocketInt->hSocket);
5150
DECLCALLBACK(int) Medium::vdTcpClientClose(VDSOCKET Sock)
5152
int rc = VINF_SUCCESS;
5153
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5155
rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /* fGracefulShutdown */);
5156
pSocketInt->hSocket = NIL_RTSOCKET;
5160
DECLCALLBACK(bool) Medium::vdTcpIsClientConnected(VDSOCKET Sock)
5162
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5163
return pSocketInt->hSocket != NIL_RTSOCKET;
5166
DECLCALLBACK(int) Medium::vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
5168
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5169
return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
5172
DECLCALLBACK(int) Medium::vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
5174
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5175
return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
5178
DECLCALLBACK(int) Medium::vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
5180
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5181
return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
5184
DECLCALLBACK(int) Medium::vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
5186
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5187
return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
5190
DECLCALLBACK(int) Medium::vdTcpFlush(VDSOCKET Sock)
5192
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5193
return RTTcpFlush(pSocketInt->hSocket);
5196
DECLCALLBACK(int) Medium::vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
5198
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5199
return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
5202
DECLCALLBACK(int) Medium::vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
5204
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5205
return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
5208
DECLCALLBACK(int) Medium::vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
5210
PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5211
return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
5216
* Starts a new thread driven by the appropriate Medium::Task::handler() method.
5218
* @note When the task is executed by this method, IProgress::notifyComplete()
5219
* is automatically called for the progress object associated with this
5220
* task when the task is finished to signal the operation completion for
5221
* other threads asynchronously waiting for it.
5223
HRESULT Medium::startThread(Medium::Task *pTask)
5225
#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
5226
/* Extreme paranoia: The calling thread should not hold the medium
5227
* tree lock or any medium lock. Since there is no separate lock class
5228
* for medium objects be even more strict: no other object locks. */
5229
Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
5230
Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
5233
/// @todo use a more descriptive task name
5234
int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
5235
0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
5237
if (RT_FAILURE(vrc))
5240
ComAssertMsgRCRet(vrc,
5241
("Could not create Medium::Task thread (%Rrc)\n",
5250
* Fix the parent UUID of all children to point to this medium as their
5253
HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
5255
MediumLockList mediumLockList;
5256
HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
5257
false /* fMediumLockWrite */,
5260
AssertComRCReturnRC(rc);
5265
int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5266
ComAssertRCThrow(vrc, E_FAIL);
5270
MediumLockList::Base::iterator lockListBegin =
5271
mediumLockList.GetBegin();
5272
MediumLockList::Base::iterator lockListEnd =
5273
mediumLockList.GetEnd();
5274
for (MediumLockList::Base::iterator it = lockListBegin;
5278
MediumLock &mediumLock = *it;
5279
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5280
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5284
pMedium->m->strFormat.c_str(),
5285
pMedium->m->strLocationFull.c_str(),
5286
VD_OPEN_FLAGS_READONLY,
5287
pMedium->m->vdDiskIfaces);
5288
if (RT_FAILURE(vrc))
5292
for (MediaList::const_iterator it = childrenToReparent.begin();
5293
it != childrenToReparent.end();
5296
/* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
5298
(*it)->m->strFormat.c_str(),
5299
(*it)->m->strLocationFull.c_str(),
5301
(*it)->m->vdDiskIfaces);
5302
if (RT_FAILURE(vrc))
5305
vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id);
5306
if (RT_FAILURE(vrc))
5309
vrc = VDClose(hdd, false /* fDelete */);
5310
if (RT_FAILURE(vrc))
5313
(*it)->UnlockWrite(NULL);
5316
catch (HRESULT aRC) { rc = aRC; }
5319
throw setError(E_FAIL,
5320
tr("Could not update medium UUID references to parent '%s' (%s)"),
5321
m->strLocationFull.raw(),
5322
vdError(aVRC).raw());
5327
catch (HRESULT aRC) { rc = aRC; }
5333
* Runs Medium::Task::handler() on the current thread instead of creating
5336
* This call implies that it is made on another temporary thread created for
5337
* some asynchronous task. Avoid calling it from a normal thread since the task
5338
* operations are potentially lengthy and will block the calling thread in this
5341
* @note When the task is executed by this method, IProgress::notifyComplete()
5342
* is not called for the progress object associated with this task when
5343
* the task is finished. Instead, the result of the operation is returned
5344
* by this method directly and it's the caller's responsibility to
5345
* complete the progress object in this case.
5347
HRESULT Medium::runNow(Medium::Task *pTask,
5348
bool *pfNeedsSaveSettings)
5350
#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
5351
/* Extreme paranoia: The calling thread should not hold the medium
5352
* tree lock or any medium lock. Since there is no separate lock class
5353
* for medium objects be even more strict: no other object locks. */
5354
Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
5355
Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
5358
pTask->m_pfNeedsSaveSettings = pfNeedsSaveSettings;
5360
/* NIL_RTTHREAD indicates synchronous call. */
5361
return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
5365
* Implementation code for the "create base" task.
5367
* This only gets started from Medium::CreateBaseStorage() and always runs
5368
* asynchronously. As a result, we always save the VirtualBox.xml file when
5374
HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
5378
/* these parameters we need after creation */
5379
uint64_t size = 0, logicalSize = 0;
5380
MediumVariant_T variant = MediumVariant_Standard;
5381
bool fGenerateUuid = false;
5385
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
5387
/* The object may request a specific UUID (through a special form of
5388
* the setLocation() argument). Otherwise we have to generate it */
5390
fGenerateUuid = id.isEmpty();
5394
/* VirtualBox::registerHardDisk() will need UUID */
5395
unconst(m->id) = id;
5398
Utf8Str format(m->strFormat);
5399
Utf8Str location(m->strLocationFull);
5400
uint64_t capabilities = m->formatObj->capabilities();
5401
ComAssertThrow(capabilities & ( VD_CAP_CREATE_FIXED
5402
| VD_CAP_CREATE_DYNAMIC), E_FAIL);
5403
Assert(m->state == MediumState_Creating);
5406
int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5407
ComAssertRCThrow(vrc, E_FAIL);
5409
/* unlock before the potentially lengthy operation */
5414
/* ensure the directory exists */
5415
rc = VirtualBox::ensureFilePathExists(location);
5419
PDMMEDIAGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
5421
vrc = VDCreateBase(hdd,
5430
VD_OPEN_FLAGS_NORMAL,
5432
task.mVDOperationIfaces);
5433
if (RT_FAILURE(vrc))
5434
throw setError(E_FAIL,
5435
tr("Could not create the medium storage unit '%s'%s"),
5436
location.raw(), vdError(vrc).raw());
5438
size = VDGetFileSize(hdd, 0);
5439
logicalSize = VDGetSize(hdd, 0) / _1M;
5440
unsigned uImageFlags;
5441
vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5442
if (RT_SUCCESS(vrc))
5443
variant = (MediumVariant_T)uImageFlags;
5445
catch (HRESULT aRC) { rc = aRC; }
5449
catch (HRESULT aRC) { rc = aRC; }
5453
/* register with mVirtualBox as the last step and move to
5454
* Created state only on success (leaving an orphan file is
5455
* better than breaking media registry consistency) */
5456
bool fNeedsSaveSettings = false;
5457
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5458
rc = m->pVirtualBox->registerHardDisk(this, &fNeedsSaveSettings);
5461
if (fNeedsSaveSettings)
5463
AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
5464
m->pVirtualBox->saveSettings();
5468
// reenter the lock before changing state
5469
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
5473
m->state = MediumState_Created;
5476
m->logicalSize = logicalSize;
5477
m->variant = variant;
5481
/* back to NotCreated on failure */
5482
m->state = MediumState_NotCreated;
5484
/* reset UUID to prevent it from being reused next time */
5486
unconst(m->id).clear();
5493
* Implementation code for the "create diff" task.
5495
* This task always gets started from Medium::createDiffStorage() and can run
5496
* synchronously or asynchronously depending on the "wait" parameter passed to
5497
* that function. If we run synchronously, the caller expects the bool
5498
* *pfNeedsSaveSettings to be set before returning; otherwise (in asynchronous
5499
* mode), we save the settings ourselves.
5504
HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
5508
bool fNeedsSaveSettings = false;
5510
const ComObjPtr<Medium> &pTarget = task.mTarget;
5512
uint64_t size = 0, logicalSize = 0;
5513
MediumVariant_T variant = MediumVariant_Standard;
5514
bool fGenerateUuid = false;
5518
/* Lock both in {parent,child} order. */
5519
AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
5521
/* The object may request a specific UUID (through a special form of
5522
* the setLocation() argument). Otherwise we have to generate it */
5523
Guid targetId = pTarget->m->id;
5524
fGenerateUuid = targetId.isEmpty();
5528
/* VirtualBox::registerHardDisk() will need UUID */
5529
unconst(pTarget->m->id) = targetId;
5534
Utf8Str targetFormat(pTarget->m->strFormat);
5535
Utf8Str targetLocation(pTarget->m->strLocationFull);
5536
uint64_t capabilities = m->formatObj->capabilities();
5537
ComAssertThrow(capabilities & VD_CAP_CREATE_DYNAMIC, E_FAIL);
5539
Assert(pTarget->m->state == MediumState_Creating);
5540
Assert(m->state == MediumState_LockedRead);
5543
int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5544
ComAssertRCThrow(vrc, E_FAIL);
5546
/* the two media are now protected by their non-default states;
5547
* unlock the media before the potentially lengthy operation */
5548
mediaLock.release();
5552
/* Open all media in the target chain but the last. */
5553
MediumLockList::Base::const_iterator targetListBegin =
5554
task.mpMediumLockList->GetBegin();
5555
MediumLockList::Base::const_iterator targetListEnd =
5556
task.mpMediumLockList->GetEnd();
5557
for (MediumLockList::Base::const_iterator it = targetListBegin;
5558
it != targetListEnd;
5561
const MediumLock &mediumLock = *it;
5562
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5564
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5566
/* Skip over the target diff medium */
5567
if (pMedium->m->state == MediumState_Creating)
5571
Assert(pMedium->m->state == MediumState_LockedRead);
5573
/* Open all media in appropriate mode. */
5575
pMedium->m->strFormat.c_str(),
5576
pMedium->m->strLocationFull.c_str(),
5577
VD_OPEN_FLAGS_READONLY,
5578
pMedium->m->vdDiskIfaces);
5579
if (RT_FAILURE(vrc))
5580
throw setError(E_FAIL,
5581
tr("Could not open the medium storage unit '%s'%s"),
5582
pMedium->m->strLocationFull.raw(),
5583
vdError(vrc).raw());
5586
/* ensure the target directory exists */
5587
rc = VirtualBox::ensureFilePathExists(targetLocation);
5591
vrc = VDCreateDiff(hdd,
5592
targetFormat.c_str(),
5593
targetLocation.c_str(),
5594
task.mVariant | VD_IMAGE_FLAGS_DIFF,
5598
VD_OPEN_FLAGS_NORMAL,
5599
pTarget->m->vdDiskIfaces,
5600
task.mVDOperationIfaces);
5601
if (RT_FAILURE(vrc))
5602
throw setError(E_FAIL,
5603
tr("Could not create the differencing medium storage unit '%s'%s"),
5604
targetLocation.raw(), vdError(vrc).raw());
5606
size = VDGetFileSize(hdd, VD_LAST_IMAGE);
5607
logicalSize = VDGetSize(hdd, VD_LAST_IMAGE) / _1M;
5608
unsigned uImageFlags;
5609
vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5610
if (RT_SUCCESS(vrc))
5611
variant = (MediumVariant_T)uImageFlags;
5613
catch (HRESULT aRC) { rc = aRC; }
5617
catch (HRESULT aRC) { rc = aRC; }
5621
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5623
Assert(pTarget->m->pParent.isNull());
5625
/* associate the child with the parent */
5626
pTarget->m->pParent = this;
5627
m->llChildren.push_back(pTarget);
5629
/** @todo r=klaus neither target nor base() are locked,
5630
* potential race! */
5631
/* diffs for immutable media are auto-reset by default */
5632
pTarget->m->autoReset = (getBase()->m->type == MediumType_Immutable);
5634
/* register with mVirtualBox as the last step and move to
5635
* Created state only on success (leaving an orphan file is
5636
* better than breaking media registry consistency) */
5637
rc = m->pVirtualBox->registerHardDisk(pTarget, &fNeedsSaveSettings);
5640
/* break the parent association on failure to register */
5644
AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
5648
pTarget->m->state = MediumState_Created;
5650
pTarget->m->size = size;
5651
pTarget->m->logicalSize = logicalSize;
5652
pTarget->m->variant = variant;
5656
/* back to NotCreated on failure */
5657
pTarget->m->state = MediumState_NotCreated;
5659
pTarget->m->autoReset = false;
5661
/* reset UUID to prevent it from being reused next time */
5663
unconst(pTarget->m->id).clear();
5666
// deregister the task registered in createDiffStorage()
5667
Assert(m->numCreateDiffTasks != 0);
5668
--m->numCreateDiffTasks;
5672
if (fNeedsSaveSettings)
5674
// save the global settings; for that we should hold only the VirtualBox lock
5675
mediaLock.release();
5676
AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
5677
m->pVirtualBox->saveSettings();
5681
// synchronous mode: report save settings result to caller
5682
if (task.m_pfNeedsSaveSettings)
5683
*task.m_pfNeedsSaveSettings = fNeedsSaveSettings;
5685
/* Note that in sync mode, it's the caller's responsibility to
5686
* unlock the medium. */
5692
* Implementation code for the "merge" task.
5694
* This task always gets started from Medium::mergeTo() and can run
5695
* synchronously or asynchrously depending on the "wait" parameter passed to
5696
* that function. If we run synchronously, the caller expects the bool
5697
* *pfNeedsSaveSettings to be set before returning; otherwise (in asynchronous
5698
* mode), we save the settings ourselves.
5703
HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
5707
const ComObjPtr<Medium> &pTarget = task.mTarget;
5712
int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5713
ComAssertRCThrow(vrc, E_FAIL);
5717
// Similar code appears in SessionMachine::onlineMergeMedium, so
5718
// if you make any changes below check whether they are applicable
5719
// in that context as well.
5721
unsigned uTargetIdx = VD_LAST_IMAGE;
5722
unsigned uSourceIdx = VD_LAST_IMAGE;
5723
/* Open all media in the chain. */
5724
MediumLockList::Base::iterator lockListBegin =
5725
task.mpMediumLockList->GetBegin();
5726
MediumLockList::Base::iterator lockListEnd =
5727
task.mpMediumLockList->GetEnd();
5729
for (MediumLockList::Base::iterator it = lockListBegin;
5733
MediumLock &mediumLock = *it;
5734
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5736
if (pMedium == this)
5738
else if (pMedium == pTarget)
5741
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5744
* complex sanity (sane complexity)
5746
* The current medium must be in the Deleting (medium is merged)
5747
* or LockedRead (parent medium) state if it is not the target.
5748
* If it is the target it must be in the LockedWrite state.
5750
Assert( ( pMedium != pTarget
5751
&& ( pMedium->m->state == MediumState_Deleting
5752
|| pMedium->m->state == MediumState_LockedRead))
5753
|| ( pMedium == pTarget
5754
&& pMedium->m->state == MediumState_LockedWrite));
5757
* Medium must be the target, in the LockedRead state
5758
* or Deleting state where it is not allowed to be attached
5759
* to a virtual machine.
5761
Assert( pMedium == pTarget
5762
|| pMedium->m->state == MediumState_LockedRead
5763
|| ( pMedium->m->backRefs.size() == 0
5764
&& pMedium->m->state == MediumState_Deleting));
5765
/* The source medium must be in Deleting state. */
5766
Assert( pMedium != this
5767
|| pMedium->m->state == MediumState_Deleting);
5769
unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
5771
if ( pMedium->m->state == MediumState_LockedRead
5772
|| pMedium->m->state == MediumState_Deleting)
5773
uOpenFlags = VD_OPEN_FLAGS_READONLY;
5774
if (pMedium->m->type == MediumType_Shareable)
5775
uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
5777
/* Open the medium */
5779
pMedium->m->strFormat.c_str(),
5780
pMedium->m->strLocationFull.c_str(),
5782
pMedium->m->vdDiskIfaces);
5783
if (RT_FAILURE(vrc))
5789
ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
5790
&& uTargetIdx != VD_LAST_IMAGE, E_FAIL);
5792
vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
5793
task.mVDOperationIfaces);
5794
if (RT_FAILURE(vrc))
5797
/* update parent UUIDs */
5798
if (!task.mfMergeForward)
5800
/* we need to update UUIDs of all source's children
5801
* which cannot be part of the container at once so
5802
* add each one in there individually */
5803
if (task.mChildrenToReparent.size() > 0)
5805
for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
5806
it != task.mChildrenToReparent.end();
5809
/* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
5811
(*it)->m->strFormat.c_str(),
5812
(*it)->m->strLocationFull.c_str(),
5814
(*it)->m->vdDiskIfaces);
5815
if (RT_FAILURE(vrc))
5818
vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
5820
if (RT_FAILURE(vrc))
5823
vrc = VDClose(hdd, false /* fDelete */);
5824
if (RT_FAILURE(vrc))
5827
(*it)->UnlockWrite(NULL);
5832
catch (HRESULT aRC) { rc = aRC; }
5835
throw setError(E_FAIL,
5836
tr("Could not merge the medium '%s' to '%s'%s"),
5837
m->strLocationFull.raw(),
5838
pTarget->m->strLocationFull.raw(),
5839
vdError(aVRC).raw());
5844
catch (HRESULT aRC) { rc = aRC; }
5850
/* all media but the target were successfully deleted by
5851
* VDMerge; reparent the last one and uninitialize deleted media. */
5853
AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5855
if (task.mfMergeForward)
5857
/* first, unregister the target since it may become a base
5858
* medium which needs re-registration */
5859
rc2 = m->pVirtualBox->unregisterHardDisk(pTarget, NULL /*&fNeedsSaveSettings*/);
5862
/* then, reparent it and disconnect the deleted branch at
5863
* both ends (chain->parent() is source's parent) */
5864
pTarget->deparent();
5865
pTarget->m->pParent = task.mParentForTarget;
5866
if (pTarget->m->pParent)
5868
pTarget->m->pParent->m->llChildren.push_back(pTarget);
5872
/* then, register again */
5873
rc2 = m->pVirtualBox->registerHardDisk(pTarget, NULL /*&fNeedsSaveSettings*/);
5878
Assert(pTarget->getChildren().size() == 1);
5879
Medium *targetChild = pTarget->getChildren().front();
5881
/* disconnect the deleted branch at the elder end */
5882
targetChild->deparent();
5884
/* reparent source's children and disconnect the deleted
5885
* branch at the younger end */
5886
if (task.mChildrenToReparent.size() > 0)
5888
/* obey {parent,child} lock order */
5889
AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
5891
for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
5892
it != task.mChildrenToReparent.end();
5895
Medium *pMedium = *it;
5896
AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
5898
pMedium->deparent(); // removes pMedium from source
5899
pMedium->setParent(pTarget);
5904
/* unregister and uninitialize all media removed by the merge */
5905
MediumLockList::Base::iterator lockListBegin =
5906
task.mpMediumLockList->GetBegin();
5907
MediumLockList::Base::iterator lockListEnd =
5908
task.mpMediumLockList->GetEnd();
5909
for (MediumLockList::Base::iterator it = lockListBegin;
5913
MediumLock &mediumLock = *it;
5914
/* Create a real copy of the medium pointer, as the medium
5915
* lock deletion below would invalidate the referenced object. */
5916
const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
5918
/* The target and all media not merged (readonly) are skipped */
5919
if ( pMedium == pTarget
5920
|| pMedium->m->state == MediumState_LockedRead)
5926
rc2 = pMedium->m->pVirtualBox->unregisterHardDisk(pMedium,
5927
NULL /*pfNeedsSaveSettings*/);
5930
/* now, uninitialize the deleted medium (note that
5931
* due to the Deleting state, uninit() will not touch
5932
* the parent-child relationship so we need to
5933
* uninitialize each disk individually) */
5935
/* note that the operation initiator medium (which is
5936
* normally also the source medium) is a special case
5937
* -- there is one more caller added by Task to it which
5938
* we must release. Also, if we are in sync mode, the
5939
* caller may still hold an AutoCaller instance for it
5940
* and therefore we cannot uninit() it (it's therefore
5941
* the caller's responsibility) */
5942
if (pMedium == this)
5944
Assert(getChildren().size() == 0);
5945
Assert(m->backRefs.size() == 0);
5946
task.mMediumCaller.release();
5949
/* Delete the medium lock list entry, which also releases the
5950
* caller added by MergeChain before uninit() and updates the
5951
* iterator to point to the right place. */
5952
rc2 = task.mpMediumLockList->RemoveByIterator(it);
5955
if (task.isAsync() || pMedium != this)
5962
// in asynchronous mode, save settings now
5963
// for that we should hold only the VirtualBox lock
5964
AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
5965
m->pVirtualBox->saveSettings();
5968
// synchronous mode: report save settings result to caller
5969
if (task.m_pfNeedsSaveSettings)
5970
*task.m_pfNeedsSaveSettings = true;
5974
/* Here we come if either VDMerge() failed (in which case we
5975
* assume that it tried to do everything to make a further
5976
* retry possible -- e.g. not deleted intermediate media
5977
* and so on) or VirtualBox::saveSettings() failed (where we
5978
* should have the original tree but with intermediate storage
5979
* units deleted by VDMerge()). We have to only restore states
5980
* (through the MergeChain dtor) unless we are run synchronously
5981
* in which case it's the responsibility of the caller as stated
5982
* in the mergeTo() docs. The latter also implies that we
5983
* don't own the merge chain, so release it in this case. */
5986
Assert(task.mChildrenToReparent.size() == 0);
5987
cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
5995
* Implementation code for the "clone" task.
5997
* This only gets started from Medium::CloneTo() and always runs asynchronously.
5998
* As a result, we always save the VirtualBox.xml file when we're done here.
6003
HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
6007
const ComObjPtr<Medium> &pTarget = task.mTarget;
6008
const ComObjPtr<Medium> &pParent = task.mParent;
6010
bool fCreatingTarget = false;
6012
uint64_t size = 0, logicalSize = 0;
6013
MediumVariant_T variant = MediumVariant_Standard;
6014
bool fGenerateUuid = false;
6018
/* Lock all in {parent,child} order. The lock is also used as a
6019
* signal from the task initiator (which releases it only after
6020
* RTThreadCreate()) that we can start the job. */
6021
AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
6023
fCreatingTarget = pTarget->m->state == MediumState_Creating;
6025
/* The object may request a specific UUID (through a special form of
6026
* the setLocation() argument). Otherwise we have to generate it */
6027
Guid targetId = pTarget->m->id;
6028
fGenerateUuid = targetId.isEmpty();
6032
/* VirtualBox::registerHardDisk() will need UUID */
6033
unconst(pTarget->m->id) = targetId;
6037
int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6038
ComAssertRCThrow(vrc, E_FAIL);
6042
/* Open all media in the source chain. */
6043
MediumLockList::Base::const_iterator sourceListBegin =
6044
task.mpSourceMediumLockList->GetBegin();
6045
MediumLockList::Base::const_iterator sourceListEnd =
6046
task.mpSourceMediumLockList->GetEnd();
6047
for (MediumLockList::Base::const_iterator it = sourceListBegin;
6048
it != sourceListEnd;
6051
const MediumLock &mediumLock = *it;
6052
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6053
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6056
Assert(pMedium->m->state == MediumState_LockedRead);
6058
/** Open all media in read-only mode. */
6060
pMedium->m->strFormat.c_str(),
6061
pMedium->m->strLocationFull.c_str(),
6062
VD_OPEN_FLAGS_READONLY,
6063
pMedium->m->vdDiskIfaces);
6064
if (RT_FAILURE(vrc))
6065
throw setError(E_FAIL,
6066
tr("Could not open the medium storage unit '%s'%s"),
6067
pMedium->m->strLocationFull.raw(),
6068
vdError(vrc).raw());
6071
Utf8Str targetFormat(pTarget->m->strFormat);
6072
Utf8Str targetLocation(pTarget->m->strLocationFull);
6074
Assert( pTarget->m->state == MediumState_Creating
6075
|| pTarget->m->state == MediumState_LockedWrite);
6076
Assert(m->state == MediumState_LockedRead);
6077
Assert(pParent.isNull() || pParent->m->state == MediumState_LockedRead);
6079
/* unlock before the potentially lengthy operation */
6082
/* ensure the target directory exists */
6083
rc = VirtualBox::ensureFilePathExists(targetLocation);
6088
vrc = VDCreate(m->vdDiskIfaces, &targetHdd);
6089
ComAssertRCThrow(vrc, E_FAIL);
6093
/* Open all media in the target chain. */
6094
MediumLockList::Base::const_iterator targetListBegin =
6095
task.mpTargetMediumLockList->GetBegin();
6096
MediumLockList::Base::const_iterator targetListEnd =
6097
task.mpTargetMediumLockList->GetEnd();
6098
for (MediumLockList::Base::const_iterator it = targetListBegin;
6099
it != targetListEnd;
6102
const MediumLock &mediumLock = *it;
6103
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6105
/* If the target medium is not created yet there's no
6106
* reason to open it. */
6107
if (pMedium == pTarget && fCreatingTarget)
6110
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6113
Assert( pMedium->m->state == MediumState_LockedRead
6114
|| pMedium->m->state == MediumState_LockedWrite);
6116
unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
6117
if (pMedium->m->state != MediumState_LockedWrite)
6118
uOpenFlags = VD_OPEN_FLAGS_READONLY;
6119
if (pMedium->m->type == MediumType_Shareable)
6120
uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6122
/* Open all media in appropriate mode. */
6123
vrc = VDOpen(targetHdd,
6124
pMedium->m->strFormat.c_str(),
6125
pMedium->m->strLocationFull.c_str(),
6127
pMedium->m->vdDiskIfaces);
6128
if (RT_FAILURE(vrc))
6129
throw setError(E_FAIL,
6130
tr("Could not open the medium storage unit '%s'%s"),
6131
pMedium->m->strLocationFull.raw(),
6132
vdError(vrc).raw());
6135
/** @todo r=klaus target isn't locked, race getting the state */
6139
targetFormat.c_str(),
6140
(fCreatingTarget) ? targetLocation.raw() : (char *)NULL,
6146
pTarget->m->vdDiskIfaces,
6147
task.mVDOperationIfaces);
6148
if (RT_FAILURE(vrc))
6149
throw setError(E_FAIL,
6150
tr("Could not create the clone medium '%s'%s"),
6151
targetLocation.raw(), vdError(vrc).raw());
6153
size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
6154
logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE) / _1M;
6155
unsigned uImageFlags;
6156
vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
6157
if (RT_SUCCESS(vrc))
6158
variant = (MediumVariant_T)uImageFlags;
6160
catch (HRESULT aRC) { rc = aRC; }
6162
VDDestroy(targetHdd);
6164
catch (HRESULT aRC) { rc = aRC; }
6168
catch (HRESULT aRC) { rc = aRC; }
6170
/* Only do the parent changes for newly created media. */
6171
if (SUCCEEDED(rc) && fCreatingTarget)
6173
/* we set mParent & children() */
6174
AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6176
Assert(pTarget->m->pParent.isNull());
6180
/* associate the clone with the parent and deassociate
6181
* from VirtualBox */
6182
pTarget->m->pParent = pParent;
6183
pParent->m->llChildren.push_back(pTarget);
6185
/* register with mVirtualBox as the last step and move to
6186
* Created state only on success (leaving an orphan file is
6187
* better than breaking media registry consistency) */
6188
rc = pParent->m->pVirtualBox->registerHardDisk(pTarget, NULL /* pfNeedsSaveSettings */);
6191
/* break parent association on failure to register */
6192
pTarget->deparent(); // removes target from parent
6197
rc = m->pVirtualBox->registerHardDisk(pTarget, NULL /* pfNeedsSaveSettings */);
6201
if (fCreatingTarget)
6203
AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
6207
pTarget->m->state = MediumState_Created;
6209
pTarget->m->size = size;
6210
pTarget->m->logicalSize = logicalSize;
6211
pTarget->m->variant = variant;
6215
/* back to NotCreated on failure */
6216
pTarget->m->state = MediumState_NotCreated;
6218
/* reset UUID to prevent it from being reused next time */
6220
unconst(pTarget->m->id).clear();
6224
// now, at the end of this task (always asynchronous), save the settings
6226
AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
6227
m->pVirtualBox->saveSettings();
6230
/* Everything is explicitly unlocked when the task exits,
6231
* as the task destruction also destroys the source chain. */
6233
/* Make sure the source chain is released early. It could happen
6234
* that we get a deadlock in Appliance::Import when Medium::Close
6235
* is called & the source chain is released at the same time. */
6236
task.mpSourceMediumLockList->Clear();
6242
* Implementation code for the "delete" task.
6244
* This task always gets started from Medium::deleteStorage() and can run
6245
* synchronously or asynchrously depending on the "wait" parameter passed to
6251
HRESULT Medium::taskDeleteHandler(Medium::DeleteTask &task)
6258
/* The lock is also used as a signal from the task initiator (which
6259
* releases it only after RTThreadCreate()) that we can start the job */
6260
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6263
int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6264
ComAssertRCThrow(vrc, E_FAIL);
6266
Utf8Str format(m->strFormat);
6267
Utf8Str location(m->strLocationFull);
6269
/* unlock before the potentially lengthy operation */
6270
Assert(m->state == MediumState_Deleting);
6278
VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
6280
if (RT_SUCCESS(vrc))
6281
vrc = VDClose(hdd, true /* fDelete */);
6283
if (RT_FAILURE(vrc))
6284
throw setError(E_FAIL,
6285
tr("Could not delete the medium storage unit '%s'%s"),
6286
location.raw(), vdError(vrc).raw());
6289
catch (HRESULT aRC) { rc = aRC; }
6293
catch (HRESULT aRC) { rc = aRC; }
6295
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6297
/* go to the NotCreated state even on failure since the storage
6298
* may have been already partially deleted and cannot be used any
6299
* more. One will be able to manually re-open the storage if really
6300
* needed to re-register it. */
6301
m->state = MediumState_NotCreated;
6303
/* Reset UUID to prevent Create* from reusing it again */
6304
unconst(m->id).clear();
6310
* Implementation code for the "reset" task.
6312
* This always gets started asynchronously from Medium::Reset().
6317
HRESULT Medium::taskResetHandler(Medium::ResetTask &task)
6321
uint64_t size = 0, logicalSize = 0;
6322
MediumVariant_T variant = MediumVariant_Standard;
6326
/* The lock is also used as a signal from the task initiator (which
6327
* releases it only after RTThreadCreate()) that we can start the job */
6328
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6330
/// @todo Below we use a pair of delete/create operations to reset
6331
/// the diff contents but the most efficient way will of course be
6332
/// to add a VDResetDiff() API call
6335
int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6336
ComAssertRCThrow(vrc, E_FAIL);
6339
Utf8Str format(m->strFormat);
6340
Utf8Str location(m->strLocationFull);
6342
Medium *pParent = m->pParent;
6343
Guid parentId = pParent->m->id;
6344
Utf8Str parentFormat(pParent->m->strFormat);
6345
Utf8Str parentLocation(pParent->m->strLocationFull);
6347
Assert(m->state == MediumState_LockedWrite);
6349
/* unlock before the potentially lengthy operation */
6354
/* Open all media in the target chain but the last. */
6355
MediumLockList::Base::const_iterator targetListBegin =
6356
task.mpMediumLockList->GetBegin();
6357
MediumLockList::Base::const_iterator targetListEnd =
6358
task.mpMediumLockList->GetEnd();
6359
for (MediumLockList::Base::const_iterator it = targetListBegin;
6360
it != targetListEnd;
6363
const MediumLock &mediumLock = *it;
6364
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6366
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6368
/* sanity check, "this" is checked above */
6369
Assert( pMedium == this
6370
|| pMedium->m->state == MediumState_LockedRead);
6372
/* Open all media in appropriate mode. */
6374
pMedium->m->strFormat.c_str(),
6375
pMedium->m->strLocationFull.c_str(),
6376
VD_OPEN_FLAGS_READONLY,
6377
pMedium->m->vdDiskIfaces);
6378
if (RT_FAILURE(vrc))
6379
throw setError(E_FAIL,
6380
tr("Could not open the medium storage unit '%s'%s"),
6381
pMedium->m->strLocationFull.raw(),
6382
vdError(vrc).raw());
6384
/* Done when we hit the media which should be reset */
6385
if (pMedium == this)
6389
/* first, delete the storage unit */
6390
vrc = VDClose(hdd, true /* fDelete */);
6391
if (RT_FAILURE(vrc))
6392
throw setError(E_FAIL,
6393
tr("Could not delete the medium storage unit '%s'%s"),
6394
location.raw(), vdError(vrc).raw());
6396
/* next, create it again */
6398
parentFormat.c_str(),
6399
parentLocation.c_str(),
6400
VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
6402
if (RT_FAILURE(vrc))
6403
throw setError(E_FAIL,
6404
tr("Could not open the medium storage unit '%s'%s"),
6405
parentLocation.raw(), vdError(vrc).raw());
6407
vrc = VDCreateDiff(hdd,
6410
/// @todo use the same medium variant as before
6411
VD_IMAGE_FLAGS_NONE,
6415
VD_OPEN_FLAGS_NORMAL,
6417
task.mVDOperationIfaces);
6418
if (RT_FAILURE(vrc))
6419
throw setError(E_FAIL,
6420
tr("Could not create the differencing medium storage unit '%s'%s"),
6421
location.raw(), vdError(vrc).raw());
6423
size = VDGetFileSize(hdd, VD_LAST_IMAGE);
6424
logicalSize = VDGetSize(hdd, VD_LAST_IMAGE) / _1M;
6425
unsigned uImageFlags;
6426
vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6427
if (RT_SUCCESS(vrc))
6428
variant = (MediumVariant_T)uImageFlags;
6430
catch (HRESULT aRC) { rc = aRC; }
6434
catch (HRESULT aRC) { rc = aRC; }
6436
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6439
m->logicalSize = logicalSize;
6440
m->variant = variant;
6444
/* unlock ourselves when done */
6445
HRESULT rc2 = UnlockWrite(NULL);
6449
/* Note that in sync mode, it's the caller's responsibility to
6450
* unlock the medium. */
6456
* Implementation code for the "compact" task.
6461
HRESULT Medium::taskCompactHandler(Medium::CompactTask &task)
6465
/* Lock all in {parent,child} order. The lock is also used as a
6466
* signal from the task initiator (which releases it only after
6467
* RTThreadCreate()) that we can start the job. */
6468
AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6473
int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6474
ComAssertRCThrow(vrc, E_FAIL);
6478
/* Open all media in the chain. */
6479
MediumLockList::Base::const_iterator mediumListBegin =
6480
task.mpMediumLockList->GetBegin();
6481
MediumLockList::Base::const_iterator mediumListEnd =
6482
task.mpMediumLockList->GetEnd();
6483
MediumLockList::Base::const_iterator mediumListLast =
6486
for (MediumLockList::Base::const_iterator it = mediumListBegin;
6487
it != mediumListEnd;
6490
const MediumLock &mediumLock = *it;
6491
const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6492
AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6495
if (it == mediumListLast)
6496
Assert(pMedium->m->state == MediumState_LockedWrite);
6498
Assert(pMedium->m->state == MediumState_LockedRead);
6500
/* Open all media but last in read-only mode. Do not handle
6501
* shareable media, as compaction and sharing are mutually
6504
pMedium->m->strFormat.c_str(),
6505
pMedium->m->strLocationFull.c_str(),
6506
(it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
6507
pMedium->m->vdDiskIfaces);
6508
if (RT_FAILURE(vrc))
6509
throw setError(E_FAIL,
6510
tr("Could not open the medium storage unit '%s'%s"),
6511
pMedium->m->strLocationFull.raw(),
6512
vdError(vrc).raw());
6515
Assert(m->state == MediumState_LockedWrite);
6517
Utf8Str location(m->strLocationFull);
6519
/* unlock before the potentially lengthy operation */
6522
vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
6523
if (RT_FAILURE(vrc))
6525
if (vrc == VERR_NOT_SUPPORTED)
6526
throw setError(VBOX_E_NOT_SUPPORTED,
6527
tr("Compacting is not yet supported for medium '%s'"),
6529
else if (vrc == VERR_NOT_IMPLEMENTED)
6530
throw setError(E_NOTIMPL,
6531
tr("Compacting is not implemented, medium '%s'"),
6534
throw setError(E_FAIL,
6535
tr("Could not compact medium '%s'%s"),
6537
vdError(vrc).raw());
6540
catch (HRESULT aRC) { rc = aRC; }
6544
catch (HRESULT aRC) { rc = aRC; }
6546
/* Everything is explicitly unlocked when the task exits,
6547
* as the task destruction also destroys the media chain. */
6552
/* vi: set tabstop=4 shiftwidth=4 expandtab: */