1
/* $Id: HardDiskImpl.cpp $ */
5
* VirtualBox COM class implementation
9
* Copyright (C) 2008 Sun Microsystems, Inc.
11
* This file is part of VirtualBox Open Source Edition (OSE), as
12
* available from http://www.virtualbox.org. This file is free software;
13
* you can redistribute it and/or modify it under the terms of the GNU
14
* General Public License (GPL) as published by the Free Software
15
* Foundation, in version 2 as it comes in the "COPYING" file of the
16
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20
* Clara, CA 95054 USA or visit http://www.sun.com if you need
21
* additional information or have any questions.
24
#include "HardDiskImpl.h"
26
#include "ProgressImpl.h"
27
#include "SystemPropertiesImpl.h"
31
#include <VBox/com/array.h>
32
#include <VBox/com/SupportErrorInfo.h>
35
#include <VBox/settings.h>
37
#include <iprt/param.h>
38
#include <iprt/path.h>
39
#include <iprt/file.h>
45
////////////////////////////////////////////////////////////////////////////////
47
////////////////////////////////////////////////////////////////////////////////
50
* Asynchronous task thread parameter bucket.
52
* Note that instances of this class must be created using new() because the
53
* task thread function will delete them when the task is complete!
55
* @note The constructor of this class adds a caller on the managed HardDisk
56
* object which is automatically released upon destruction.
58
struct HardDisk::Task : public com::SupportErrorInfoBase
60
enum Operation { CreateBase, CreateDiff,
61
Merge, Clone, Delete, Reset, Compact };
64
VirtualBoxBaseProto::AutoCaller autoCaller;
66
ComObjPtr <Progress> progress;
69
/** Where to save the result when executed using #runNow(). */
72
Task (HardDisk *aThat, Progress *aProgress, Operation aOperation)
73
: that (aThat), autoCaller (aThat)
74
, progress (aProgress)
75
, operation (aOperation)
80
void setData (HardDisk *aTarget)
83
HRESULT rc = d.target->addCaller();
87
void setData (HardDisk *aTarget, HardDisk *aParent)
90
HRESULT rc = d.target->addCaller();
92
d.parentDisk = aParent;
95
rc = d.parentDisk->addCaller();
100
void setData (MergeChain *aChain)
102
AssertReturnVoid (aChain != NULL);
103
d.chain.reset (aChain);
106
void setData (ImageChain *aSrcChain, ImageChain *aParentChain)
108
AssertReturnVoid (aSrcChain != NULL);
109
AssertReturnVoid (aParentChain != NULL);
110
d.source.reset (aSrcChain);
111
d.parent.reset (aParentChain);
114
void setData (ImageChain *aImgChain)
116
AssertReturnVoid (aImgChain != NULL);
117
d.images.reset (aImgChain);
120
HRESULT startThread();
131
/* CreateBase, CreateDiff, Clone */
133
HardDiskVariant_T variant;
135
/* CreateDiff, Clone */
137
ComObjPtr<HardDisk> target;
141
/** Hard disks to open, in {parent,child} order */
142
std::auto_ptr <ImageChain> source;
143
/** Hard disks which are parent of target, in {parent,child} order */
144
std::auto_ptr <ImageChain> parent;
145
/** The to-be parent hard disk object */
146
ComObjPtr<HardDisk> parentDisk;
150
/** Hard disks to merge, in {parent,child} order */
151
std::auto_ptr <MergeChain> chain;
155
/** Hard disks to open, in {parent,child} order */
156
std::auto_ptr <ImageChain> images;
162
// SupportErrorInfoBase interface
163
const GUID &mainInterfaceID() const { return COM_IIDOF (IHardDisk); }
164
const char *componentName() const { return HardDisk::ComponentName(); }
167
HardDisk::Task::~Task()
169
/* remove callers added by setData() */
170
if (!d.target.isNull())
171
d.target->releaseCaller();
175
* Starts a new thread driven by the HardDisk::taskThread() function and passes
176
* this Task instance as an argument.
178
* Note that if this method returns success, this Task object becomes an ownee
179
* of the started thread and will be automatically deleted when the thread
182
* @note When the task is executed by this method, IProgress::notifyComplete()
183
* is automatically called for the progress object associated with this
184
* task when the task is finished to signal the operation completion for
185
* other threads asynchronously waiting for it.
187
HRESULT HardDisk::Task::startThread()
189
int vrc = RTThreadCreate (NULL, HardDisk::taskThread, this,
190
0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
192
ComAssertMsgRCRet (vrc,
193
("Could not create HardDisk::Task thread (%Rrc)\n", vrc), E_FAIL);
199
* Runs HardDisk::taskThread() by passing it this Task instance as an argument
200
* on the current thread instead of creating a new one.
202
* This call implies that it is made on another temporary thread created for
203
* some asynchronous task. Avoid calling it from a normal thread since the task
204
* operatinos are potentially lengthy and will block the calling thread in this
207
* Note that this Task object will be deleted by taskThread() when this method
210
* @note When the task is executed by this method, IProgress::notifyComplete()
211
* is not called for the progress object associated with this task when
212
* the task is finished. Instead, the result of the operation is returned
213
* by this method directly and it's the caller's responsibility to
214
* complete the progress object in this case.
216
HRESULT HardDisk::Task::runNow()
218
HardDisk::taskThread (NIL_RTTHREAD, this);
223
////////////////////////////////////////////////////////////////////////////////
226
* Helper class for merge operations.
228
* @note It is assumed that when modifying methods of this class are called,
229
* HardDisk::treeLock() is held in read mode.
231
class HardDisk::MergeChain : public HardDisk::List,
232
public com::SupportErrorInfoBase
236
MergeChain (bool aForward, bool aIgnoreAttachments)
237
: mForward (aForward)
238
, mIgnoreAttachments (aIgnoreAttachments) {}
242
for (iterator it = mChildren.begin(); it != mChildren.end(); ++ it)
244
HRESULT rc = (*it)->UnlockWrite (NULL);
247
(*it)->releaseCaller();
250
for (iterator it = begin(); it != end(); ++ it)
252
AutoWriteLock alock (*it);
253
Assert ((*it)->m.state == MediaState_LockedWrite ||
254
(*it)->m.state == MediaState_Deleting);
255
if ((*it)->m.state == MediaState_LockedWrite)
256
(*it)->UnlockWrite (NULL);
258
(*it)->m.state = MediaState_Created;
260
(*it)->releaseCaller();
263
if (!mParent.isNull())
264
mParent->releaseCaller();
267
HRESULT addSource (HardDisk *aHardDisk)
269
HRESULT rc = aHardDisk->addCaller();
270
CheckComRCReturnRC (rc);
272
AutoWriteLock alock (aHardDisk);
276
rc = checkChildrenAndAttachmentsAndImmutable (aHardDisk);
279
aHardDisk->releaseCaller();
284
/* We have to fetch the state with the COM method, cause it's possible
285
that the hard disk isn't fully initialized yet. See HRESULT
286
ImageMediumBase::protectedInit (VirtualBox *aVirtualBox, const
287
settings::Key &aImageNode) for an explanation why. */
289
rc = aHardDisk->COMGETTER(State)(&m);
290
CheckComRCReturnRC (rc);
294
case MediaState_Created:
295
aHardDisk->m.state = MediaState_Deleting;
298
aHardDisk->releaseCaller();
299
return aHardDisk->setStateError();
302
push_front (aHardDisk);
306
/* we will need parent to reparent target */
307
if (!aHardDisk->mParent.isNull())
309
rc = aHardDisk->mParent->addCaller();
310
CheckComRCReturnRC (rc);
312
mParent = aHardDisk->mParent;
317
/* we will need to reparent children */
318
for (List::const_iterator it = aHardDisk->children().begin();
319
it != aHardDisk->children().end(); ++ it)
321
rc = (*it)->addCaller();
322
CheckComRCReturnRC (rc);
324
rc = (*it)->LockWrite (NULL);
327
(*it)->releaseCaller();
331
mChildren.push_back (*it);
338
HRESULT addTarget (HardDisk *aHardDisk)
340
HRESULT rc = aHardDisk->addCaller();
341
CheckComRCReturnRC (rc);
343
AutoWriteLock alock (aHardDisk);
347
rc = checkChildrenAndImmutable (aHardDisk);
350
aHardDisk->releaseCaller();
355
/* go to LockedWrite */
356
rc = aHardDisk->LockWrite (NULL);
359
aHardDisk->releaseCaller();
363
push_front (aHardDisk);
368
HRESULT addIntermediate (HardDisk *aHardDisk)
370
HRESULT rc = aHardDisk->addCaller();
371
CheckComRCReturnRC (rc);
373
AutoWriteLock alock (aHardDisk);
375
rc = checkChildrenAndAttachments (aHardDisk);
378
aHardDisk->releaseCaller();
383
switch (aHardDisk->m.state)
385
case MediaState_Created:
386
aHardDisk->m.state = MediaState_Deleting;
389
aHardDisk->releaseCaller();
390
return aHardDisk->setStateError();
393
push_front (aHardDisk);
398
bool isForward() const { return mForward; }
399
HardDisk *parent() const { return mParent; }
400
const List &children() const { return mChildren; }
402
HardDisk *source() const
403
{ AssertReturn (size() > 0, NULL); return mForward ? front() : back(); }
405
HardDisk *target() const
406
{ AssertReturn (size() > 0, NULL); return mForward ? back() : front(); }
410
// SupportErrorInfoBase interface
411
const GUID &mainInterfaceID() const { return COM_IIDOF (IHardDisk); }
412
const char *componentName() const { return HardDisk::ComponentName(); }
416
HRESULT check (HardDisk *aHardDisk, bool aChildren, bool aAttachments,
421
/* not going to multi-merge as it's too expensive */
422
if (aHardDisk->children().size() > 1)
424
return setError (E_FAIL,
425
tr ("Hard disk '%ls' involved in the merge operation has more than one child hard disk (%d)"),
426
aHardDisk->m.locationFull.raw(),
427
aHardDisk->children().size());
431
if (aAttachments && !mIgnoreAttachments)
433
if (aHardDisk->m.backRefs.size() != 0)
434
return setError (E_FAIL,
435
tr ("Hard disk '%ls' is attached to %d virtual machines"),
436
aHardDisk->m.locationFull.raw(),
437
aHardDisk->m.backRefs.size());
442
if (aHardDisk->mm.type == HardDiskType_Immutable)
443
return setError (E_FAIL,
444
tr ("Hard disk '%ls' is immutable"),
445
aHardDisk->m.locationFull.raw());
451
HRESULT checkChildren (HardDisk *aHardDisk)
452
{ return check (aHardDisk, true, false, false); }
454
HRESULT checkChildrenAndImmutable (HardDisk *aHardDisk)
455
{ return check (aHardDisk, true, false, true); }
457
HRESULT checkChildrenAndAttachments (HardDisk *aHardDisk)
458
{ return check (aHardDisk, true, true, false); }
460
HRESULT checkChildrenAndAttachmentsAndImmutable (HardDisk *aHardDisk)
461
{ return check (aHardDisk, true, true, true); }
463
/** true if forward merge, false if backward */
465
/** true to not perform attachment checks */
466
bool mIgnoreAttachments : 1;
468
/** Parent of the source when forward merge (if any) */
469
ComObjPtr <HardDisk> mParent;
470
/** Children of the source when backward merge (if any) */
474
////////////////////////////////////////////////////////////////////////////////
477
* Helper class for image operations involving the entire parent chain.
479
* @note It is assumed that when modifying methods of this class are called,
480
* HardDisk::treeLock() is held in read mode.
482
class HardDisk::ImageChain : public HardDisk::List,
483
public com::SupportErrorInfoBase
492
if (begin() != end())
494
List::const_iterator last = end();
496
for (List::const_iterator it = begin(); it != end(); ++ it)
498
AutoWriteLock alock (*it);
501
Assert ( (*it)->m.state == MediaState_LockedRead
502
|| (*it)->m.state == MediaState_LockedWrite);
503
if ((*it)->m.state == MediaState_LockedRead)
504
(*it)->UnlockRead (NULL);
505
else if ((*it)->m.state == MediaState_LockedWrite)
506
(*it)->UnlockWrite (NULL);
510
Assert ((*it)->m.state == MediaState_LockedRead);
511
if ((*it)->m.state == MediaState_LockedRead)
512
(*it)->UnlockRead (NULL);
515
(*it)->releaseCaller();
520
HRESULT addImage (HardDisk *aHardDisk)
522
HRESULT rc = aHardDisk->addCaller();
523
CheckComRCReturnRC (rc);
525
push_front (aHardDisk);
530
HRESULT lockImagesRead ()
532
/* Lock all disks in the chain in {parent, child} order,
533
* and make sure they are accessible. */
534
/// @todo code duplication with SessionMachine::lockMedia, see below
535
ErrorInfoKeeper eik (true /* aIsNull */);
536
MultiResult mrc (S_OK);
537
for (List::const_iterator it = begin(); it != end(); ++ it)
540
MediaState_T mediaState;
541
rc = (*it)->LockRead(&mediaState);
542
CheckComRCReturnRC (rc);
544
if (mediaState == MediaState_Inaccessible)
546
rc = (*it)->COMGETTER(State) (&mediaState);
547
CheckComRCReturnRC (rc);
548
Assert (mediaState == MediaState_LockedRead);
550
/* Note that we locked the medium already, so use the error
551
* value to see if there was an accessibility failure */
553
rc = (*it)->COMGETTER(LastAccessError) (error.asOutParam());
554
CheckComRCReturnRC (rc);
556
if (!error.isEmpty())
559
rc = (*it)->COMGETTER(Location) (loc.asOutParam());
560
CheckComRCThrowRC (rc);
562
/* collect multiple errors */
565
/* be in sync with MediumBase::setStateError() */
566
Assert (!error.isEmpty());
567
mrc = setError (E_FAIL,
568
tr ("Medium '%ls' is not accessible. %ls"),
569
loc.raw(), error.raw());
577
CheckComRCReturnRC ((HRESULT) mrc);
582
HRESULT lockImagesReadAndLastWrite ()
584
/* Lock all disks in the chain in {parent, child} order,
585
* and make sure they are accessible. */
586
/// @todo code duplication with SessionMachine::lockMedia, see below
587
ErrorInfoKeeper eik (true /* aIsNull */);
588
MultiResult mrc (S_OK);
589
List::const_iterator last = end();
591
for (List::const_iterator it = begin(); it != end(); ++ it)
594
MediaState_T mediaState;
596
rc = (*it)->LockWrite(&mediaState);
598
rc = (*it)->LockRead(&mediaState);
599
CheckComRCReturnRC (rc);
601
if (mediaState == MediaState_Inaccessible)
603
rc = (*it)->COMGETTER(State) (&mediaState);
604
CheckComRCReturnRC (rc);
606
Assert (mediaState == MediaState_LockedWrite);
608
Assert (mediaState == MediaState_LockedRead);
610
/* Note that we locked the medium already, so use the error
611
* value to see if there was an accessibility failure */
613
rc = (*it)->COMGETTER(LastAccessError) (error.asOutParam());
614
CheckComRCReturnRC (rc);
616
if (!error.isEmpty())
619
rc = (*it)->COMGETTER(Location) (loc.asOutParam());
620
CheckComRCThrowRC (rc);
622
/* collect multiple errors */
625
/* be in sync with MediumBase::setStateError() */
626
Assert (!error.isEmpty());
627
mrc = setError (E_FAIL,
628
tr ("Medium '%ls' is not accessible. %ls"),
629
loc.raw(), error.raw());
637
CheckComRCReturnRC ((HRESULT) mrc);
644
// SupportErrorInfoBase interface
645
const GUID &mainInterfaceID() const { return COM_IIDOF (IHardDisk); }
646
const char *componentName() const { return HardDisk::ComponentName(); }
652
////////////////////////////////////////////////////////////////////////////////
654
////////////////////////////////////////////////////////////////////////////////
656
// constructor / destructor
657
////////////////////////////////////////////////////////////////////////////////
659
DEFINE_EMPTY_CTOR_DTOR (HardDisk)
661
HRESULT HardDisk::FinalConstruct()
663
/* Initialize the callbacks of the VD error interface */
664
mm.vdIfCallsError.cbSize = sizeof (VDINTERFACEERROR);
665
mm.vdIfCallsError.enmInterface = VDINTERFACETYPE_ERROR;
666
mm.vdIfCallsError.pfnError = vdErrorCall;
668
/* Initialize the callbacks of the VD progress interface */
669
mm.vdIfCallsProgress.cbSize = sizeof (VDINTERFACEPROGRESS);
670
mm.vdIfCallsProgress.enmInterface = VDINTERFACETYPE_PROGRESS;
671
mm.vdIfCallsProgress.pfnProgress = vdProgressCall;
673
/* Initialize the callbacks of the VD config interface */
674
mm.vdIfCallsConfig.cbSize = sizeof (VDINTERFACECONFIG);
675
mm.vdIfCallsConfig.enmInterface = VDINTERFACETYPE_CONFIG;
676
mm.vdIfCallsConfig.pfnAreKeysValid = vdConfigAreKeysValid;
677
mm.vdIfCallsConfig.pfnQuerySize = vdConfigQuerySize;
678
mm.vdIfCallsConfig.pfnQuery = vdConfigQuery;
680
/* Initialize the callbacks of the VD TCP interface (we always use the host
681
* IP stack for now) */
682
mm.vdIfCallsTcpNet.cbSize = sizeof (VDINTERFACETCPNET);
683
mm.vdIfCallsTcpNet.enmInterface = VDINTERFACETYPE_TCPNET;
684
mm.vdIfCallsTcpNet.pfnClientConnect = RTTcpClientConnect;
685
mm.vdIfCallsTcpNet.pfnClientClose = RTTcpClientClose;
686
mm.vdIfCallsTcpNet.pfnSelectOne = RTTcpSelectOne;
687
mm.vdIfCallsTcpNet.pfnRead = RTTcpRead;
688
mm.vdIfCallsTcpNet.pfnWrite = RTTcpWrite;
689
mm.vdIfCallsTcpNet.pfnFlush = RTTcpFlush;
691
/* Initialize the per-disk interface chain */
693
vrc = VDInterfaceAdd (&mm.vdIfError,
694
"HardDisk::vdInterfaceError",
695
VDINTERFACETYPE_ERROR,
696
&mm.vdIfCallsError, this, &mm.vdDiskIfaces);
697
AssertRCReturn (vrc, E_FAIL);
699
vrc = VDInterfaceAdd (&mm.vdIfProgress,
700
"HardDisk::vdInterfaceProgress",
701
VDINTERFACETYPE_PROGRESS,
702
&mm.vdIfCallsProgress, this, &mm.vdDiskIfaces);
703
AssertRCReturn (vrc, E_FAIL);
705
vrc = VDInterfaceAdd (&mm.vdIfConfig,
706
"HardDisk::vdInterfaceConfig",
707
VDINTERFACETYPE_CONFIG,
708
&mm.vdIfCallsConfig, this, &mm.vdDiskIfaces);
709
AssertRCReturn (vrc, E_FAIL);
711
vrc = VDInterfaceAdd (&mm.vdIfTcpNet,
712
"HardDisk::vdInterfaceTcpNet",
713
VDINTERFACETYPE_TCPNET,
714
&mm.vdIfCallsTcpNet, this, &mm.vdDiskIfaces);
715
AssertRCReturn (vrc, E_FAIL);
720
void HardDisk::FinalRelease()
725
// public initializer/uninitializer for internal purposes only
726
////////////////////////////////////////////////////////////////////////////////
729
* Initializes the hard disk object without creating or opening an associated
732
* For hard disks that don't have the VD_CAP_CREATE_FIXED or
733
* VD_CAP_CREATE_DYNAMIC capability (and therefore cannot be created or deleted
734
* with the means of VirtualBox) the associated storage unit is assumed to be
735
* ready for use so the state of the hard disk object will be set to Created.
737
* @param aVirtualBox VirtualBox object.
738
* @param aLocation Storage unit location.
740
HRESULT HardDisk::init (VirtualBox *aVirtualBox,
744
AssertReturn (aVirtualBox != NULL, E_FAIL);
745
AssertReturn (aFormat != NULL && *aFormat != '\0', E_FAIL);
747
/* Enclose the state transition NotReady->InInit->Ready */
748
AutoInitSpan autoInitSpan (this);
749
AssertReturn (autoInitSpan.isOk(), E_FAIL);
753
/* share VirtualBox weakly (parent remains NULL so far) */
754
unconst (mVirtualBox) = aVirtualBox;
756
/* register with VirtualBox early, since uninit() will
757
* unconditionally unregister on failure */
758
aVirtualBox->addDependentChild (this);
761
m.state = MediaState_NotCreated;
763
/* No storage unit is created yet, no need to queryInfo() */
765
rc = setFormat (aFormat);
766
CheckComRCReturnRC (rc);
768
if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
770
rc = setLocation (aLocation);
771
CheckComRCReturnRC (rc);
775
rc = setLocation (aLocation);
776
CheckComRCReturnRC (rc);
778
/// @todo later we may want to use a pfnComposeLocation backend info
779
/// callback to generate a well-formed location value (based on the hard
780
/// disk properties we have) rather than allowing each caller to invent
781
/// its own (pseudo-)location.
784
if (!(mm.formatObj->capabilities() &
785
(HardDiskFormatCapabilities_CreateFixed |
786
HardDiskFormatCapabilities_CreateDynamic)))
788
/* storage for hard disks of this format can neither be explicitly
789
* created by VirtualBox nor deleted, so we place the hard disk to
790
* Created state here and also add it to the registry */
791
m.state = MediaState_Created;
792
unconst (m.id).create();
793
rc = mVirtualBox->registerHardDisk (this);
795
/// @todo later we may want to use a pfnIsConfigSufficient backend info
796
/// callback that would tell us when we have enough properties to work
797
/// with the hard disk and this information could be used to actually
798
/// move such hard disks from NotCreated to Created state. Instead of
799
/// pfnIsConfigSufficient we can use HardDiskFormat property
800
/// descriptions to see which properties are mandatory
803
/* Confirm a successful initialization when it's the case */
805
autoInitSpan.setSucceeded();
811
* Initializes the hard disk object by opening the storage unit at the specified
812
* location. The enOpenMode parameter defines whether the image will be opened
813
* read/write or read-only.
815
* Note that the UUID, format and the parent of this hard disk will be
816
* determined when reading the hard disk storage unit, unless new values are
817
* specified by the parameters. If the detected or set parent is
818
* not known to VirtualBox, then this method will fail.
820
* @param aVirtualBox VirtualBox object.
821
* @param aLocation Storage unit location.
822
* @param enOpenMode Whether to open the image read/write or read-only.
823
* @param aSetImageId Whether to set the image UUID or not.
824
* @param aImageId New image UUID if @aSetId is true. Empty string means
825
* create a new UUID, and a zero UUID is invalid.
826
* @param aSetParentId Whether to set the parent UUID or not.
827
* @param aParentId New parent UUID if @aSetParentId is true. Empty string
828
* means create a new UUID, and a zero UUID is valid.
830
HRESULT HardDisk::init(VirtualBox *aVirtualBox,
832
HDDOpenMode enOpenMode,
834
const Guid &aImageId,
836
const Guid &aParentId)
838
AssertReturn (aVirtualBox, E_INVALIDARG);
839
AssertReturn (aLocation, E_INVALIDARG);
841
/* Enclose the state transition NotReady->InInit->Ready */
842
AutoInitSpan autoInitSpan (this);
843
AssertReturn (autoInitSpan.isOk(), E_FAIL);
847
/* share VirtualBox weakly (parent remains NULL so far) */
848
unconst (mVirtualBox) = aVirtualBox;
850
/* register with VirtualBox early, since uninit() will
851
* unconditionally unregister on failure */
852
aVirtualBox->addDependentChild (this);
854
/* there must be a storage unit */
855
m.state = MediaState_Created;
857
/* remember the open mode (defaults to ReadWrite) */
858
mm.hddOpenMode = enOpenMode;
860
rc = setLocation (aLocation);
861
CheckComRCReturnRC (rc);
863
/* save the new uuid values, will be used by queryInfo() */
864
mm.setImageId = aSetImageId;
865
unconst(mm.imageId) = aImageId;
866
mm.setParentId = aSetParentId;
867
unconst(mm.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 == MediaState_Inaccessible)
878
Assert (!m.lastAccessError.isEmpty());
879
rc = setError (E_FAIL, Utf8Str (m.lastAccessError));
883
AssertReturn(!m.id.isEmpty(), E_FAIL);
885
/* storage format must be detected by queryInfo() if the medium is accessible */
886
AssertReturn(!mm.format.isNull(), E_FAIL);
890
/* Confirm a successful initialization when it's the case */
892
autoInitSpan.setSucceeded();
898
* Initializes the hard disk object by loading its data from the given settings
899
* node. In this mode, the image will always be opened read/write.
901
* @param aVirtualBox VirtualBox object.
902
* @param aParent Parent hard disk or NULL for a root (base) hard disk.
903
* @param aNode <HardDisk> settings node.
905
* @note Locks VirtualBox lock for writing, treeLock() for writing.
907
HRESULT HardDisk::init (VirtualBox *aVirtualBox,
909
const settings::Key &aNode)
911
using namespace settings;
913
AssertReturn (aVirtualBox, E_INVALIDARG);
915
/* Enclose the state transition NotReady->InInit->Ready */
916
AutoInitSpan autoInitSpan (this);
917
AssertReturn (autoInitSpan.isOk(), E_FAIL);
921
/* share VirtualBox and parent weakly */
922
unconst (mVirtualBox) = aVirtualBox;
924
/* register with VirtualBox/parent early, since uninit() will
925
* unconditionally unregister on failure */
927
aVirtualBox->addDependentChild (this);
931
AutoWriteLock treeLock (this->treeLock());
934
aParent->addDependentChild (this);
937
/* see below why we don't call queryInfo() (and therefore treat the medium
938
* as inaccessible for now */
939
m.state = MediaState_Inaccessible;
940
m.lastAccessError = tr ("Accessibility check was not yet performed");
943
unconst (m.id) = aNode.value <Guid> ("uuid");
947
settings::Key descNode = aNode.findKey ("Description");
948
if (!descNode.isNull())
949
m.description = descNode.keyStringValue();
953
Bstr format = aNode.stringValue ("format");
954
AssertReturn (!format.isNull(), E_FAIL);
955
rc = setFormat (format);
956
CheckComRCReturnRC (rc);
958
/* optional, only for diffs, default is false */
960
mm.autoReset = aNode.value <bool> ("autoReset");
962
mm.autoReset = false;
964
/* properties (after setting the format as it populates the map). Note that
965
* if some properties are not supported but preseint in the settings file,
966
* they will still be read and accessible (for possible backward
967
* compatibility; we can also clean them up from the XML upon next
968
* XML format version change if we wish) */
969
Key::List properties = aNode.keys ("Property");
970
for (Key::List::const_iterator it = properties.begin();
971
it != properties.end(); ++ it)
973
mm.properties [Bstr (it->stringValue ("name"))] =
974
Bstr (it->stringValue ("value"));
978
Bstr location = aNode.stringValue ("location");
979
rc = setLocation (location);
980
CheckComRCReturnRC (rc);
982
/* type is only for base hard disks */
983
if (mParent.isNull())
985
const char *type = aNode.stringValue ("type");
986
if (strcmp (type, "Normal") == 0)
987
mm.type = HardDiskType_Normal;
988
else if (strcmp (type, "Immutable") == 0)
989
mm.type = HardDiskType_Immutable;
990
else if (strcmp (type, "Writethrough") == 0)
991
mm.type = HardDiskType_Writethrough;
996
LogFlowThisFunc (("m.locationFull='%ls', mm.format=%ls, m.id={%RTuuid}\n",
997
m.locationFull.raw(), mm.format.raw(), m.id.raw()));
999
/* Don't call queryInfo() for registered media to prevent the calling
1000
* thread (i.e. the VirtualBox server startup thread) from an unexpected
1001
* freeze but mark it as initially inaccessible instead. The vital UUID,
1002
* location and format properties are read from the registry file above; to
1003
* get the actual state and the rest of the data, the user will have to call
1004
* COMGETTER(State). */
1006
/* load all children */
1007
Key::List hardDisks = aNode.keys ("HardDisk");
1008
for (Key::List::const_iterator it = hardDisks.begin();
1009
it != hardDisks.end(); ++ it)
1011
ComObjPtr<HardDisk> hardDisk;
1012
hardDisk.createObject();
1013
rc = hardDisk->init(aVirtualBox, this, *it);
1014
CheckComRCBreakRC (rc);
1016
rc = mVirtualBox->registerHardDisk(hardDisk, false /* aSaveRegistry */);
1017
CheckComRCBreakRC (rc);
1020
/* Confirm a successful initialization when it's the case */
1022
autoInitSpan.setSucceeded();
1028
* Uninitializes the instance.
1030
* Called either from FinalRelease() or by the parent when it gets destroyed.
1032
* @note All children of this hard disk get uninitialized by calling their
1035
* @note Locks treeLock() for writing, VirtualBox for writing.
1037
void HardDisk::uninit()
1039
/* Enclose the state transition Ready->InUninit->NotReady */
1040
AutoUninitSpan autoUninitSpan (this);
1041
if (autoUninitSpan.uninitDone())
1044
if (!mm.formatObj.isNull())
1046
/* remove the caller reference we added in setFormat() */
1047
mm.formatObj->releaseCaller();
1048
mm.formatObj.setNull();
1051
if (m.state == MediaState_Deleting)
1053
/* we are being uninitialized after've been deleted by merge.
1054
* Reparenting has already been done so don't touch it here (we are
1055
* now orphans and remoeDependentChild() will assert) */
1057
Assert (mParent.isNull());
1061
/* we uninit children and reset mParent
1062
* and VirtualBox::removeDependentChild() needs a write lock */
1063
AutoMultiWriteLock2 alock (mVirtualBox->lockHandle(), this->treeLock());
1065
uninitDependentChildren();
1067
if (!mParent.isNull())
1069
mParent->removeDependentChild (this);
1073
mVirtualBox->removeDependentChild (this);
1076
unconst (mVirtualBox).setNull();
1079
// IHardDisk properties
1080
////////////////////////////////////////////////////////////////////////////////
1082
STDMETHODIMP HardDisk::COMGETTER(Format) (BSTR *aFormat)
1084
if (aFormat == NULL)
1087
AutoCaller autoCaller (this);
1088
CheckComRCReturnRC (autoCaller.rc());
1090
/* no need to lock, mm.format is const */
1091
mm.format.cloneTo (aFormat);
1096
STDMETHODIMP HardDisk::COMGETTER(Type) (HardDiskType_T *aType)
1101
AutoCaller autoCaller (this);
1102
CheckComRCReturnRC (autoCaller.rc());
1104
AutoReadLock alock (this);
1111
STDMETHODIMP HardDisk::COMSETTER(Type) (HardDiskType_T aType)
1113
AutoCaller autoCaller (this);
1114
CheckComRCReturnRC (autoCaller.rc());
1116
/* VirtualBox::saveSettings() needs a write lock */
1117
AutoMultiWriteLock2 alock (mVirtualBox, this);
1121
case MediaState_Created:
1122
case MediaState_Inaccessible:
1125
return setStateError();
1128
if (mm.type == aType)
1134
/* we access mParent & children() */
1135
AutoReadLock treeLock (this->treeLock());
1137
/* cannot change the type of a differencing hard disk */
1138
if (!mParent.isNull())
1139
return setError (E_FAIL,
1140
tr ("Hard disk '%ls' is a differencing hard disk"),
1141
m.locationFull.raw());
1143
/* cannot change the type of a hard disk being in use */
1144
if (m.backRefs.size() != 0)
1145
return setError (E_FAIL,
1146
tr ("Hard disk '%ls' is attached to %d virtual machines"),
1147
m.locationFull.raw(), m.backRefs.size());
1151
case HardDiskType_Normal:
1152
case HardDiskType_Immutable:
1154
/* normal can be easily converted to imutable and vice versa even
1155
* if they have children as long as they are not attached to any
1156
* machine themselves */
1159
case HardDiskType_Writethrough:
1161
/* cannot change to writethrough if there are children */
1162
if (children().size() != 0)
1163
return setError (E_FAIL,
1164
tr ("Hard disk '%ls' has %d child hard disks"),
1169
AssertFailedReturn (E_FAIL);
1174
HRESULT rc = mVirtualBox->saveSettings();
1179
STDMETHODIMP HardDisk::COMGETTER(Parent) (IHardDisk **aParent)
1181
if (aParent == NULL)
1184
AutoCaller autoCaller (this);
1185
CheckComRCReturnRC (autoCaller.rc());
1187
/* we access mParent */
1188
AutoReadLock treeLock (this->treeLock());
1190
mParent.queryInterfaceTo (aParent);
1195
STDMETHODIMP HardDisk::COMGETTER(Children) (ComSafeArrayOut (IHardDisk *, aChildren))
1197
if (ComSafeArrayOutIsNull (aChildren))
1200
AutoCaller autoCaller (this);
1201
CheckComRCReturnRC (autoCaller.rc());
1203
/* we access children */
1204
AutoReadLock treeLock (this->treeLock());
1206
SafeIfaceArray<IHardDisk> children (this->children());
1207
children.detachTo (ComSafeArrayOutArg (aChildren));
1212
STDMETHODIMP HardDisk::COMGETTER(Root)(IHardDisk **aRoot)
1217
/* root() will do callers/locking */
1219
root().queryInterfaceTo (aRoot);
1224
STDMETHODIMP HardDisk::COMGETTER(ReadOnly) (BOOL *aReadOnly)
1226
if (aReadOnly == NULL)
1229
AutoCaller autoCaller (this);
1230
CheckComRCReturnRC (autoCaller.rc());
1232
/* isRadOnly() will do locking */
1234
*aReadOnly = isReadOnly();
1239
STDMETHODIMP HardDisk::COMGETTER(LogicalSize) (ULONG64 *aLogicalSize)
1241
CheckComArgOutPointerValid (aLogicalSize);
1244
AutoCaller autoCaller (this);
1245
CheckComRCReturnRC (autoCaller.rc());
1247
AutoReadLock alock (this);
1249
/* we access mParent */
1250
AutoReadLock treeLock (this->treeLock());
1252
if (mParent.isNull())
1254
*aLogicalSize = mm.logicalSize;
1260
/* We assume that some backend may decide to return a meaningless value in
1261
* response to VDGetSize() for differencing hard disks and therefore
1262
* always ask the base hard disk ourselves. */
1264
/* root() will do callers/locking */
1266
return root()->COMGETTER (LogicalSize) (aLogicalSize);
1269
STDMETHODIMP HardDisk::COMGETTER(AutoReset) (BOOL *aAutoReset)
1271
CheckComArgOutPointerValid (aAutoReset);
1273
AutoCaller autoCaller (this);
1274
CheckComRCReturnRC (autoCaller.rc());
1276
AutoReadLock alock (this);
1278
if (mParent.isNull())
1279
*aAutoReset = FALSE;
1281
*aAutoReset = mm.autoReset;
1286
STDMETHODIMP HardDisk::COMSETTER(AutoReset) (BOOL aAutoReset)
1288
AutoCaller autoCaller (this);
1289
CheckComRCReturnRC (autoCaller.rc());
1291
/* VirtualBox::saveSettings() needs a write lock */
1292
AutoMultiWriteLock2 alock (mVirtualBox, this);
1294
if (mParent.isNull())
1295
return setError (VBOX_E_NOT_SUPPORTED,
1296
tr ("Hard disk '%ls' is not differencing"),
1297
m.locationFull.raw());
1299
if (mm.autoReset != aAutoReset)
1301
mm.autoReset = aAutoReset;
1303
return mVirtualBox->saveSettings();
1309
// IHardDisk methods
1310
////////////////////////////////////////////////////////////////////////////////
1312
STDMETHODIMP HardDisk::GetProperty (IN_BSTR aName, BSTR *aValue)
1314
CheckComArgStrNotEmptyOrNull (aName);
1315
CheckComArgOutPointerValid (aValue);
1317
AutoCaller autoCaller (this);
1318
CheckComRCReturnRC (autoCaller.rc());
1320
AutoReadLock alock (this);
1322
Data::PropertyMap::const_iterator it = mm.properties.find (Bstr (aName));
1323
if (it == mm.properties.end())
1324
return setError (VBOX_E_OBJECT_NOT_FOUND,
1325
tr ("Property '%ls' does not exist"), aName);
1327
if (it->second.isEmpty())
1328
Bstr("").cloneTo (aValue);
1330
it->second.cloneTo (aValue);
1335
STDMETHODIMP HardDisk::SetProperty (IN_BSTR aName, IN_BSTR aValue)
1337
CheckComArgStrNotEmptyOrNull (aName);
1339
AutoCaller autoCaller (this);
1340
CheckComRCReturnRC (autoCaller.rc());
1342
/* VirtualBox::saveSettings() needs a write lock */
1343
AutoMultiWriteLock2 alock (mVirtualBox, this);
1347
case MediaState_Created:
1348
case MediaState_Inaccessible:
1351
return setStateError();
1354
Data::PropertyMap::iterator it = mm.properties.find (Bstr (aName));
1355
if (it == mm.properties.end())
1356
return setError (VBOX_E_OBJECT_NOT_FOUND,
1357
tr ("Property '%ls' does not exist"), aName);
1359
if (aValue && !*aValue)
1360
it->second = (const char *)NULL;
1362
it->second = aValue;
1364
HRESULT rc = mVirtualBox->saveSettings();
1369
STDMETHODIMP HardDisk::GetProperties(IN_BSTR aNames,
1370
ComSafeArrayOut (BSTR, aReturnNames),
1371
ComSafeArrayOut (BSTR, aReturnValues))
1373
CheckComArgOutSafeArrayPointerValid (aReturnNames);
1374
CheckComArgOutSafeArrayPointerValid (aReturnValues);
1376
AutoCaller autoCaller (this);
1377
CheckComRCReturnRC (autoCaller.rc());
1379
AutoReadLock alock (this);
1381
/// @todo make use of aNames according to the documentation
1384
com::SafeArray <BSTR> names (mm.properties.size());
1385
com::SafeArray <BSTR> values (mm.properties.size());
1388
for (Data::PropertyMap::const_iterator it = mm.properties.begin();
1389
it != mm.properties.end(); ++ it)
1391
it->first.cloneTo (&names [i]);
1392
if (it->second.isEmpty())
1393
Bstr("").cloneTo(&values [i]);
1395
it->second.cloneTo (&values [i]);
1399
names.detachTo (ComSafeArrayOutArg (aReturnNames));
1400
values.detachTo (ComSafeArrayOutArg (aReturnValues));
1405
STDMETHODIMP HardDisk::SetProperties(ComSafeArrayIn (IN_BSTR, aNames),
1406
ComSafeArrayIn (IN_BSTR, aValues))
1408
CheckComArgSafeArrayNotNull (aNames);
1409
CheckComArgSafeArrayNotNull (aValues);
1411
AutoCaller autoCaller (this);
1412
CheckComRCReturnRC (autoCaller.rc());
1414
/* VirtualBox::saveSettings() needs a write lock */
1415
AutoMultiWriteLock2 alock (mVirtualBox, this);
1417
com::SafeArray <IN_BSTR> names (ComSafeArrayInArg (aNames));
1418
com::SafeArray <IN_BSTR> values (ComSafeArrayInArg (aValues));
1420
/* first pass: validate names */
1421
for (size_t i = 0; i < names.size(); ++ i)
1423
if (mm.properties.find (Bstr (names [i])) == mm.properties.end())
1424
return setError (VBOX_E_OBJECT_NOT_FOUND,
1425
tr ("Property '%ls' does not exist"), names [i]);
1428
/* second pass: assign */
1429
for (size_t i = 0; i < names.size(); ++ i)
1431
Data::PropertyMap::iterator it = mm.properties.find (Bstr (names [i]));
1432
AssertReturn (it != mm.properties.end(), E_FAIL);
1434
if (values[i] && !*values[i])
1435
it->second = (const char *)NULL;
1437
it->second = values [i];
1440
HRESULT rc = mVirtualBox->saveSettings();
1445
STDMETHODIMP HardDisk::CreateBaseStorage(ULONG64 aLogicalSize,
1446
HardDiskVariant_T aVariant,
1447
IProgress **aProgress)
1449
CheckComArgOutPointerValid (aProgress);
1451
AutoCaller autoCaller (this);
1452
CheckComRCReturnRC (autoCaller.rc());
1454
AutoWriteLock alock (this);
1456
aVariant = (HardDiskVariant_T)((unsigned)aVariant & (unsigned)~HardDiskVariant_Diff);
1457
if ( !(aVariant & HardDiskVariant_Fixed)
1458
&& !(mm.formatObj->capabilities() & HardDiskFormatCapabilities_CreateDynamic))
1459
return setError (VBOX_E_NOT_SUPPORTED,
1460
tr ("Hard disk format '%ls' does not support dynamic storage creation"),
1462
if ( (aVariant & HardDiskVariant_Fixed)
1463
&& !(mm.formatObj->capabilities() & HardDiskFormatCapabilities_CreateDynamic))
1464
return setError (VBOX_E_NOT_SUPPORTED,
1465
tr ("Hard disk format '%ls' does not support fixed storage creation"),
1470
case MediaState_NotCreated:
1473
return setStateError();
1476
ComObjPtr <Progress> progress;
1477
progress.createObject();
1478
/// @todo include fixed/dynamic
1479
HRESULT rc = progress->init (mVirtualBox, static_cast<IHardDisk*>(this),
1480
(aVariant & HardDiskVariant_Fixed)
1481
? BstrFmt (tr ("Creating fixed hard disk storage unit '%ls'"), m.locationFull.raw())
1482
: BstrFmt (tr ("Creating dynamic hard disk storage unit '%ls'"), m.locationFull.raw()),
1483
TRUE /* aCancelable */);
1484
CheckComRCReturnRC (rc);
1486
/* setup task object and thread to carry out the operation
1489
std::auto_ptr <Task> task (new Task (this, progress, Task::CreateBase));
1490
AssertComRCReturnRC (task->autoCaller.rc());
1492
task->d.size = aLogicalSize;
1493
task->d.variant = aVariant;
1495
rc = task->startThread();
1496
CheckComRCReturnRC (rc);
1498
/* go to Creating state on success */
1499
m.state = MediaState_Creating;
1501
/* task is now owned by taskThread() so release it */
1504
/* return progress to the caller */
1505
progress.queryInterfaceTo (aProgress);
1510
STDMETHODIMP HardDisk::DeleteStorage (IProgress **aProgress)
1512
CheckComArgOutPointerValid (aProgress);
1514
AutoCaller autoCaller (this);
1515
CheckComRCReturnRC (autoCaller.rc());
1517
ComObjPtr <Progress> progress;
1519
HRESULT rc = deleteStorageNoWait (progress);
1522
/* return progress to the caller */
1523
progress.queryInterfaceTo (aProgress);
1529
STDMETHODIMP HardDisk::CreateDiffStorage (IHardDisk *aTarget,
1530
HardDiskVariant_T aVariant,
1531
IProgress **aProgress)
1533
CheckComArgNotNull (aTarget);
1534
CheckComArgOutPointerValid (aProgress);
1536
AutoCaller autoCaller (this);
1537
CheckComRCReturnRC (autoCaller.rc());
1539
ComObjPtr<HardDisk> diff;
1540
HRESULT rc = mVirtualBox->cast (aTarget, diff);
1541
CheckComRCReturnRC (rc);
1543
AutoWriteLock alock (this);
1545
if (mm.type == HardDiskType_Writethrough)
1546
return setError (E_FAIL,
1547
tr ("Hard disk '%ls' is Writethrough"),
1548
m.locationFull.raw());
1550
/* We want to be locked for reading as long as our diff child is being
1552
rc = LockRead (NULL);
1553
CheckComRCReturnRC (rc);
1555
ComObjPtr <Progress> progress;
1557
rc = createDiffStorageNoWait (diff, aVariant, progress);
1560
HRESULT rc2 = UnlockRead (NULL);
1562
/* Note: on success, taskThread() will unlock this */
1566
/* return progress to the caller */
1567
progress.queryInterfaceTo (aProgress);
1573
STDMETHODIMP HardDisk::MergeTo (IN_BSTR /* aTargetId */, IProgress ** /* aProgress */)
1575
AutoCaller autoCaller (this);
1576
CheckComRCReturnRC (autoCaller.rc());
1578
ReturnComNotImplemented();
1581
STDMETHODIMP HardDisk::CloneTo (IHardDisk *aTarget,
1582
HardDiskVariant_T aVariant,
1584
IProgress **aProgress)
1586
CheckComArgNotNull (aTarget);
1587
CheckComArgOutPointerValid (aProgress);
1589
AutoCaller autoCaller (this);
1590
CheckComRCReturnRC (autoCaller.rc());
1592
ComObjPtr <HardDisk> target;
1593
HRESULT rc = mVirtualBox->cast (aTarget, target);
1594
CheckComRCReturnRC (rc);
1595
ComObjPtr <HardDisk> parent;
1598
rc = mVirtualBox->cast (aParent, parent);
1599
CheckComRCReturnRC (rc);
1602
AutoMultiWriteLock3 alock (this, target, parent);
1604
ComObjPtr <Progress> progress;
1608
if ( target->m.state != MediaState_NotCreated
1609
&& target->m.state != MediaState_Created)
1610
throw target->setStateError();
1612
/** @todo separate out creating/locking an image chain from
1613
* SessionMachine::lockMedia and use it from here too.
1614
* logically this belongs into HardDisk functionality. */
1616
/* Build the source chain and lock images in the proper order. */
1617
std::auto_ptr <ImageChain> srcChain (new ImageChain ());
1619
/* we walk the source tree */
1620
AutoReadLock srcTreeLock (this->treeLock());
1621
for (HardDisk *hd = this; hd; hd = hd->mParent)
1623
rc = srcChain->addImage(hd);
1624
CheckComRCThrowRC (rc);
1626
rc = srcChain->lockImagesRead();
1627
CheckComRCThrowRC (rc);
1629
/* Build the parent chain and lock images in the proper order. */
1630
std::auto_ptr <ImageChain> parentChain (new ImageChain ());
1632
/* we walk the future parent tree */
1633
AutoReadLock parentTreeLock;
1635
parentTreeLock.attach(parent->treeLock());
1636
for (HardDisk *hd = parent; hd; hd = hd->mParent)
1638
rc = parentChain->addImage(hd);
1639
CheckComRCThrowRC (rc);
1641
if (target->m.state == MediaState_Created)
1643
/* If we're cloning to an existing image the parent chain also
1644
* contains the target image, and it gets locked for writing. */
1645
rc = parentChain->addImage(target);
1646
CheckComRCThrowRC (rc);
1647
rc = parentChain->lockImagesReadAndLastWrite();
1648
CheckComRCThrowRC (rc);
1652
rc = parentChain->lockImagesRead();
1653
CheckComRCThrowRC (rc);
1656
progress.createObject();
1657
rc = progress->init (mVirtualBox, static_cast <IHardDisk *> (this),
1658
BstrFmt (tr ("Creating clone hard disk '%ls'"),
1659
target->m.locationFull.raw()),
1660
TRUE /* aCancelable */);
1661
CheckComRCThrowRC (rc);
1663
/* setup task object and thread to carry out the operation
1666
std::auto_ptr <Task> task (new Task (this, progress, Task::Clone));
1667
AssertComRCThrowRC (task->autoCaller.rc());
1669
task->setData (target, parent);
1670
task->d.variant = aVariant;
1671
task->setData (srcChain.release(), parentChain.release());
1673
rc = task->startThread();
1674
CheckComRCThrowRC (rc);
1676
if (target->m.state == MediaState_NotCreated)
1678
/* go to Creating state before leaving the lock */
1679
target->m.state = MediaState_Creating;
1682
/* task is now owned (or already deleted) by taskThread() so release it */
1692
/* return progress to the caller */
1693
progress.queryInterfaceTo (aProgress);
1699
STDMETHODIMP HardDisk::Compact (IProgress **aProgress)
1701
CheckComArgOutPointerValid (aProgress);
1703
AutoCaller autoCaller (this);
1704
CheckComRCReturnRC (autoCaller.rc());
1706
AutoWriteLock alock (this);
1708
ComObjPtr <Progress> progress;
1714
/** @todo separate out creating/locking an image chain from
1715
* SessionMachine::lockMedia and use it from here too.
1716
* logically this belongs into HardDisk functionality. */
1718
/* Build the image chain and lock images in the proper order. */
1719
std::auto_ptr <ImageChain> imgChain (new ImageChain ());
1721
/* we walk the image tree */
1722
AutoReadLock srcTreeLock (this->treeLock());
1723
for (HardDisk *hd = this; hd; hd = hd->mParent)
1725
rc = imgChain->addImage(hd);
1726
CheckComRCThrowRC (rc);
1728
rc = imgChain->lockImagesReadAndLastWrite();
1729
CheckComRCThrowRC (rc);
1731
progress.createObject();
1732
rc = progress->init (mVirtualBox, static_cast <IHardDisk *> (this),
1733
BstrFmt (tr ("Compacting hard disk '%ls'"), m.locationFull.raw()),
1734
TRUE /* aCancelable */);
1735
CheckComRCThrowRC (rc);
1737
/* setup task object and thread to carry out the operation
1740
std::auto_ptr <Task> task (new Task (this, progress, Task::Compact));
1741
AssertComRCThrowRC (task->autoCaller.rc());
1743
task->setData (imgChain.release());
1745
rc = task->startThread();
1746
CheckComRCThrowRC (rc);
1748
/* task is now owned (or already deleted) by taskThread() so release it */
1758
/* return progress to the caller */
1759
progress.queryInterfaceTo (aProgress);
1765
STDMETHODIMP HardDisk::Reset (IProgress **aProgress)
1767
CheckComArgOutPointerValid (aProgress);
1769
AutoCaller autoCaller (this);
1770
CheckComRCReturnRC (autoCaller.rc());
1772
AutoWriteLock alock (this);
1774
if (mParent.isNull())
1775
return setError (VBOX_E_NOT_SUPPORTED,
1776
tr ("Hard disk '%ls' is not differencing"),
1777
m.locationFull.raw());
1779
HRESULT rc = canClose();
1780
CheckComRCReturnRC (rc);
1782
rc = LockWrite (NULL);
1783
CheckComRCReturnRC (rc);
1785
ComObjPtr <Progress> progress;
1789
progress.createObject();
1790
rc = progress->init (mVirtualBox, static_cast <IHardDisk *> (this),
1791
BstrFmt (tr ("Resetting differencing hard disk '%ls'"),
1792
m.locationFull.raw()),
1793
FALSE /* aCancelable */);
1794
CheckComRCThrowRC (rc);
1796
/* setup task object and thread to carry out the operation
1799
std::auto_ptr <Task> task (new Task (this, progress, Task::Reset));
1800
AssertComRCThrowRC (task->autoCaller.rc());
1802
rc = task->startThread();
1803
CheckComRCThrowRC (rc);
1805
/* task is now owned (or already deleted) by taskThread() so release it */
1815
HRESULT rc2 = UnlockWrite (NULL);
1817
/* Note: on success, taskThread() will unlock this */
1821
/* return progress to the caller */
1822
progress.queryInterfaceTo (aProgress);
1828
// public methods for internal purposes only
1829
////////////////////////////////////////////////////////////////////////////////
1832
* Checks if the given change of \a aOldPath to \a aNewPath affects the location
1833
* of this hard disk or any its child and updates the paths if necessary to
1834
* reflect the new location.
1836
* @param aOldPath Old path (full).
1837
* @param aNewPath New path (full).
1839
* @note Locks treeLock() for reading, this object and all children for writing.
1841
void HardDisk::updatePaths (const char *aOldPath, const char *aNewPath)
1843
AssertReturnVoid (aOldPath);
1844
AssertReturnVoid (aNewPath);
1846
AutoCaller autoCaller (this);
1847
AssertComRCReturnVoid (autoCaller.rc());
1849
AutoWriteLock alock (this);
1851
/* we access children() */
1852
AutoReadLock treeLock (this->treeLock());
1854
updatePath (aOldPath, aNewPath);
1856
/* update paths of all children */
1857
for (List::const_iterator it = children().begin();
1858
it != children().end();
1861
(*it)->updatePaths (aOldPath, aNewPath);
1866
* Returns the base hard disk of the hard disk chain this hard disk is part of.
1868
* The root hard disk is found by walking up the parent-child relationship axis.
1869
* If the hard disk doesn't have a parent (i.e. it's a base hard disk), it
1870
* returns itself in response to this method.
1872
* @param aLevel Where to store the number of ancestors of this hard disk
1873
* (zero for the root), may be @c NULL.
1875
* @note Locks treeLock() for reading.
1877
ComObjPtr <HardDisk> HardDisk::root (uint32_t *aLevel /*= NULL*/)
1879
ComObjPtr <HardDisk> root;
1882
AutoCaller autoCaller (this);
1883
AssertReturn (autoCaller.isOk(), root);
1885
/* we access mParent */
1886
AutoReadLock treeLock (this->treeLock());
1891
if (!mParent.isNull())
1895
AutoCaller rootCaller (root);
1896
AssertReturn (rootCaller.isOk(), root);
1898
if (root->mParent.isNull())
1901
root = root->mParent;
1913
* Returns @c true if this hard disk cannot be modified because it has
1914
* dependants (children) or is part of the snapshot. Related to the hard disk
1915
* type and posterity, not to the current media state.
1917
* @note Locks this object and treeLock() for reading.
1919
bool HardDisk::isReadOnly()
1921
AutoCaller autoCaller (this);
1922
AssertComRCReturn (autoCaller.rc(), false);
1924
AutoReadLock alock (this);
1926
/* we access children */
1927
AutoReadLock treeLock (this->treeLock());
1931
case HardDiskType_Normal:
1933
if (children().size() != 0)
1936
for (BackRefList::const_iterator it = m.backRefs.begin();
1937
it != m.backRefs.end(); ++ it)
1938
if (it->snapshotIds.size() != 0)
1943
case HardDiskType_Immutable:
1947
case HardDiskType_Writethrough:
1955
AssertFailedReturn (false);
1959
* Saves hard disk data by appending a new <HardDisk> child node to the given
1960
* parent node which can be either <HardDisks> or <HardDisk>.
1962
* @param aaParentNode Parent <HardDisks> or <HardDisk> node.
1964
* @note Locks this object, treeLock() and children for reading.
1966
HRESULT HardDisk::saveSettings (settings::Key &aParentNode)
1968
using namespace settings;
1970
AssertReturn (!aParentNode.isNull(), E_FAIL);
1972
AutoCaller autoCaller (this);
1973
CheckComRCReturnRC (autoCaller.rc());
1975
AutoReadLock alock (this);
1977
/* we access mParent */
1978
AutoReadLock treeLock (this->treeLock());
1980
Key diskNode = aParentNode.appendKey ("HardDisk");
1982
diskNode.setValue <Guid> ("uuid", m.id);
1983
/* required (note: the original locaiton, not full) */
1984
diskNode.setValue <Bstr> ("location", m.location);
1986
diskNode.setValue <Bstr> ("format", mm.format);
1987
/* optional, only for diffs, default is false */
1988
if (!mParent.isNull())
1989
diskNode.setValueOr <bool> ("autoReset", !!mm.autoReset, false);
1991
if (!m.description.isNull())
1993
Key descNode = diskNode.createKey ("Description");
1994
descNode.setKeyValue <Bstr> (m.description);
1997
/* optional properties */
1998
for (Data::PropertyMap::const_iterator it = mm.properties.begin();
1999
it != mm.properties.end(); ++ it)
2001
/* only save properties that have non-default values */
2002
if (!it->second.isNull())
2004
Key propNode = diskNode.appendKey ("Property");
2005
propNode.setValue <Bstr> ("name", it->first);
2006
propNode.setValue <Bstr> ("value", it->second);
2010
/* only for base hard disks */
2011
if (mParent.isNull())
2014
mm.type == HardDiskType_Normal ? "Normal" :
2015
mm.type == HardDiskType_Immutable ? "Immutable" :
2016
mm.type == HardDiskType_Writethrough ? "Writethrough" : NULL;
2017
Assert (type != NULL);
2018
diskNode.setStringValue ("type", type);
2021
/* save all children */
2022
for (List::const_iterator it = children().begin();
2023
it != children().end();
2026
HRESULT rc = (*it)->saveSettings (diskNode);
2027
AssertComRCReturnRC (rc);
2034
* Compares the location of this hard disk to the given location.
2036
* The comparison takes the location details into account. For example, if the
2037
* location is a file in the host's filesystem, a case insensitive comparison
2038
* will be performed for case insensitive filesystems.
2040
* @param aLocation Location to compare to (as is).
2041
* @param aResult Where to store the result of comparison: 0 if locations
2042
* are equal, 1 if this object's location is greater than
2043
* the specified location, and -1 otherwise.
2045
HRESULT HardDisk::compareLocationTo (const char *aLocation, int &aResult)
2047
AutoCaller autoCaller (this);
2048
AssertComRCReturnRC (autoCaller.rc());
2050
AutoReadLock alock (this);
2052
Utf8Str locationFull (m.locationFull);
2054
/// @todo NEWMEDIA delegate the comparison to the backend?
2056
if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
2058
Utf8Str location (aLocation);
2060
/* For locations represented by files, append the default path if
2061
* only the name is given, and then get the full path. */
2062
if (!RTPathHavePath (aLocation))
2064
AutoReadLock propsLock (mVirtualBox->systemProperties());
2065
location = Utf8StrFmt ("%ls%c%s",
2066
mVirtualBox->systemProperties()->defaultHardDiskFolder().raw(),
2067
RTPATH_DELIMITER, aLocation);
2070
int vrc = mVirtualBox->calculateFullPath (location, location);
2071
if (RT_FAILURE (vrc))
2072
return setError (E_FAIL,
2073
tr ("Invalid hard disk storage file location '%s' (%Rrc)"),
2074
location.raw(), vrc);
2076
aResult = RTPathCompare (locationFull, location);
2079
aResult = locationFull.compare (aLocation);
2085
* Returns a short version of the location attribute.
2087
* Reimplements MediumBase::name() to specially treat non-FS-path locations.
2089
* @note Must be called from under this object's read or write lock.
2091
Utf8Str HardDisk::name()
2093
/// @todo NEWMEDIA treat non-FS-paths specially! (may require to requiest
2094
/// this information from the VD backend)
2096
Utf8Str location (m.locationFull);
2098
Utf8Str name = RTPathFilename (location);
2103
* Checks that this hard disk may be discarded and performs necessary state
2106
* This method is to be called prior to calling the #discard() to perform
2107
* necessary consistency checks and place involved hard disks to appropriate
2108
* states. If #discard() is not called or fails, the state modifications
2109
* performed by this method must be undone by #cancelDiscard().
2111
* See #discard() for more info about discarding hard disks.
2113
* @param aChain Where to store the created merge chain (may return NULL
2114
* if no real merge is necessary).
2116
* @note Locks treeLock() for reading. Locks this object, aTarget and all
2117
* intermediate hard disks for writing.
2119
HRESULT HardDisk::prepareDiscard (MergeChain * &aChain)
2121
AutoCaller autoCaller (this);
2122
AssertComRCReturnRC (autoCaller.rc());
2126
AutoWriteLock alock (this);
2128
/* we access mParent & children() */
2129
AutoReadLock treeLock (this->treeLock());
2131
AssertReturn (mm.type == HardDiskType_Normal, E_FAIL);
2133
if (children().size() == 0)
2135
/* special treatment of the last hard disk in the chain: */
2137
if (mParent.isNull())
2139
/* lock only, to prevent any usage; discard() will unlock */
2140
return LockWrite (NULL);
2143
/* the differencing hard disk w/o children will be deleted, protect it
2144
* from attaching to other VMs (this is why Deleting) */
2148
case MediaState_Created:
2149
m.state = MediaState_Deleting;
2152
return setStateError();
2155
/* aChain is intentionally NULL here */
2160
/* not going multi-merge as it's too expensive */
2161
if (children().size() > 1)
2162
return setError (E_FAIL,
2163
tr ("Hard disk '%ls' has more than one child hard disk (%d)"),
2164
m.locationFull.raw(), children().size());
2166
/* this is a read-only hard disk with children; it must be associated with
2167
* exactly one snapshot (when the snapshot is being taken, none of the
2168
* current VM's hard disks may be attached to other VMs). Note that by the
2169
* time when discard() is called, there must be no any attachments at all
2170
* (the code calling prepareDiscard() should detach). */
2171
AssertReturn (m.backRefs.size() == 1 &&
2172
!m.backRefs.front().inCurState &&
2173
m.backRefs.front().snapshotIds.size() == 1, E_FAIL);
2175
ComObjPtr<HardDisk> child = children().front();
2177
/* we keep this locked, so lock the affected child to make sure the lock
2178
* order is correct when calling prepareMergeTo() */
2179
AutoWriteLock childLock (child);
2181
/* delegate the rest to the profi */
2182
if (mParent.isNull())
2184
/* base hard disk, backward merge */
2186
Assert (child->m.backRefs.size() == 1);
2187
if (child->m.backRefs.front().machineId != m.backRefs.front().machineId)
2189
/* backward merge is too tricky, we'll just detach on discard, so
2190
* lock only, to prevent any usage; discard() will only unlock
2191
* (since we return NULL in aChain) */
2192
return LockWrite (NULL);
2195
return child->prepareMergeTo (this, aChain,
2196
true /* aIgnoreAttachments */);
2201
return prepareMergeTo (child, aChain,
2202
true /* aIgnoreAttachments */);
2207
* Discards this hard disk.
2209
* Discarding the hard disk is merging its contents to its differencing child
2210
* hard disk (forward merge) or contents of its child hard disk to itself
2211
* (backward merge) if this hard disk is a base hard disk. If this hard disk is
2212
* a differencing hard disk w/o children, then it will be simply deleted.
2213
* Calling this method on a base hard disk w/o children will do nothing and
2214
* silently succeed. If this hard disk has more than one child, the method will
2215
* currently return an error (since merging in this case would be too expensive
2216
* and result in data duplication).
2218
* When the backward merge takes place (i.e. this hard disk is a target) then,
2219
* on success, this hard disk will automatically replace the differencing child
2220
* hard disk used as a source (which will then be deleted) in the attachment
2221
* this child hard disk is associated with. This will happen only if both hard
2222
* disks belong to the same machine because otherwise such a replace would be
2223
* too tricky and could be not expected by the other machine. Same relates to a
2224
* case when the child hard disk is not associated with any machine at all. When
2225
* the backward merge is not applied, the method behaves as if the base hard
2226
* disk were not attached at all -- i.e. simply detaches it from the machine but
2227
* leaves the hard disk chain intact.
2229
* This method is basically a wrapper around #mergeTo() that selects the correct
2230
* merge direction and performs additional actions as described above and.
2232
* Note that this method will not return until the merge operation is complete
2233
* (which may be quite time consuming depending on the size of the merged hard
2236
* Note that #prepareDiscard() must be called before calling this method. If
2237
* this method returns a failure, the caller must call #cancelDiscard(). On
2238
* success, #cancelDiscard() must not be called (this method will perform all
2239
* necessary steps such as resetting states of all involved hard disks and
2240
* deleting @a aChain).
2242
* @param aChain Merge chain created by #prepareDiscard() (may be NULL if
2243
* no real merge takes place).
2245
* @note Locks the hard disks from the chain for writing. Locks the machine
2246
* object when the backward merge takes place. Locks treeLock() lock for
2247
* reading or writing.
2249
HRESULT HardDisk::discard (ComObjPtr <Progress> &aProgress, MergeChain *aChain)
2251
AssertReturn (!aProgress.isNull(), E_FAIL);
2253
ComObjPtr <HardDisk> hdFrom;
2258
AutoCaller autoCaller (this);
2259
AssertComRCReturnRC (autoCaller.rc());
2261
aProgress->setNextOperation(BstrFmt(tr("Discarding hard disk '%s'"), name().raw()),
2266
AutoWriteLock alock (this);
2268
/* we access mParent & children() */
2269
AutoReadLock treeLock (this->treeLock());
2271
Assert (children().size() == 0);
2273
/* special treatment of the last hard disk in the chain: */
2275
if (mParent.isNull())
2277
rc = UnlockWrite (NULL);
2282
/* delete the differencing hard disk w/o children */
2284
Assert (m.state == MediaState_Deleting);
2286
/* go back to Created since deleteStorage() expects this state */
2287
m.state = MediaState_Created;
2291
rc = deleteStorageAndWait (&aProgress);
2295
hdFrom = aChain->source();
2297
rc = hdFrom->mergeToAndWait (aChain, &aProgress);
2303
/* mergeToAndWait() cannot uninitialize the initiator because of
2304
* possible AutoCallers on the current thread, deleteStorageAndWait()
2305
* doesn't do it either; do it ourselves */
2313
* Undoes what #prepareDiscard() did. Must be called if #discard() is not called
2314
* or fails. Frees memory occupied by @a aChain.
2316
* @param aChain Merge chain created by #prepareDiscard() (may be NULL if
2317
* no real merge takes place).
2319
* @note Locks the hard disks from the chain for writing. Locks treeLock() for
2322
void HardDisk::cancelDiscard (MergeChain *aChain)
2324
AutoCaller autoCaller (this);
2325
AssertComRCReturnVoid (autoCaller.rc());
2329
AutoWriteLock alock (this);
2331
/* we access mParent & children() */
2332
AutoReadLock treeLock (this->treeLock());
2334
Assert (children().size() == 0);
2336
/* special treatment of the last hard disk in the chain: */
2338
if (mParent.isNull())
2340
HRESULT rc = UnlockWrite (NULL);
2345
/* the differencing hard disk w/o children will be deleted, protect it
2346
* from attaching to other VMs (this is why Deleting) */
2348
Assert (m.state == MediaState_Deleting);
2349
m.state = MediaState_Created;
2354
/* delegate the rest to the profi */
2355
cancelMergeTo (aChain);
2359
* Returns a preferred format for differencing hard disks.
2361
Bstr HardDisk::preferredDiffFormat()
2365
AutoCaller autoCaller (this);
2366
AssertComRCReturn (autoCaller.rc(), format);
2368
/* mm.format is const, no need to lock */
2371
/* check that our own format supports diffs */
2372
if (!(mm.formatObj->capabilities() & HardDiskFormatCapabilities_Differencing))
2374
/* use the default format if not */
2375
AutoReadLock propsLock (mVirtualBox->systemProperties());
2376
format = mVirtualBox->systemProperties()->defaultHardDiskFormat();
2382
// protected methods
2383
////////////////////////////////////////////////////////////////////////////////
2386
* Deletes the hard disk storage unit.
2388
* If @a aProgress is not NULL but the object it points to is @c null then a new
2389
* progress object will be created and assigned to @a *aProgress on success,
2390
* otherwise the existing progress object is used. If Progress is NULL, then no
2391
* progress object is created/used at all.
2393
* When @a aWait is @c false, this method will create a thread to perform the
2394
* delete operation asynchronously and will return immediately. Otherwise, it
2395
* will perform the operation on the calling thread and will not return to the
2396
* caller until the operation is completed. Note that @a aProgress cannot be
2397
* NULL when @a aWait is @c false (this method will assert in this case).
2399
* @param aProgress Where to find/store a Progress object to track operation
2401
* @param aWait @c true if this method should block instead of creating
2402
* an asynchronous thread.
2404
* @note Locks mVirtualBox and this object for writing. Locks treeLock() for
2407
HRESULT HardDisk::deleteStorage (ComObjPtr <Progress> *aProgress, bool aWait)
2409
AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
2411
/* unregisterWithVirtualBox() needs a write lock. We want to unregister
2412
* ourselves atomically after detecting that deletion is possible to make
2413
* sure that we don't do that after another thread has done
2414
* VirtualBox::findHardDisk() but before it starts using us (provided that
2415
* it holds a mVirtualBox lock too of course). */
2417
AutoWriteLock vboxLock (mVirtualBox);
2419
AutoWriteLock alock (this);
2421
if (!(mm.formatObj->capabilities() &
2422
(HardDiskFormatCapabilities_CreateDynamic |
2423
HardDiskFormatCapabilities_CreateFixed)))
2424
return setError (VBOX_E_NOT_SUPPORTED,
2425
tr ("Hard disk format '%ls' does not support storage deletion"),
2428
/* Note that we are fine with Inaccessible state too: a) for symmetry with
2429
* create calls and b) because it doesn't really harm to try, if it is
2430
* really inaccessibke, the delete operation will fail anyway. Accepting
2431
* Inaccessible state is especially important because all registered hard
2432
* disks are initially Inaccessible upon VBoxSVC startup until
2433
* COMGETTER(State) is called. */
2437
case MediaState_Created:
2438
case MediaState_Inaccessible:
2441
return setStateError();
2444
if (m.backRefs.size() != 0)
2445
return setError (VBOX_E_OBJECT_IN_USE,
2446
tr ("Hard disk '%ls' is attached to %d virtual machines"),
2447
m.locationFull.raw(), m.backRefs.size());
2449
HRESULT rc = canClose();
2450
CheckComRCReturnRC (rc);
2452
/* go to Deleting state before leaving the lock */
2453
m.state = MediaState_Deleting;
2455
/* we need to leave this object's write lock now because of
2456
* unregisterWithVirtualBox() that locks treeLock() for writing */
2459
/* try to remove from the list of known hard disks before performing actual
2460
* deletion (we favor the consistency of the media registry in the first
2461
* place which would have been broken if unregisterWithVirtualBox() failed
2462
* after we successfully deleted the storage) */
2464
rc = unregisterWithVirtualBox();
2468
/* restore the state because we may fail below; we will set it later again*/
2469
m.state = MediaState_Created;
2471
CheckComRCReturnRC (rc);
2473
ComObjPtr <Progress> progress;
2475
if (aProgress != NULL)
2477
/* use the existing progress object... */
2478
progress = *aProgress;
2480
/* ...but create a new one if it is null */
2481
if (progress.isNull())
2483
progress.createObject();
2484
rc = progress->init (mVirtualBox, static_cast<IHardDisk*>(this),
2485
BstrFmt (tr ("Deleting hard disk storage unit '%ls'"),
2486
m.locationFull.raw()),
2487
FALSE /* aCancelable */);
2488
CheckComRCReturnRC (rc);
2492
std::auto_ptr <Task> task (new Task (this, progress, Task::Delete));
2493
AssertComRCReturnRC (task->autoCaller.rc());
2497
/* go to Deleting state before starting the task */
2498
m.state = MediaState_Deleting;
2500
rc = task->runNow();
2504
rc = task->startThread();
2505
CheckComRCReturnRC (rc);
2507
/* go to Deleting state before leaving the lock */
2508
m.state = MediaState_Deleting;
2511
/* task is now owned (or already deleted) by taskThread() so release it */
2514
if (aProgress != NULL)
2516
/* return progress to the caller */
2517
*aProgress = progress;
2524
* Creates a new differencing storage unit using the given target hard disk's
2525
* format and the location. Note that @c aTarget must be NotCreated.
2527
* As opposed to the CreateDiffStorage() method, this method doesn't try to lock
2528
* this hard disk for reading assuming that the caller has already done so. This
2529
* is used when taking an online snaopshot (where all original hard disks are
2530
* locked for writing and must remain such). Note however that if @a aWait is
2531
* @c false and this method returns a success then the thread started by
2532
* this method will unlock the hard disk (unless it is in
2533
* MediaState_LockedWrite state) so make sure the hard disk is either in
2534
* MediaState_LockedWrite or call #LockRead() before calling this method! If @a
2535
* aWait is @c true then this method neither locks nor unlocks the hard disk, so
2536
* make sure you do it yourself as needed.
2538
* If @a aProgress is not NULL but the object it points to is @c null then a new
2539
* progress object will be created and assigned to @a *aProgress on success,
2540
* otherwise the existing progress object is used. If @a aProgress is NULL, then no
2541
* progress object is created/used at all.
2543
* When @a aWait is @c false, this method will create a thread to perform the
2544
* create operation asynchronously and will return immediately. Otherwise, it
2545
* will perform the operation on the calling thread and will not return to the
2546
* caller until the operation is completed. Note that @a aProgress cannot be
2547
* NULL when @a aWait is @c false (this method will assert in this case).
2549
* @param aTarget Target hard disk.
2550
* @param aVariant Precise image variant to create.
2551
* @param aProgress Where to find/store a Progress object to track operation
2553
* @param aWait @c true if this method should block instead of creating
2554
* an asynchronous thread.
2556
* @note Locks this object and @a aTarget for writing.
2558
HRESULT HardDisk::createDiffStorage(ComObjPtr<HardDisk> &aTarget,
2559
HardDiskVariant_T aVariant,
2560
ComObjPtr<Progress> *aProgress,
2563
AssertReturn (!aTarget.isNull(), E_FAIL);
2564
AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
2566
AutoCaller autoCaller (this);
2567
CheckComRCReturnRC (autoCaller.rc());
2569
AutoCaller targetCaller (aTarget);
2570
CheckComRCReturnRC (targetCaller.rc());
2572
AutoMultiWriteLock2 alock (this, aTarget);
2574
AssertReturn (mm.type != HardDiskType_Writethrough, E_FAIL);
2576
/* Note: MediaState_LockedWrite is ok when taking an online snapshot */
2577
AssertReturn (m.state == MediaState_LockedRead ||
2578
m.state == MediaState_LockedWrite, E_FAIL);
2580
if (aTarget->m.state != MediaState_NotCreated)
2581
return aTarget->setStateError();
2585
/* check that the hard disk is not attached to any VM in the current state*/
2586
for (BackRefList::const_iterator it = m.backRefs.begin();
2587
it != m.backRefs.end(); ++ it)
2591
/* Note: when a VM snapshot is being taken, all normal hard disks
2592
* attached to the VM in the current state will be, as an exception,
2593
* also associated with the snapshot which is about to create (see
2594
* SnapshotMachine::init()) before deassociating them from the
2595
* current state (which takes place only on success in
2596
* Machine::fixupHardDisks()), so that the size of snapshotIds
2597
* will be 1 in this case. The given condition is used to filter out
2598
* this legal situatinon and do not report an error. */
2600
if (it->snapshotIds.size() == 0)
2602
return setError (VBOX_E_INVALID_OBJECT_STATE,
2603
tr ("Hard disk '%ls' is attached to a virtual machine with UUID {%RTuuid}. No differencing hard disks based on it may be created until it is detached"),
2604
m.locationFull.raw(), it->machineId.raw());
2607
Assert (it->snapshotIds.size() == 1);
2611
ComObjPtr <Progress> progress;
2613
if (aProgress != NULL)
2615
/* use the existing progress object... */
2616
progress = *aProgress;
2618
/* ...but create a new one if it is null */
2619
if (progress.isNull())
2621
progress.createObject();
2622
rc = progress->init (mVirtualBox, static_cast<IHardDisk*> (this),
2623
BstrFmt (tr ("Creating differencing hard disk storage unit '%ls'"),
2624
aTarget->m.locationFull.raw()),
2625
TRUE /* aCancelable */);
2626
CheckComRCReturnRC (rc);
2630
/* setup task object and thread to carry out the operation
2633
std::auto_ptr <Task> task (new Task (this, progress, Task::CreateDiff));
2634
AssertComRCReturnRC (task->autoCaller.rc());
2636
task->setData (aTarget);
2637
task->d.variant = aVariant;
2639
/* register a task (it will deregister itself when done) */
2640
++ mm.numCreateDiffTasks;
2641
Assert (mm.numCreateDiffTasks != 0); /* overflow? */
2645
/* go to Creating state before starting the task */
2646
aTarget->m.state = MediaState_Creating;
2648
rc = task->runNow();
2652
rc = task->startThread();
2653
CheckComRCReturnRC (rc);
2655
/* go to Creating state before leaving the lock */
2656
aTarget->m.state = MediaState_Creating;
2659
/* task is now owned (or already deleted) by taskThread() so release it */
2662
if (aProgress != NULL)
2664
/* return progress to the caller */
2665
*aProgress = progress;
2672
* Prepares this (source) hard disk, target hard disk and all intermediate hard
2673
* disks for the merge operation.
2675
* This method is to be called prior to calling the #mergeTo() to perform
2676
* necessary consistency checks and place involved hard disks to appropriate
2677
* states. If #mergeTo() is not called or fails, the state modifications
2678
* performed by this method must be undone by #cancelMergeTo().
2680
* Note that when @a aIgnoreAttachments is @c true then it's the caller's
2681
* responsibility to detach the source and all intermediate hard disks before
2682
* calling #mergeTo() (which will fail otherwise).
2684
* See #mergeTo() for more information about merging.
2686
* @param aTarget Target hard disk.
2687
* @param aChain Where to store the created merge chain.
2688
* @param aIgnoreAttachments Don't check if the source or any intermediate
2689
* hard disk is attached to any VM.
2691
* @note Locks treeLock() for reading. Locks this object, aTarget and all
2692
* intermediate hard disks for writing.
2694
HRESULT HardDisk::prepareMergeTo(HardDisk *aTarget,
2695
MergeChain * &aChain,
2696
bool aIgnoreAttachments /*= false*/)
2698
AssertReturn (aTarget != NULL, E_FAIL);
2700
AutoCaller autoCaller (this);
2701
AssertComRCReturnRC (autoCaller.rc());
2703
AutoCaller targetCaller (aTarget);
2704
AssertComRCReturnRC (targetCaller.rc());
2708
/* we walk the tree */
2709
AutoReadLock treeLock (this->treeLock());
2713
/* detect the merge direction */
2716
HardDisk *parent = mParent;
2717
while (parent != NULL && parent != aTarget)
2718
parent = parent->mParent;
2719
if (parent == aTarget)
2723
parent = aTarget->mParent;
2724
while (parent != NULL && parent != this)
2725
parent = parent->mParent;
2732
AutoReadLock alock (this);
2733
tgtLoc = aTarget->locationFull();
2736
AutoReadLock alock (this);
2737
return setError (E_FAIL,
2738
tr ("Hard disks '%ls' and '%ls' are unrelated"),
2739
m.locationFull.raw(), tgtLoc.raw());
2744
/* build the chain (will do necessary checks and state changes) */
2745
std::auto_ptr <MergeChain> chain (new MergeChain (forward,
2746
aIgnoreAttachments));
2748
HardDisk *last = forward ? aTarget : this;
2749
HardDisk *first = forward ? this : aTarget;
2753
if (last == aTarget)
2754
rc = chain->addTarget (last);
2755
else if (last == this)
2756
rc = chain->addSource (last);
2758
rc = chain->addIntermediate (last);
2759
CheckComRCReturnRC (rc);
2764
last = last->mParent;
2768
aChain = chain.release();
2774
* Merges this hard disk to the specified hard disk which must be either its
2775
* direct ancestor or descendant.
2777
* Given this hard disk is SOURCE and the specified hard disk is TARGET, we will
2778
* get two varians of the merge operation:
2781
* ------------------------->
2782
* [Extra] <- SOURCE <- Intermediate <- TARGET
2783
* Any Del Del LockWr
2787
* <-------------------------
2788
* TARGET <- Intermediate <- SOURCE <- [Extra]
2789
* LockWr Del Del LockWr
2791
* Each scheme shows the involved hard disks on the hard disk chain where
2792
* SOURCE and TARGET belong. Under each hard disk there is a state value which
2793
* the hard disk must have at a time of the mergeTo() call.
2795
* The hard disks in the square braces may be absent (e.g. when the forward
2796
* operation takes place and SOURCE is the base hard disk, or when the backward
2797
* merge operation takes place and TARGET is the last child in the chain) but if
2798
* they present they are involved too as shown.
2800
* Nor the source hard disk neither intermediate hard disks may be attached to
2801
* any VM directly or in the snapshot, otherwise this method will assert.
2803
* The #prepareMergeTo() method must be called prior to this method to place all
2804
* involved to necessary states and perform other consistency checks.
2806
* If @a aWait is @c true then this method will perform the operation on the
2807
* calling thread and will not return to the caller until the operation is
2808
* completed. When this method succeeds, all intermediate hard disk objects in
2809
* the chain will be uninitialized, the state of the target hard disk (and all
2810
* involved extra hard disks) will be restored and @a aChain will be deleted.
2811
* Note that this (source) hard disk is not uninitialized because of possible
2812
* AutoCaller instances held by the caller of this method on the current thread.
2813
* It's therefore the responsibility of the caller to call HardDisk::uninit()
2814
* after releasing all callers in this case!
2816
* If @a aWait is @c false then this method will crea,te a thread to perform the
2817
* create operation asynchronously and will return immediately. If the operation
2818
* succeeds, the thread will uninitialize the source hard disk object and all
2819
* intermediate hard disk objects in the chain, reset the state of the target
2820
* hard disk (and all involved extra hard disks) and delete @a aChain. If the
2821
* operation fails, the thread will only reset the states of all involved hard
2822
* disks and delete @a aChain.
2824
* When this method fails (regardless of the @a aWait mode), it is a caller's
2825
* responsiblity to undo state changes and delete @a aChain using
2828
* If @a aProgress is not NULL but the object it points to is @c null then a new
2829
* progress object will be created and assigned to @a *aProgress on success,
2830
* otherwise the existing progress object is used. If Progress is NULL, then no
2831
* progress object is created/used at all. Note that @a aProgress cannot be
2832
* NULL when @a aWait is @c false (this method will assert in this case).
2834
* @param aChain Merge chain created by #prepareMergeTo().
2835
* @param aProgress Where to find/store a Progress object to track operation
2837
* @param aWait @c true if this method should block instead of creating
2838
* an asynchronous thread.
2840
* @note Locks the branch lock for writing. Locks the hard disks from the chain
2843
HRESULT HardDisk::mergeTo(MergeChain *aChain,
2844
ComObjPtr <Progress> *aProgress,
2847
AssertReturn (aChain != NULL, E_FAIL);
2848
AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
2850
AutoCaller autoCaller (this);
2851
CheckComRCReturnRC (autoCaller.rc());
2855
ComObjPtr <Progress> progress;
2857
if (aProgress != NULL)
2859
/* use the existing progress object... */
2860
progress = *aProgress;
2862
/* ...but create a new one if it is null */
2863
if (progress.isNull())
2865
AutoReadLock alock (this);
2867
progress.createObject();
2868
rc = progress->init (mVirtualBox, static_cast<IHardDisk*>(this),
2869
BstrFmt (tr ("Merging hard disk '%s' to '%s'"),
2870
name().raw(), aChain->target()->name().raw()),
2871
TRUE /* aCancelable */);
2872
CheckComRCReturnRC (rc);
2876
/* setup task object and thread to carry out the operation
2879
std::auto_ptr <Task> task (new Task (this, progress, Task::Merge));
2880
AssertComRCReturnRC (task->autoCaller.rc());
2882
task->setData (aChain);
2884
/* Note: task owns aChain (will delete it when not needed) in all cases
2885
* except when @a aWait is @c true and runNow() fails -- in this case
2886
* aChain will be left away because cancelMergeTo() will be applied by the
2887
* caller on it as it is required in the documentation above */
2891
rc = task->runNow();
2895
rc = task->startThread();
2896
CheckComRCReturnRC (rc);
2899
/* task is now owned (or already deleted) by taskThread() so release it */
2902
if (aProgress != NULL)
2904
/* return progress to the caller */
2905
*aProgress = progress;
2912
* Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not called
2913
* or fails. Frees memory occupied by @a aChain.
2915
* @param aChain Merge chain created by #prepareMergeTo().
2917
* @note Locks the hard disks from the chain for writing.
2919
void HardDisk::cancelMergeTo (MergeChain *aChain)
2921
AutoCaller autoCaller (this);
2922
AssertComRCReturnVoid (autoCaller.rc());
2924
AssertReturnVoid (aChain != NULL);
2926
/* the destructor will do the thing */
2931
////////////////////////////////////////////////////////////////////////////////
2934
* Sets the value of m.location and calculates the value of m.locationFull.
2936
* Reimplements MediumBase::setLocation() to specially treat non-FS-path
2937
* locations and to prepend the default hard disk folder if the given location
2938
* string does not contain any path information at all.
2940
* Also, if the specified location is a file path that ends with '/' then the
2941
* file name part will be generated by this method automatically in the format
2942
* '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
2943
* and assign to this medium, and <ext> is the default extension for this
2944
* medium's storage format. Note that this procedure requires the media state to
2945
* be NotCreated and will return a faiulre otherwise.
2947
* @param aLocation Location of the storage unit. If the locaiton is a FS-path,
2948
* then it can be relative to the VirtualBox home directory.
2950
* @note Must be called from under this object's write lock.
2952
HRESULT HardDisk::setLocation (CBSTR aLocation)
2954
/// @todo so far, we assert but later it makes sense to support null
2955
/// locations for hard disks that are not yet created fail to create a
2956
/// storage unit instead
2957
CheckComArgStrNotEmptyOrNull (aLocation);
2959
AutoCaller autoCaller (this);
2960
AssertComRCReturnRC (autoCaller.rc());
2962
/* formatObj may be null only when initializing from an existing path and
2963
* no format is known yet */
2964
AssertReturn ((!mm.format.isNull() && !mm.formatObj.isNull()) ||
2965
(autoCaller.state() == InInit &&
2966
m.state != MediaState_NotCreated && m.id.isEmpty() &&
2967
mm.format.isNull() && mm.formatObj.isNull()),
2970
/* are we dealing with a new hard disk constructed using the existing
2972
bool isImport = mm.format.isNull();
2975
(mm.formatObj->capabilities() & HardDiskFormatCapabilities_File))
2979
Utf8Str location (aLocation);
2981
if (m.state == MediaState_NotCreated)
2983
/* must be a file (formatObj must be already known) */
2984
Assert (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File);
2986
if (RTPathFilename (location) == NULL)
2988
/* no file name is given (either an empty string or ends with a
2989
* slash), generate a new UUID + file name if the state allows
2992
ComAssertMsgRet (!mm.formatObj->fileExtensions().empty(),
2993
("Must be at least one extension if it is HardDiskFormatCapabilities_File\n"),
2996
Bstr ext = mm.formatObj->fileExtensions().front();
2997
ComAssertMsgRet (!ext.isEmpty(),
2998
("Default extension must not be empty\n"),
3003
location = Utf8StrFmt ("%s{%RTuuid}.%ls",
3004
location.raw(), id.raw(), ext.raw());
3008
/* append the default folder if no path is given */
3009
if (!RTPathHavePath (location))
3011
AutoReadLock propsLock (mVirtualBox->systemProperties());
3012
location = Utf8StrFmt ("%ls%c%s",
3013
mVirtualBox->systemProperties()->defaultHardDiskFolder().raw(),
3018
/* get the full file name */
3019
Utf8Str locationFull;
3020
int vrc = mVirtualBox->calculateFullPath (location, locationFull);
3021
if (RT_FAILURE (vrc))
3022
return setError (VBOX_E_FILE_ERROR,
3023
tr ("Invalid hard disk storage file location '%s' (%Rrc)"),
3024
location.raw(), vrc);
3026
/* detect the backend from the storage unit if importing */
3029
char *backendName = NULL;
3034
vrc = RTFileOpen (&file, locationFull, RTFILE_O_READ);
3035
if (RT_SUCCESS (vrc))
3038
if (RT_SUCCESS (vrc))
3040
vrc = VDGetFormat (locationFull, &backendName);
3042
else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
3044
/* assume it's not a file, restore the original location */
3045
location = locationFull = aLocation;
3046
vrc = VDGetFormat (locationFull, &backendName);
3049
if (RT_FAILURE (vrc))
3051
if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
3052
return setError (VBOX_E_FILE_ERROR,
3053
tr ("Could not find file for the hard disk '%s' (%Rrc)"),
3054
locationFull.raw(), vrc);
3056
return setError (VBOX_E_IPRT_ERROR,
3057
tr ("Could not get the storage format of the hard disk '%s' (%Rrc)"),
3058
locationFull.raw(), vrc);
3061
ComAssertRet (backendName != NULL && *backendName != '\0', E_FAIL);
3063
HRESULT rc = setFormat (Bstr (backendName));
3064
RTStrFree (backendName);
3066
/* setFormat() must not fail since we've just used the backend so
3067
* the format object must be there */
3068
AssertComRCReturnRC (rc);
3071
/* is it still a file? */
3072
if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
3074
m.location = location;
3075
m.locationFull = locationFull;
3077
if (m.state == MediaState_NotCreated)
3079
/* assign a new UUID (this UUID will be used when calling
3080
* VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
3081
* also do that if we didn't generate it to make sure it is
3082
* either generated by us or reset to null */
3083
unconst (m.id) = id;
3088
m.location = locationFull;
3089
m.locationFull = locationFull;
3094
m.location = aLocation;
3095
m.locationFull = aLocation;
3102
* Checks that the format ID is valid and sets it on success.
3104
* Note that this method will caller-reference the format object on success!
3105
* This reference must be released somewhere to let the HardDiskFormat object be
3108
* @note Must be called from under this object's write lock.
3110
HRESULT HardDisk::setFormat (CBSTR aFormat)
3112
/* get the format object first */
3114
AutoReadLock propsLock (mVirtualBox->systemProperties());
3116
unconst (mm.formatObj)
3117
= mVirtualBox->systemProperties()->hardDiskFormat (aFormat);
3118
if (mm.formatObj.isNull())
3119
return setError (E_INVALIDARG,
3120
tr ("Invalid hard disk storage format '%ls'"), aFormat);
3122
/* reference the format permanently to prevent its unexpected
3123
* uninitialization */
3124
HRESULT rc = mm.formatObj->addCaller();
3125
AssertComRCReturnRC (rc);
3127
/* get properties (preinsert them as keys in the map). Note that the
3128
* map doesn't grow over the object life time since the set of
3129
* properties is meant to be constant. */
3131
Assert (mm.properties.empty());
3133
for (HardDiskFormat::PropertyList::const_iterator it =
3134
mm.formatObj->properties().begin();
3135
it != mm.formatObj->properties().end();
3138
mm.properties.insert (std::make_pair (it->name, Bstr::Null));
3142
unconst (mm.format) = aFormat;
3148
* Queries information from the image file.
3150
* As a result of this call, the accessibility state and data members such as
3151
* size and description will be updated with the current information.
3153
* Reimplements MediumBase::queryInfo() to query hard disk information using the
3154
* VD backend interface.
3156
* @note This method may block during a system I/O call that checks storage
3159
* @note Locks treeLock() for reading and writing (for new diff media checked
3160
* for the first time). Locks mParent for reading. Locks this object for
3163
HRESULT HardDisk::queryInfo()
3165
AutoWriteLock alock (this);
3167
AssertReturn (m.state == MediaState_Created ||
3168
m.state == MediaState_Inaccessible ||
3169
m.state == MediaState_LockedRead ||
3170
m.state == MediaState_LockedWrite,
3175
int vrc = VINF_SUCCESS;
3177
/* check if a blocking queryInfo() call is in progress on some other thread,
3178
* and wait for it to finish if so instead of querying data ourselves */
3179
if (m.queryInfoSem != NIL_RTSEMEVENTMULTI)
3181
Assert (m.state == MediaState_LockedRead);
3183
++ m.queryInfoCallers;
3186
vrc = RTSemEventMultiWait (m.queryInfoSem, RT_INDEFINITE_WAIT);
3189
-- m.queryInfoCallers;
3191
if (m.queryInfoCallers == 0)
3193
/* last waiting caller deletes the semaphore */
3194
RTSemEventMultiDestroy (m.queryInfoSem);
3195
m.queryInfoSem = NIL_RTSEMEVENTMULTI;
3203
/* lazily create a semaphore for possible callers */
3204
vrc = RTSemEventMultiCreate (&m.queryInfoSem);
3205
ComAssertRCRet (vrc, E_FAIL);
3207
bool tempStateSet = false;
3208
if (m.state != MediaState_LockedRead &&
3209
m.state != MediaState_LockedWrite)
3211
/* Cause other methods to prevent any modifications before leaving the
3212
* lock. Note that clients will never see this temporary state change
3213
* since any COMGETTER(State) is (or will be) blocked until we finish
3214
* and restore the actual state. */
3215
m.state = MediaState_LockedRead;
3216
tempStateSet = true;
3219
/* leave the lock before a blocking operation */
3222
bool success = false;
3223
Utf8Str lastAccessError;
3227
Utf8Str location (m.locationFull);
3229
/* are we dealing with a new hard disk constructed using the existing
3231
bool isImport = m.id.isEmpty();
3234
vrc = VDCreate (mm.vdDiskIfaces, &hdd);
3235
ComAssertRCThrow (vrc, E_FAIL);
3239
unsigned flags = VD_OPEN_FLAGS_INFO;
3241
/* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
3242
* hard disks because that would prevent necessary modifications
3243
* when opening hard disks of some third-party formats for the first
3244
* time in VirtualBox (such as VMDK for which VDOpen() needs to
3245
* generate an UUID if it is missing) */
3246
if ( (mm.hddOpenMode == OpenReadOnly)
3249
flags |= VD_OPEN_FLAGS_READONLY;
3251
/** @todo This kind of opening of images is assuming that diff
3252
* images can be opened as base images. Not very clean, and should
3253
* be fixed eventually. */
3259
if (RT_FAILURE (vrc))
3261
lastAccessError = Utf8StrFmt (
3262
tr ("Could not open the hard disk '%ls'%s"),
3263
m.locationFull.raw(), vdError (vrc).raw());
3267
if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_Uuid)
3269
/* modify the UUIDs if necessary */
3272
vrc = VDSetUuid(hdd, 0, mm.imageId);
3273
ComAssertRCThrow(vrc, E_FAIL);
3277
vrc = VDSetUuid(hdd, 0, mm.parentId);
3278
ComAssertRCThrow(vrc, E_FAIL);
3280
/* zap the information, these are no long-term members */
3281
mm.setImageId = false;
3282
unconst(mm.imageId).clear();
3283
mm.setParentId = false;
3284
unconst(mm.parentId).clear();
3286
/* check the UUID */
3288
vrc = VDGetUuid(hdd, 0, &uuid);
3289
ComAssertRCThrow(vrc, E_FAIL);
3293
unconst(m.id) = uuid;
3295
if (m.id.isEmpty() && (mm.hddOpenMode == OpenReadOnly))
3296
// only when importing a VDMK that has no UUID, create one in memory
3297
unconst(m.id).create();
3301
Assert (!m.id.isEmpty());
3305
lastAccessError = Utf8StrFmt (
3306
tr ("UUID {%RTuuid} of the hard disk '%ls' does not match the value {%RTuuid} stored in the media registry ('%ls')"),
3307
&uuid, m.locationFull.raw(), m.id.raw(),
3308
mVirtualBox->settingsFileName().raw());
3315
/* the backend does not support storing UUIDs within the
3316
* underlying storage so use what we store in XML */
3318
/* generate an UUID for an imported UUID-less hard disk */
3322
unconst(m.id) = mm.imageId;
3324
unconst(m.id).create();
3328
/* check the type */
3329
unsigned uImageFlags;
3330
vrc = VDGetImageFlags (hdd, 0, &uImageFlags);
3331
ComAssertRCThrow (vrc, E_FAIL);
3333
if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
3336
vrc = VDGetParentUuid (hdd, 0, &parentId);
3337
ComAssertRCThrow (vrc, E_FAIL);
3341
/* the parent must be known to us. Note that we freely
3342
* call locking methods of mVirtualBox and parent from the
3343
* write lock (breaking the {parent,child} lock order)
3344
* because there may be no concurrent access to the just
3345
* opened hard disk on ther threads yet (and init() will
3346
* fail if this method reporst MediaState_Inaccessible) */
3349
ComObjPtr<HardDisk> parent;
3350
rc = mVirtualBox->findHardDisk(&id, NULL,
3351
false /* aSetError */,
3355
lastAccessError = Utf8StrFmt (
3356
tr ("Parent hard disk with UUID {%RTuuid} of the hard disk '%ls' is not found in the media registry ('%ls')"),
3357
&parentId, m.locationFull.raw(),
3358
mVirtualBox->settingsFileName().raw());
3362
/* deassociate from VirtualBox, associate with parent */
3364
mVirtualBox->removeDependentChild (this);
3366
/* we set mParent & children() */
3367
AutoWriteLock treeLock (this->treeLock());
3369
Assert (mParent.isNull());
3371
mParent->addDependentChild (this);
3375
/* we access mParent */
3376
AutoReadLock treeLock (this->treeLock());
3378
/* check that parent UUIDs match. Note that there's no need
3379
* for the parent's AutoCaller (our lifetime is bound to
3382
if (mParent.isNull())
3384
lastAccessError = Utf8StrFmt (
3385
tr ("Hard disk '%ls' is differencing but it is not associated with any parent hard disk in the media registry ('%ls')"),
3386
m.locationFull.raw(),
3387
mVirtualBox->settingsFileName().raw());
3391
AutoReadLock parentLock (mParent);
3392
if (mParent->state() != MediaState_Inaccessible &&
3393
mParent->id() != parentId)
3395
lastAccessError = Utf8StrFmt (
3396
tr ("Parent UUID {%RTuuid} of the hard disk '%ls' does not match UUID {%RTuuid} of its parent hard disk stored in the media registry ('%ls')"),
3397
&parentId, m.locationFull.raw(),
3398
mParent->id().raw(),
3399
mVirtualBox->settingsFileName().raw());
3403
/// @todo NEWMEDIA what to do if the parent is not
3404
/// accessible while the diff is? Probably, nothing. The
3405
/// real code will detect the mismatch anyway.
3409
m.size = VDGetFileSize (hdd, 0);
3410
mm.logicalSize = VDGetSize (hdd, 0) / _1M;
3430
m.lastAccessError.setNull();
3433
m.lastAccessError = lastAccessError;
3434
LogWarningFunc (("'%ls' is not accessible (error='%ls', rc=%Rhrc, vrc=%Rrc)\n",
3435
m.locationFull.raw(), m.lastAccessError.raw(),
3439
/* inform other callers if there are any */
3440
if (m.queryInfoCallers > 0)
3442
RTSemEventMultiSignal (m.queryInfoSem);
3446
/* delete the semaphore ourselves */
3447
RTSemEventMultiDestroy (m.queryInfoSem);
3448
m.queryInfoSem = NIL_RTSEMEVENTMULTI;
3453
/* Set the proper state according to the result of the check */
3455
m.state = MediaState_Created;
3457
m.state = MediaState_Inaccessible;
3461
/* we're locked, use a special field to store the result */
3462
m.accessibleInLock = success;
3469
* @note Called from this object's AutoMayUninitSpan and from under mVirtualBox
3472
* @note Also reused by HardDisk::Reset().
3474
* @note Locks treeLock() for reading.
3476
HRESULT HardDisk::canClose()
3478
/* we access children */
3479
AutoReadLock treeLock (this->treeLock());
3481
if (children().size() != 0)
3482
return setError (E_FAIL,
3483
tr ("Hard disk '%ls' has %d child hard disks"),
3490
* @note Called from within this object's AutoWriteLock.
3492
HRESULT HardDisk::canAttach(const Guid & /* aMachineId */,
3493
const Guid & /* aSnapshotId */)
3495
if (mm.numCreateDiffTasks > 0)
3496
return setError (E_FAIL,
3497
tr ("One or more differencing child hard disks are being created for the hard disk '%ls' (%u)"),
3498
m.locationFull.raw(), mm.numCreateDiffTasks);
3504
* @note Called from within this object's AutoMayUninitSpan (or AutoCaller) and
3505
* from under mVirtualBox write lock.
3507
* @note Locks treeLock() for writing.
3509
HRESULT HardDisk::unregisterWithVirtualBox()
3511
/* Note that we need to de-associate ourselves from the parent to let
3512
* unregisterHardDisk() properly save the registry */
3514
/* we modify mParent and access children */
3515
AutoWriteLock treeLock (this->treeLock());
3517
const ComObjPtr<HardDisk, ComWeakRef> parent = mParent;
3519
AssertReturn (children().size() == 0, E_FAIL);
3521
if (!mParent.isNull())
3523
/* deassociate from the parent, associate with VirtualBox */
3524
mVirtualBox->addDependentChild (this);
3525
mParent->removeDependentChild (this);
3529
HRESULT rc = mVirtualBox->unregisterHardDisk(this);
3533
if (!parent.isNull())
3535
/* re-associate with the parent as we are still relatives in the
3538
mParent->addDependentChild (this);
3539
mVirtualBox->removeDependentChild (this);
3547
* Returns the last error message collected by the vdErrorCall callback and
3550
* The error message is returned prepended with a dot and a space, like this:
3552
* ". <error_text> (%Rrc)"
3554
* to make it easily appendable to a more general error message. The @c %Rrc
3555
* format string is given @a aVRC as an argument.
3557
* If there is no last error message collected by vdErrorCall or if it is a
3558
* null or empty string, then this function returns the following text:
3563
* @note Doesn't do any object locking; it is assumed that the caller makes sure
3564
* the callback isn't called by more than one thread at a time.
3566
* @param aVRC VBox error code to use when no error message is provided.
3568
Utf8Str HardDisk::vdError (int aVRC)
3572
if (mm.vdError.isEmpty())
3573
error = Utf8StrFmt (" (%Rrc)", aVRC);
3575
error = Utf8StrFmt (".\n%s", mm.vdError.raw());
3577
mm.vdError.setNull();
3583
* Error message callback.
3585
* Puts the reported error message to the mm.vdError field.
3587
* @note Doesn't do any object locking; it is assumed that the caller makes sure
3588
* the callback isn't called by more than one thread at a time.
3590
* @param pvUser The opaque data passed on container creation.
3591
* @param rc The VBox error code.
3592
* @param RT_SRC_POS_DECL Use RT_SRC_POS.
3593
* @param pszFormat Error message format string.
3594
* @param va Error message arguments.
3597
DECLCALLBACK(void) HardDisk::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
3598
const char *pszFormat, va_list va)
3600
NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
3602
HardDisk *that = static_cast<HardDisk*>(pvUser);
3603
AssertReturnVoid (that != NULL);
3605
if (that->mm.vdError.isEmpty())
3607
Utf8StrFmt ("%s (%Rrc)", Utf8StrFmtVA (pszFormat, va).raw(), rc);
3610
Utf8StrFmt ("%s.\n%s (%Rrc)", that->mm.vdError.raw(),
3611
Utf8StrFmtVA (pszFormat, va).raw(), rc);
3615
* PFNVMPROGRESS callback handler for Task operations.
3617
* @param uPercent Completetion precentage (0-100).
3618
* @param pvUser Pointer to the Progress instance.
3621
DECLCALLBACK(int) HardDisk::vdProgressCall(PVM /* pVM */, unsigned uPercent,
3624
HardDisk *that = static_cast<HardDisk*>(pvUser);
3625
AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
3627
if (that->mm.vdProgress != NULL)
3629
/* update the progress object, capping it at 99% as the final percent
3630
* is used for additional operations like setting the UUIDs and similar. */
3631
HRESULT rc = that->mm.vdProgress->setCurrentOperationProgress(uPercent * 99 / 100);
3635
return VERR_CANCELLED;
3637
return VERR_INVALID_STATE;
3641
return VINF_SUCCESS;
3645
DECLCALLBACK(bool) HardDisk::vdConfigAreKeysValid (void *pvUser,
3646
const char * /* pszzValid */)
3648
HardDisk *that = static_cast<HardDisk*>(pvUser);
3649
AssertReturn (that != NULL, false);
3651
/* we always return true since the only keys we have are those found in
3657
DECLCALLBACK(int) HardDisk::vdConfigQuerySize(void *pvUser, const char *pszName,
3660
AssertReturn (VALID_PTR (pcbValue), VERR_INVALID_POINTER);
3662
HardDisk *that = static_cast<HardDisk*>(pvUser);
3663
AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
3665
Data::PropertyMap::const_iterator it =
3666
that->mm.properties.find (Bstr (pszName));
3667
if (it == that->mm.properties.end())
3668
return VERR_CFGM_VALUE_NOT_FOUND;
3670
/* we interpret null values as "no value" in HardDisk */
3671
if (it->second.isNull())
3672
return VERR_CFGM_VALUE_NOT_FOUND;
3674
*pcbValue = it->second.length() + 1 /* include terminator */;
3676
return VINF_SUCCESS;
3680
DECLCALLBACK(int) HardDisk::vdConfigQuery (void *pvUser, const char *pszName,
3681
char *pszValue, size_t cchValue)
3683
AssertReturn (VALID_PTR (pszValue), VERR_INVALID_POINTER);
3685
HardDisk *that = static_cast<HardDisk*>(pvUser);
3686
AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
3688
Data::PropertyMap::const_iterator it =
3689
that->mm.properties.find (Bstr (pszName));
3690
if (it == that->mm.properties.end())
3691
return VERR_CFGM_VALUE_NOT_FOUND;
3693
Utf8Str value = it->second;
3694
if (value.length() >= cchValue)
3695
return VERR_CFGM_NOT_ENOUGH_SPACE;
3697
/* we interpret null values as "no value" in HardDisk */
3698
if (it->second.isNull())
3699
return VERR_CFGM_VALUE_NOT_FOUND;
3701
memcpy (pszValue, value, value.length() + 1);
3703
return VINF_SUCCESS;
3707
* Thread function for time-consuming tasks.
3709
* The Task structure passed to @a pvUser must be allocated using new and will
3710
* be freed by this method before it returns.
3712
* @param pvUser Pointer to the Task instance.
3715
DECLCALLBACK(int) HardDisk::taskThread (RTTHREAD thread, void *pvUser)
3717
std::auto_ptr <Task> task (static_cast <Task *> (pvUser));
3718
AssertReturn (task.get(), VERR_GENERAL_FAILURE);
3720
bool isAsync = thread != NIL_RTTHREAD;
3722
HardDisk *that = task->that;
3724
/// @todo ugly hack, fix ComAssert... later
3725
#define setError that->setError
3727
/* Note: no need in AutoCaller because Task does that */
3730
LogFlowFunc (("{%p}: operation=%d\n", that, task->operation));
3734
switch (task->operation)
3736
////////////////////////////////////////////////////////////////////////
3738
case Task::CreateBase:
3740
/* The lock is also used as a signal from the task initiator (which
3741
* releases it only after RTThreadCreate()) that we can start the job */
3742
AutoWriteLock thatLock (that);
3744
/* these parameters we need after creation */
3745
uint64_t size = 0, logicalSize = 0;
3747
/* The object may request a specific UUID (through a special form of
3748
* the setLocation() argument). Otherwise we have to generate it */
3749
Guid id = that->m.id;
3750
bool generateUuid = id.isEmpty();
3754
/* VirtualBox::registerHardDisk() will need UUID */
3755
unconst (that->m.id) = id;
3761
int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
3762
ComAssertRCThrow (vrc, E_FAIL);
3764
Utf8Str format (that->mm.format);
3765
Utf8Str location (that->m.locationFull);
3766
/* uint64_t capabilities = */ that->mm.formatObj->capabilities();
3768
/* unlock before the potentially lengthy operation */
3769
Assert (that->m.state == MediaState_Creating);
3774
/* ensure the directory exists */
3775
rc = VirtualBox::ensureFilePathExists (location);
3776
CheckComRCThrowRC (rc);
3778
PDMMEDIAGEOMETRY geo = { 0 }; /* auto-detect */
3780
/* needed for vdProgressCallback */
3781
that->mm.vdProgress = task->progress;
3783
vrc = VDCreateBase (hdd, format, location,
3786
NULL, &geo, &geo, id.raw(),
3787
VD_OPEN_FLAGS_NORMAL,
3788
NULL, that->mm.vdDiskIfaces);
3790
if (RT_FAILURE (vrc))
3792
throw setError (E_FAIL,
3793
tr ("Could not create the hard disk storage unit '%s'%s"),
3794
location.raw(), that->vdError (vrc).raw());
3797
size = VDGetFileSize (hdd, 0);
3798
logicalSize = VDGetSize (hdd, 0) / _1M;
3800
catch (HRESULT aRC) { rc = aRC; }
3804
catch (HRESULT aRC) { rc = aRC; }
3808
/* register with mVirtualBox as the last step and move to
3809
* Created state only on success (leaving an orphan file is
3810
* better than breaking media registry consistency) */
3811
rc = that->mVirtualBox->registerHardDisk(that);
3814
thatLock.maybeEnter();
3818
that->m.state = MediaState_Created;
3820
that->m.size = size;
3821
that->mm.logicalSize = logicalSize;
3825
/* back to NotCreated on failure */
3826
that->m.state = MediaState_NotCreated;
3828
/* reset UUID to prevent it from being reused next time */
3830
unconst (that->m.id).clear();
3836
////////////////////////////////////////////////////////////////////////
3838
case Task::CreateDiff:
3840
ComObjPtr<HardDisk> &target = task->d.target;
3842
/* Lock both in {parent,child} order. The lock is also used as a
3843
* signal from the task initiator (which releases it only after
3844
* RTThreadCreate()) that we can start the job*/
3845
AutoMultiWriteLock2 thatLock (that, target);
3847
uint64_t size = 0, logicalSize = 0;
3849
/* The object may request a specific UUID (through a special form of
3850
* the setLocation() argument). Otherwise we have to generate it */
3851
Guid targetId = target->m.id;
3852
bool generateUuid = targetId.isEmpty();
3856
/* VirtualBox::registerHardDisk() will need UUID */
3857
unconst (target->m.id) = targetId;
3863
int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
3864
ComAssertRCThrow (vrc, E_FAIL);
3866
Guid id = that->m.id;
3867
Utf8Str format (that->mm.format);
3868
Utf8Str location (that->m.locationFull);
3870
Utf8Str targetFormat (target->mm.format);
3871
Utf8Str targetLocation (target->m.locationFull);
3873
Assert (target->m.state == MediaState_Creating);
3875
/* Note: MediaState_LockedWrite is ok when taking an online
3877
Assert (that->m.state == MediaState_LockedRead ||
3878
that->m.state == MediaState_LockedWrite);
3880
/* unlock before the potentially lengthy operation */
3885
vrc = VDOpen (hdd, format, location,
3886
VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
3887
that->mm.vdDiskIfaces);
3888
if (RT_FAILURE (vrc))
3890
throw setError (E_FAIL,
3891
tr ("Could not open the hard disk storage unit '%s'%s"),
3892
location.raw(), that->vdError (vrc).raw());
3895
/* ensure the target directory exists */
3896
rc = VirtualBox::ensureFilePathExists (targetLocation);
3897
CheckComRCThrowRC (rc);
3899
/* needed for vdProgressCallback */
3900
that->mm.vdProgress = task->progress;
3902
vrc = VDCreateDiff (hdd, targetFormat, targetLocation,
3904
NULL, targetId.raw(),
3906
VD_OPEN_FLAGS_NORMAL,
3907
target->mm.vdDiskIfaces,
3908
that->mm.vdDiskIfaces);
3910
that->mm.vdProgress = NULL;
3912
if (RT_FAILURE (vrc))
3914
throw setError (E_FAIL,
3915
tr ("Could not create the differencing hard disk storage unit '%s'%s"),
3916
targetLocation.raw(), that->vdError (vrc).raw());
3919
size = VDGetFileSize (hdd, 1);
3920
logicalSize = VDGetSize (hdd, 1) / _1M;
3922
catch (HRESULT aRC) { rc = aRC; }
3926
catch (HRESULT aRC) { rc = aRC; }
3930
/* we set mParent & children() (note that thatLock is released
3931
* here), but lock VirtualBox first to follow the rule */
3932
AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
3935
Assert (target->mParent.isNull());
3937
/* associate the child with the parent and deassociate from
3939
target->mParent = that;
3940
that->addDependentChild (target);
3941
target->mVirtualBox->removeDependentChild (target);
3943
/* diffs for immutable hard disks are auto-reset by default */
3944
target->mm.autoReset =
3945
that->root()->mm.type == HardDiskType_Immutable ?
3948
/* register with mVirtualBox as the last step and move to
3949
* Created state only on success (leaving an orphan file is
3950
* better than breaking media registry consistency) */
3951
rc = that->mVirtualBox->registerHardDisk (target);
3955
/* break the parent association on failure to register */
3956
target->mVirtualBox->addDependentChild (target);
3957
that->removeDependentChild (target);
3958
target->mParent.setNull();
3962
thatLock.maybeEnter();
3966
target->m.state = MediaState_Created;
3968
target->m.size = size;
3969
target->mm.logicalSize = logicalSize;
3973
/* back to NotCreated on failure */
3974
target->m.state = MediaState_NotCreated;
3976
target->mm.autoReset = FALSE;
3978
/* reset UUID to prevent it from being reused next time */
3980
unconst (target->m.id).clear();
3985
/* unlock ourselves when done (unless in MediaState_LockedWrite
3986
* state because of taking the online snapshot*/
3987
if (that->m.state != MediaState_LockedWrite)
3989
HRESULT rc2 = that->UnlockRead (NULL);
3994
/* deregister the task registered in createDiffStorage() */
3995
Assert (that->mm.numCreateDiffTasks != 0);
3996
-- that->mm.numCreateDiffTasks;
3998
/* Note that in sync mode, it's the caller's responsibility to
3999
* unlock the hard disk */
4004
////////////////////////////////////////////////////////////////////////
4008
/* The lock is also used as a signal from the task initiator (which
4009
* releases it only after RTThreadCreate()) that we can start the
4010
* job. We don't actually need the lock for anything else since the
4011
* object is protected by MediaState_Deleting and we don't modify
4012
* its sensitive fields below */
4014
AutoWriteLock thatLock (that);
4017
MergeChain *chain = task->d.chain.get();
4020
LogFlow (("*** MERGE forward = %RTbool\n", chain->isForward()));
4026
int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
4027
ComAssertRCThrow (vrc, E_FAIL);
4031
/* Open all hard disks in the chain (they are in the
4032
* {parent,child} order in there. Note that we don't lock
4033
* objects in this chain since they must be in states
4034
* (Deleting and LockedWrite) that prevent from changing
4035
* their format and location fields from outside. */
4037
for (MergeChain::const_iterator it = chain->begin();
4038
it != chain->end(); ++ it)
4040
/* complex sanity (sane complexity) */
4041
Assert ((chain->isForward() &&
4042
((*it != chain->back() &&
4043
(*it)->m.state == MediaState_Deleting) ||
4044
(*it == chain->back() &&
4045
(*it)->m.state == MediaState_LockedWrite))) ||
4046
(!chain->isForward() &&
4047
((*it != chain->front() &&
4048
(*it)->m.state == MediaState_Deleting) ||
4049
(*it == chain->front() &&
4050
(*it)->m.state == MediaState_LockedWrite))));
4052
Assert (*it == chain->target() ||
4053
(*it)->m.backRefs.size() == 0);
4055
/* open the first image with VDOPEN_FLAGS_INFO because
4056
* it's not necessarily the base one */
4057
vrc = VDOpen (hdd, Utf8Str ((*it)->mm.format),
4058
Utf8Str ((*it)->m.locationFull),
4059
it == chain->begin() ?
4060
VD_OPEN_FLAGS_INFO : 0,
4061
(*it)->mm.vdDiskIfaces);
4062
if (RT_FAILURE (vrc))
4065
LogFlow (("*** MERGE disk = %ls\n",
4066
(*it)->m.locationFull.raw()));
4070
/* needed for vdProgressCallback */
4071
that->mm.vdProgress = task->progress;
4073
unsigned start = chain->isForward() ?
4074
0 : (unsigned)chain->size() - 1;
4075
unsigned end = chain->isForward() ?
4076
(unsigned)chain->size() - 1 : 0;
4078
LogFlow (("*** MERGE from %d to %d\n", start, end));
4080
vrc = VDMerge (hdd, start, end, that->mm.vdDiskIfaces);
4082
that->mm.vdProgress = NULL;
4084
if (RT_FAILURE (vrc))
4087
/* update parent UUIDs */
4088
/// @todo VDMerge should be taught to do so, including the
4089
/// multiple children case
4090
if (chain->isForward())
4092
/* target's UUID needs to be updated (note that target
4093
* is the only image in the container on success) */
4094
vrc = VDSetParentUuid (hdd, 0, chain->parent()->m.id);
4095
if (RT_FAILURE (vrc))
4100
/* we need to update UUIDs of all source's children
4101
* which cannot be part of the container at once so
4102
* add each one in there individually */
4103
if (chain->children().size() > 0)
4105
for (List::const_iterator it = chain->children().begin();
4106
it != chain->children().end(); ++ it)
4108
/* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
4109
vrc = VDOpen (hdd, Utf8Str ((*it)->mm.format),
4110
Utf8Str ((*it)->m.locationFull),
4112
(*it)->mm.vdDiskIfaces);
4113
if (RT_FAILURE (vrc))
4116
vrc = VDSetParentUuid (hdd, 1,
4117
chain->target()->m.id);
4118
if (RT_FAILURE (vrc))
4121
vrc = VDClose (hdd, false /* fDelete */);
4122
if (RT_FAILURE (vrc))
4128
catch (HRESULT aRC) { rc = aRC; }
4131
throw setError (E_FAIL,
4132
tr ("Could not merge the hard disk '%ls' to '%ls'%s"),
4133
chain->source()->m.locationFull.raw(),
4134
chain->target()->m.locationFull.raw(),
4135
that->vdError (aVRC).raw());
4140
catch (HRESULT aRC) { rc = aRC; }
4144
bool saveSettingsFailed = false;
4148
/* all hard disks but the target were successfully deleted by
4149
* VDMerge; reparent the last one and uninitialize deleted */
4151
/* we set mParent & children() (note that thatLock is released
4152
* here), but lock VirtualBox first to follow the rule */
4153
AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
4156
HardDisk *source = chain->source();
4157
HardDisk *target = chain->target();
4159
if (chain->isForward())
4161
/* first, unregister the target since it may become a base
4162
* hard disk which needs re-registration */
4163
rc2 = target->mVirtualBox->
4164
unregisterHardDisk (target, false /* aSaveSettings */);
4167
/* then, reparent it and disconnect the deleted branch at
4168
* both ends (chain->parent() is source's parent) */
4169
target->mParent->removeDependentChild (target);
4170
target->mParent = chain->parent();
4171
if (!target->mParent.isNull())
4173
target->mParent->addDependentChild (target);
4174
target->mParent->removeDependentChild (source);
4175
source->mParent.setNull();
4179
target->mVirtualBox->addDependentChild (target);
4180
target->mVirtualBox->removeDependentChild (source);
4183
/* then, register again */
4184
rc2 = target->mVirtualBox->
4185
registerHardDisk (target, false /* aSaveSettings */);
4190
Assert (target->children().size() == 1);
4191
HardDisk *targetChild = target->children().front();
4193
/* disconnect the deleted branch at the elder end */
4194
target->removeDependentChild (targetChild);
4195
targetChild->mParent.setNull();
4197
const List &children = chain->children();
4199
/* reparent source's chidren and disconnect the deleted
4200
* branch at the younger end m*/
4201
if (children.size() > 0)
4203
/* obey {parent,child} lock order */
4204
AutoWriteLock sourceLock (source);
4206
for (List::const_iterator it = children.begin();
4207
it != children.end(); ++ it)
4209
AutoWriteLock childLock (*it);
4211
(*it)->mParent = target;
4212
(*it)->mParent->addDependentChild (*it);
4213
source->removeDependentChild (*it);
4218
/* try to save the hard disk registry */
4219
rc = that->mVirtualBox->saveSettings();
4223
/* unregister and uninitialize all hard disks in the chain
4226
for (MergeChain::iterator it = chain->begin();
4227
it != chain->end();)
4229
if (*it == chain->target())
4235
rc2 = (*it)->mVirtualBox->
4236
unregisterHardDisk(*it, false /* aSaveSettings */);
4239
/* now, uninitialize the deleted hard disk (note that
4240
* due to the Deleting state, uninit() will not touch
4241
* the parent-child relationship so we need to
4242
* uninitialize each disk individually) */
4244
/* note that the operation initiator hard disk (which is
4245
* normally also the source hard disk) is a special case
4246
* -- there is one more caller added by Task to it which
4247
* we must release. Also, if we are in sync mode, the
4248
* caller may still hold an AutoCaller instance for it
4249
* and therefore we cannot uninit() it (it's therefore
4250
* the caller's responsibility) */
4252
task->autoCaller.release();
4254
/* release the caller added by MergeChain before
4256
(*it)->releaseCaller();
4258
if (isAsync || *it != that)
4261
/* delete (to prevent uninitialization in MergeChain
4262
* dtor) and advance to the next item */
4263
it = chain->erase (it);
4266
/* Note that states of all other hard disks (target, parent,
4267
* children) will be restored by the MergeChain dtor */
4271
/* too bad if we fail, but we'll need to rollback everything
4272
* we did above to at least keep the HD tree in sync with
4273
* the current registry on disk */
4275
saveSettingsFailed = true;
4277
/// @todo NEWMEDIA implement a proper undo
4285
/* Here we come if either VDMerge() failed (in which case we
4286
* assume that it tried to do everything to make a further
4287
* retry possible -- e.g. not deleted intermediate hard disks
4288
* and so on) or VirtualBox::saveSettings() failed (where we
4289
* should have the original tree but with intermediate storage
4290
* units deleted by VDMerge()). We have to only restore states
4291
* (through the MergeChain dtor) unless we are run synchronously
4292
* in which case it's the responsibility of the caller as stated
4293
* in the mergeTo() docs. The latter also implies that we
4294
* don't own the merge chain, so release it in this case. */
4297
task->d.chain.release();
4299
NOREF (saveSettingsFailed);
4305
////////////////////////////////////////////////////////////////////////
4309
ComObjPtr<HardDisk> &target = task->d.target;
4310
ComObjPtr<HardDisk> &parent = task->d.parentDisk;
4312
/* Lock all in {parent,child} order. The lock is also used as a
4313
* signal from the task initiator (which releases it only after
4314
* RTThreadCreate()) that we can start the job. */
4315
AutoMultiWriteLock3 thatLock (that, target, parent);
4317
ImageChain *srcChain = task->d.source.get();
4318
ImageChain *parentChain = task->d.parent.get();
4320
uint64_t size = 0, logicalSize = 0;
4322
/* The object may request a specific UUID (through a special form of
4323
* the setLocation() argument). Otherwise we have to generate it */
4324
Guid targetId = target->m.id;
4325
bool generateUuid = targetId.isEmpty();
4329
/* VirtualBox::registerHardDisk() will need UUID */
4330
unconst (target->m.id) = targetId;
4336
int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
4337
ComAssertRCThrow (vrc, E_FAIL);
4341
/* Open all hard disk images in the source chain. */
4342
for (List::const_iterator it = srcChain->begin();
4343
it != srcChain->end(); ++ it)
4346
Assert ((*it)->m.state == MediaState_LockedRead);
4348
/** Open all images in read-only mode. */
4349
vrc = VDOpen (hdd, Utf8Str ((*it)->mm.format),
4350
Utf8Str ((*it)->m.locationFull),
4351
VD_OPEN_FLAGS_READONLY,
4352
(*it)->mm.vdDiskIfaces);
4353
if (RT_FAILURE (vrc))
4355
throw setError (E_FAIL,
4356
tr ("Could not open the hard disk storage unit '%s'%s"),
4357
Utf8Str ((*it)->m.locationFull).raw(),
4358
that->vdError (vrc).raw());
4362
/* unlock before the potentially lengthy operation */
4365
Utf8Str targetFormat (target->mm.format);
4366
Utf8Str targetLocation (target->m.locationFull);
4368
Assert ( target->m.state == MediaState_Creating
4369
|| target->m.state == MediaState_LockedWrite);
4370
Assert (that->m.state == MediaState_LockedRead);
4371
Assert (parent.isNull() || parent->m.state == MediaState_LockedRead);
4373
/* ensure the target directory exists */
4374
rc = VirtualBox::ensureFilePathExists (targetLocation);
4375
CheckComRCThrowRC (rc);
4377
/* needed for vdProgressCallback */
4378
that->mm.vdProgress = task->progress;
4381
int vrc = VDCreate (that->mm.vdDiskIfaces, &targetHdd);
4382
ComAssertRCThrow (vrc, E_FAIL);
4386
/* Open all hard disk images in the parent chain. */
4387
for (List::const_iterator it = parentChain->begin();
4388
it != parentChain->end(); ++ it)
4391
Assert ( (*it)->m.state == MediaState_LockedRead
4392
|| (*it)->m.state == MediaState_LockedWrite);
4394
/* Open all images in appropriate mode. */
4395
vrc = VDOpen (targetHdd, Utf8Str ((*it)->mm.format),
4396
Utf8Str ((*it)->m.locationFull),
4397
((*it)->m.state == MediaState_LockedWrite) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
4398
(*it)->mm.vdDiskIfaces);
4399
if (RT_FAILURE (vrc))
4401
throw setError (E_FAIL,
4402
tr ("Could not open the hard disk storage unit '%s'%s"),
4403
Utf8Str ((*it)->m.locationFull).raw(),
4404
that->vdError (vrc).raw());
4408
vrc = VDCopy (hdd, VD_LAST_IMAGE, targetHdd,
4410
target->m.state == MediaState_Creating ? targetLocation.raw() : (char *)NULL,
4412
task->d.variant, targetId.raw(), NULL,
4413
target->mm.vdDiskIfaces,
4414
that->mm.vdDiskIfaces);
4416
that->mm.vdProgress = NULL;
4418
if (RT_FAILURE (vrc))
4420
throw setError (E_FAIL,
4421
tr ("Could not create the clone hard disk '%s'%s"),
4422
targetLocation.raw(), that->vdError (vrc).raw());
4424
size = VDGetFileSize (targetHdd, 0);
4425
logicalSize = VDGetSize (targetHdd, 0) / _1M;
4427
catch (HRESULT aRC) { rc = aRC; }
4429
VDDestroy (targetHdd);
4431
catch (HRESULT aRC) { rc = aRC; }
4435
catch (HRESULT aRC) { rc = aRC; }
4437
/* Only do the parent changes for newly created images. */
4438
if (target->m.state == MediaState_Creating)
4442
/* we set mParent & children() (note that thatLock is released
4443
* here), but lock VirtualBox first to follow the rule */
4444
AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
4447
Assert (target->mParent.isNull());
4451
/* associate the clone with the parent and deassociate
4452
* from VirtualBox */
4453
target->mParent = parent;
4454
parent->addDependentChild (target);
4455
target->mVirtualBox->removeDependentChild (target);
4457
/* register with mVirtualBox as the last step and move to
4458
* Created state only on success (leaving an orphan file is
4459
* better than breaking media registry consistency) */
4460
rc = parent->mVirtualBox->registerHardDisk(target);
4464
/* break parent association on failure to register */
4465
target->mVirtualBox->addDependentChild (target);
4466
parent->removeDependentChild (target);
4467
target->mParent.setNull();
4473
rc = that->mVirtualBox->registerHardDisk(target);
4478
thatLock.maybeEnter();
4480
if (target->m.state == MediaState_Creating)
4484
target->m.state = MediaState_Created;
4486
target->m.size = size;
4487
target->mm.logicalSize = logicalSize;
4491
/* back to NotCreated on failure */
4492
target->m.state = MediaState_NotCreated;
4494
/* reset UUID to prevent it from being reused next time */
4496
unconst (target->m.id).clear();
4500
/* Everything is explicitly unlocked when the task exits,
4501
* as the task destruction also destroys the source chain. */
4503
/* Make sure the source chain is released early. It could happen
4504
* that we get a deadlock in Appliance::Import when Medium::Close
4505
* is called & the source chain is released at the same time. */
4506
task->d.source.reset();
4510
////////////////////////////////////////////////////////////////////////
4514
/* The lock is also used as a signal from the task initiator (which
4515
* releases it only after RTThreadCreate()) that we can start the job */
4516
AutoWriteLock thatLock (that);
4521
int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
4522
ComAssertRCThrow (vrc, E_FAIL);
4524
Utf8Str format (that->mm.format);
4525
Utf8Str location (that->m.locationFull);
4527
/* unlock before the potentially lengthy operation */
4528
Assert (that->m.state == MediaState_Deleting);
4533
vrc = VDOpen (hdd, format, location,
4534
VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
4535
that->mm.vdDiskIfaces);
4536
if (RT_SUCCESS (vrc))
4537
vrc = VDClose (hdd, true /* fDelete */);
4539
if (RT_FAILURE (vrc))
4541
throw setError (E_FAIL,
4542
tr ("Could not delete the hard disk storage unit '%s'%s"),
4543
location.raw(), that->vdError (vrc).raw());
4547
catch (HRESULT aRC) { rc = aRC; }
4551
catch (HRESULT aRC) { rc = aRC; }
4553
thatLock.maybeEnter();
4555
/* go to the NotCreated state even on failure since the storage
4556
* may have been already partially deleted and cannot be used any
4557
* more. One will be able to manually re-open the storage if really
4558
* needed to re-register it. */
4559
that->m.state = MediaState_NotCreated;
4561
/* Reset UUID to prevent Create* from reusing it again */
4562
unconst (that->m.id).clear();
4569
/* The lock is also used as a signal from the task initiator (which
4570
* releases it only after RTThreadCreate()) that we can start the job */
4571
AutoWriteLock thatLock (that);
4573
/// @todo Below we use a pair of delete/create operations to reset
4574
/// the diff contents but the most efficient way will of course be
4575
/// to add a VDResetDiff() API call
4577
uint64_t size = 0, logicalSize = 0;
4582
int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
4583
ComAssertRCThrow (vrc, E_FAIL);
4585
Guid id = that->m.id;
4586
Utf8Str format (that->mm.format);
4587
Utf8Str location (that->m.locationFull);
4589
Guid parentId = that->mParent->m.id;
4590
Utf8Str parentFormat (that->mParent->mm.format);
4591
Utf8Str parentLocation (that->mParent->m.locationFull);
4593
Assert (that->m.state == MediaState_LockedWrite);
4595
/* unlock before the potentially lengthy operation */
4600
/* first, delete the storage unit */
4601
vrc = VDOpen (hdd, format, location,
4602
VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
4603
that->mm.vdDiskIfaces);
4604
if (RT_SUCCESS (vrc))
4605
vrc = VDClose (hdd, true /* fDelete */);
4607
if (RT_FAILURE (vrc))
4609
throw setError (E_FAIL,
4610
tr ("Could not delete the hard disk storage unit '%s'%s"),
4611
location.raw(), that->vdError (vrc).raw());
4614
/* next, create it again */
4615
vrc = VDOpen (hdd, parentFormat, parentLocation,
4616
VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
4617
that->mm.vdDiskIfaces);
4618
if (RT_FAILURE (vrc))
4620
throw setError (E_FAIL,
4621
tr ("Could not open the hard disk storage unit '%s'%s"),
4622
parentLocation.raw(), that->vdError (vrc).raw());
4625
/* needed for vdProgressCallback */
4626
that->mm.vdProgress = task->progress;
4628
vrc = VDCreateDiff (hdd, format, location,
4629
/// @todo use the same image variant as before
4630
VD_IMAGE_FLAGS_NONE,
4633
VD_OPEN_FLAGS_NORMAL,
4634
that->mm.vdDiskIfaces,
4635
that->mm.vdDiskIfaces);
4637
that->mm.vdProgress = NULL;
4639
if (RT_FAILURE (vrc))
4641
throw setError (E_FAIL,
4642
tr ("Could not create the differencing hard disk storage unit '%s'%s"),
4643
location.raw(), that->vdError (vrc).raw());
4646
size = VDGetFileSize (hdd, 1);
4647
logicalSize = VDGetSize (hdd, 1) / _1M;
4649
catch (HRESULT aRC) { rc = aRC; }
4653
catch (HRESULT aRC) { rc = aRC; }
4657
that->m.size = size;
4658
that->mm.logicalSize = logicalSize;
4662
/* unlock ourselves when done */
4663
HRESULT rc2 = that->UnlockWrite (NULL);
4667
/* Note that in sync mode, it's the caller's responsibility to
4668
* unlock the hard disk */
4673
////////////////////////////////////////////////////////////////////////
4677
/* Lock all in {parent,child} order. The lock is also used as a
4678
* signal from the task initiator (which releases it only after
4679
* RTThreadCreate()) that we can start the job. */
4680
AutoWriteLock thatLock (that);
4682
ImageChain *imgChain = task->d.images.get();
4687
int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
4688
ComAssertRCThrow (vrc, E_FAIL);
4692
/* Open all hard disk images in the chain. */
4693
List::const_iterator last = imgChain->end();
4695
for (List::const_iterator it = imgChain->begin();
4696
it != imgChain->end(); ++ it)
4700
Assert ((*it)->m.state == MediaState_LockedWrite);
4702
Assert ((*it)->m.state == MediaState_LockedRead);
4704
/** Open all images but last in read-only mode. */
4705
vrc = VDOpen (hdd, Utf8Str ((*it)->mm.format),
4706
Utf8Str ((*it)->m.locationFull),
4707
(it == last) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
4708
(*it)->mm.vdDiskIfaces);
4709
if (RT_FAILURE (vrc))
4711
throw setError (E_FAIL,
4712
tr ("Could not open the hard disk storage unit '%s'%s"),
4713
Utf8Str ((*it)->m.locationFull).raw(),
4714
that->vdError (vrc).raw());
4718
/* unlock before the potentially lengthy operation */
4721
Assert (that->m.state == MediaState_LockedWrite);
4723
/* needed for vdProgressCallback */
4724
that->mm.vdProgress = task->progress;
4726
vrc = VDCompact (hdd, VD_LAST_IMAGE, that->mm.vdDiskIfaces);
4728
that->mm.vdProgress = NULL;
4730
if (RT_FAILURE (vrc))
4732
if (vrc == VERR_NOT_SUPPORTED)
4733
throw setError(VBOX_E_NOT_SUPPORTED,
4734
tr("Compacting is not supported yet for hard disk '%s'"),
4735
Utf8Str (that->m.locationFull).raw());
4736
else if (vrc == VERR_NOT_IMPLEMENTED)
4737
throw setError(E_NOTIMPL,
4738
tr("Compacting is not implemented, hard disk '%s'"),
4739
Utf8Str (that->m.locationFull).raw());
4741
throw setError (E_FAIL,
4742
tr ("Could not compact hard disk '%s'%s"),
4743
Utf8Str (that->m.locationFull).raw(),
4744
that->vdError (vrc).raw());
4747
catch (HRESULT aRC) { rc = aRC; }
4751
catch (HRESULT aRC) { rc = aRC; }
4753
/* Everything is explicitly unlocked when the task exits,
4754
* as the task destruction also destroys the image chain. */
4760
AssertFailedReturn (VERR_GENERAL_FAILURE);
4763
/* complete the progress if run asynchronously */
4766
if (!task->progress.isNull())
4767
task->progress->notifyComplete (rc);
4774
LogFlowFunc (("rc=%Rhrc\n", rc));
4777
return VINF_SUCCESS;
4779
/// @todo ugly hack, fix ComAssert... later
4782
/* vi: set tabstop=4 shiftwidth=4 expandtab: */