VirtualBox

source: vbox/trunk/src/VBox/Main/MediumImpl.cpp@ 31570

Last change on this file since 31570 was 31570, checked in by vboxsync, 14 years ago

Main: fix for missing UUIDs for dvd and floppy media

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 212.0 KB
Line 
1/* $Id: MediumImpl.cpp 31570 2010-08-11 13:42:06Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2008-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "MediumImpl.h"
19#include "ProgressImpl.h"
20#include "SystemPropertiesImpl.h"
21#include "VirtualBoxImpl.h"
22
23#include "AutoCaller.h"
24#include "Logging.h"
25
26#include <VBox/com/array.h>
27#include "VBox/com/MultiResult.h"
28#include "VBox/com/ErrorInfo.h"
29
30#include <VBox/err.h>
31#include <VBox/settings.h>
32
33#include <iprt/param.h>
34#include <iprt/path.h>
35#include <iprt/file.h>
36#include <iprt/tcp.h>
37#include <iprt/cpp/utils.h>
38
39#include <VBox/VBoxHDD.h>
40
41#include <algorithm>
42
43////////////////////////////////////////////////////////////////////////////////
44//
45// Medium data definition
46//
47////////////////////////////////////////////////////////////////////////////////
48
49/** Describes how a machine refers to this medium. */
50struct BackRef
51{
52 /** Equality predicate for stdc++. */
53 struct EqualsTo : public std::unary_function <BackRef, bool>
54 {
55 explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
56
57 bool operator()(const argument_type &aThat) const
58 {
59 return aThat.machineId == machineId;
60 }
61
62 const Guid machineId;
63 };
64
65 typedef std::list<Guid> GuidList;
66
67 BackRef(const Guid &aMachineId,
68 const Guid &aSnapshotId = Guid::Empty)
69 : machineId(aMachineId),
70 fInCurState(aSnapshotId.isEmpty())
71 {
72 if (!aSnapshotId.isEmpty())
73 llSnapshotIds.push_back(aSnapshotId);
74 }
75
76 Guid machineId;
77 bool fInCurState : 1;
78 GuidList llSnapshotIds;
79};
80
81typedef std::list<BackRef> BackRefList;
82
83struct Medium::Data
84{
85 Data()
86 : pVirtualBox(NULL),
87 state(MediumState_NotCreated),
88 variant(MediumVariant_Standard),
89 size(0),
90 readers(0),
91 preLockState(MediumState_NotCreated),
92 queryInfoSem(NIL_RTSEMEVENTMULTI),
93 queryInfoRunning(false),
94 type(MediumType_Normal),
95 devType(DeviceType_HardDisk),
96 logicalSize(0),
97 hddOpenMode(OpenReadWrite),
98 autoReset(false),
99 hostDrive(false),
100 implicit(false),
101 numCreateDiffTasks(0),
102 vdDiskIfaces(NULL)
103 { }
104
105 /** weak VirtualBox parent */
106 VirtualBox * const pVirtualBox;
107
108 // pParent and llChildren are protected by VirtualBox::getMediaTreeLockHandle()
109 ComObjPtr<Medium> pParent;
110 MediaList llChildren; // to add a child, just call push_back; to remove a child, call child->deparent() which does a lookup
111
112 Guid uuidRegistryMachine; // machine in whose registry this medium is listed or NULL; see getRegistryMachine()
113
114 const Guid id;
115 Utf8Str strDescription;
116 MediumState_T state;
117 MediumVariant_T variant;
118 Utf8Str strLocation;
119 Utf8Str strLocationFull;
120 uint64_t size;
121 Utf8Str strLastAccessError;
122
123 BackRefList backRefs;
124
125 size_t readers;
126 MediumState_T preLockState;
127
128 RTSEMEVENTMULTI queryInfoSem;
129 bool queryInfoRunning : 1;
130
131 const Utf8Str strFormat;
132 ComObjPtr<MediumFormat> formatObj;
133
134 MediumType_T type;
135 DeviceType_T devType;
136 uint64_t logicalSize; /*< In MBytes. */
137
138 HDDOpenMode hddOpenMode;
139
140 bool autoReset : 1;
141
142 /** the following members are invalid after changing UUID on open */
143 const Guid uuidImage;
144 const Guid uuidParentImage;
145
146 bool hostDrive : 1;
147
148 settings::StringsMap mapProperties;
149
150 bool implicit : 1;
151
152 uint32_t numCreateDiffTasks;
153
154 Utf8Str vdError; /*< Error remembered by the VD error callback. */
155
156 VDINTERFACE vdIfError;
157 VDINTERFACEERROR vdIfCallsError;
158
159 VDINTERFACE vdIfConfig;
160 VDINTERFACECONFIG vdIfCallsConfig;
161
162 VDINTERFACE vdIfTcpNet;
163 VDINTERFACETCPNET vdIfCallsTcpNet;
164
165 PVDINTERFACE vdDiskIfaces;
166};
167
168typedef struct VDSOCKETINT
169{
170 /** Socket handle. */
171 RTSOCKET hSocket;
172} VDSOCKETINT, *PVDSOCKETINT;
173
174////////////////////////////////////////////////////////////////////////////////
175//
176// Globals
177//
178////////////////////////////////////////////////////////////////////////////////
179
180/**
181 * Medium::Task class for asynchronous operations.
182 *
183 * @note Instances of this class must be created using new() because the
184 * task thread function will delete them when the task is complete.
185 *
186 * @note The constructor of this class adds a caller on the managed Medium
187 * object which is automatically released upon destruction.
188 */
189class Medium::Task
190{
191public:
192 Task(Medium *aMedium, Progress *aProgress)
193 : mVDOperationIfaces(NULL),
194 m_pfNeedsSaveSettings(NULL),
195 mMedium(aMedium),
196 mMediumCaller(aMedium),
197 mThread(NIL_RTTHREAD),
198 mProgress(aProgress)
199 {
200 AssertReturnVoidStmt(aMedium, mRC = E_FAIL);
201 mRC = mMediumCaller.rc();
202 if (FAILED(mRC))
203 return;
204
205 /* Set up a per-operation progress interface, can be used freely (for
206 * binary operations you can use it either on the source or target). */
207 mVDIfCallsProgress.cbSize = sizeof(VDINTERFACEPROGRESS);
208 mVDIfCallsProgress.enmInterface = VDINTERFACETYPE_PROGRESS;
209 mVDIfCallsProgress.pfnProgress = vdProgressCall;
210 int vrc = VDInterfaceAdd(&mVDIfProgress,
211 "Medium::Task::vdInterfaceProgress",
212 VDINTERFACETYPE_PROGRESS,
213 &mVDIfCallsProgress,
214 mProgress,
215 &mVDOperationIfaces);
216 AssertRC(vrc);
217 if (RT_FAILURE(vrc))
218 mRC = E_FAIL;
219 }
220
221 // Make all destructors virtual. Just in case.
222 virtual ~Task()
223 {}
224
225 HRESULT rc() const { return mRC; }
226 bool isOk() const { return SUCCEEDED(rc()); }
227
228 static int fntMediumTask(RTTHREAD aThread, void *pvUser);
229
230 bool isAsync() { return mThread != NIL_RTTHREAD; }
231
232 PVDINTERFACE mVDOperationIfaces;
233
234 // Whether the caller needs to call VirtualBox::saveSettings() after
235 // the task function returns. Only used in synchronous (wait) mode;
236 // otherwise the task will save the settings itself.
237 bool *m_pfNeedsSaveSettings;
238
239 const ComObjPtr<Medium> mMedium;
240 AutoCaller mMediumCaller;
241
242 friend HRESULT Medium::runNow(Medium::Task*, bool*);
243
244protected:
245 HRESULT mRC;
246 RTTHREAD mThread;
247
248private:
249 virtual HRESULT handler() = 0;
250
251 const ComObjPtr<Progress> mProgress;
252
253 static DECLCALLBACK(int) vdProgressCall(void *pvUser, unsigned uPercent);
254
255 VDINTERFACE mVDIfProgress;
256 VDINTERFACEPROGRESS mVDIfCallsProgress;
257};
258
259class Medium::CreateBaseTask : public Medium::Task
260{
261public:
262 CreateBaseTask(Medium *aMedium,
263 Progress *aProgress,
264 uint64_t aSize,
265 MediumVariant_T aVariant)
266 : Medium::Task(aMedium, aProgress),
267 mSize(aSize),
268 mVariant(aVariant)
269 {}
270
271 uint64_t mSize;
272 MediumVariant_T mVariant;
273
274private:
275 virtual HRESULT handler();
276};
277
278class Medium::CreateDiffTask : public Medium::Task
279{
280public:
281 CreateDiffTask(Medium *aMedium,
282 Progress *aProgress,
283 Medium *aTarget,
284 MediumVariant_T aVariant,
285 MediumLockList *aMediumLockList,
286 bool fKeepMediumLockList = false)
287 : Medium::Task(aMedium, aProgress),
288 mpMediumLockList(aMediumLockList),
289 mTarget(aTarget),
290 mVariant(aVariant),
291 mTargetCaller(aTarget),
292 mfKeepMediumLockList(fKeepMediumLockList)
293 {
294 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
295 mRC = mTargetCaller.rc();
296 if (FAILED(mRC))
297 return;
298 }
299
300 ~CreateDiffTask()
301 {
302 if (!mfKeepMediumLockList && mpMediumLockList)
303 delete mpMediumLockList;
304 }
305
306 MediumLockList *mpMediumLockList;
307
308 const ComObjPtr<Medium> mTarget;
309 MediumVariant_T mVariant;
310
311private:
312 virtual HRESULT handler();
313
314 AutoCaller mTargetCaller;
315 bool mfKeepMediumLockList;
316};
317
318class Medium::CloneTask : public Medium::Task
319{
320public:
321 CloneTask(Medium *aMedium,
322 Progress *aProgress,
323 Medium *aTarget,
324 MediumVariant_T aVariant,
325 Medium *aParent,
326 MediumLockList *aSourceMediumLockList,
327 MediumLockList *aTargetMediumLockList,
328 bool fKeepSourceMediumLockList = false,
329 bool fKeepTargetMediumLockList = false)
330 : Medium::Task(aMedium, aProgress),
331 mTarget(aTarget),
332 mParent(aParent),
333 mpSourceMediumLockList(aSourceMediumLockList),
334 mpTargetMediumLockList(aTargetMediumLockList),
335 mVariant(aVariant),
336 mTargetCaller(aTarget),
337 mParentCaller(aParent),
338 mfKeepSourceMediumLockList(fKeepSourceMediumLockList),
339 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
340 {
341 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
342 mRC = mTargetCaller.rc();
343 if (FAILED(mRC))
344 return;
345 /* aParent may be NULL */
346 mRC = mParentCaller.rc();
347 if (FAILED(mRC))
348 return;
349 AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
350 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
351 }
352
353 ~CloneTask()
354 {
355 if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
356 delete mpSourceMediumLockList;
357 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
358 delete mpTargetMediumLockList;
359 }
360
361 const ComObjPtr<Medium> mTarget;
362 const ComObjPtr<Medium> mParent;
363 MediumLockList *mpSourceMediumLockList;
364 MediumLockList *mpTargetMediumLockList;
365 MediumVariant_T mVariant;
366
367private:
368 virtual HRESULT handler();
369
370 AutoCaller mTargetCaller;
371 AutoCaller mParentCaller;
372 bool mfKeepSourceMediumLockList;
373 bool mfKeepTargetMediumLockList;
374};
375
376class Medium::CompactTask : public Medium::Task
377{
378public:
379 CompactTask(Medium *aMedium,
380 Progress *aProgress,
381 MediumLockList *aMediumLockList,
382 bool fKeepMediumLockList = false)
383 : Medium::Task(aMedium, aProgress),
384 mpMediumLockList(aMediumLockList),
385 mfKeepMediumLockList(fKeepMediumLockList)
386 {
387 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
388 }
389
390 ~CompactTask()
391 {
392 if (!mfKeepMediumLockList && mpMediumLockList)
393 delete mpMediumLockList;
394 }
395
396 MediumLockList *mpMediumLockList;
397
398private:
399 virtual HRESULT handler();
400
401 bool mfKeepMediumLockList;
402};
403
404class Medium::ResetTask : public Medium::Task
405{
406public:
407 ResetTask(Medium *aMedium,
408 Progress *aProgress,
409 MediumLockList *aMediumLockList,
410 bool fKeepMediumLockList = false)
411 : Medium::Task(aMedium, aProgress),
412 mpMediumLockList(aMediumLockList),
413 mfKeepMediumLockList(fKeepMediumLockList)
414 {}
415
416 ~ResetTask()
417 {
418 if (!mfKeepMediumLockList && mpMediumLockList)
419 delete mpMediumLockList;
420 }
421
422 MediumLockList *mpMediumLockList;
423
424private:
425 virtual HRESULT handler();
426
427 bool mfKeepMediumLockList;
428};
429
430class Medium::DeleteTask : public Medium::Task
431{
432public:
433 DeleteTask(Medium *aMedium,
434 Progress *aProgress,
435 MediumLockList *aMediumLockList,
436 bool fKeepMediumLockList = false)
437 : Medium::Task(aMedium, aProgress),
438 mpMediumLockList(aMediumLockList),
439 mfKeepMediumLockList(fKeepMediumLockList)
440 {}
441
442 ~DeleteTask()
443 {
444 if (!mfKeepMediumLockList && mpMediumLockList)
445 delete mpMediumLockList;
446 }
447
448 MediumLockList *mpMediumLockList;
449
450private:
451 virtual HRESULT handler();
452
453 bool mfKeepMediumLockList;
454};
455
456class Medium::MergeTask : public Medium::Task
457{
458public:
459 MergeTask(Medium *aMedium,
460 Medium *aTarget,
461 bool fMergeForward,
462 Medium *aParentForTarget,
463 const MediaList &aChildrenToReparent,
464 Progress *aProgress,
465 MediumLockList *aMediumLockList,
466 bool fKeepMediumLockList = false)
467 : Medium::Task(aMedium, aProgress),
468 mTarget(aTarget),
469 mfMergeForward(fMergeForward),
470 mParentForTarget(aParentForTarget),
471 mChildrenToReparent(aChildrenToReparent),
472 mpMediumLockList(aMediumLockList),
473 mTargetCaller(aTarget),
474 mParentForTargetCaller(aParentForTarget),
475 mfChildrenCaller(false),
476 mfKeepMediumLockList(fKeepMediumLockList)
477 {
478 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
479 for (MediaList::const_iterator it = mChildrenToReparent.begin();
480 it != mChildrenToReparent.end();
481 ++it)
482 {
483 HRESULT rc2 = (*it)->addCaller();
484 if (FAILED(rc2))
485 {
486 mRC = E_FAIL;
487 for (MediaList::const_iterator it2 = mChildrenToReparent.begin();
488 it2 != it;
489 --it2)
490 {
491 (*it2)->releaseCaller();
492 }
493 return;
494 }
495 }
496 mfChildrenCaller = true;
497 }
498
499 ~MergeTask()
500 {
501 if (!mfKeepMediumLockList && mpMediumLockList)
502 delete mpMediumLockList;
503 if (mfChildrenCaller)
504 {
505 for (MediaList::const_iterator it = mChildrenToReparent.begin();
506 it != mChildrenToReparent.end();
507 ++it)
508 {
509 (*it)->releaseCaller();
510 }
511 }
512 }
513
514 const ComObjPtr<Medium> mTarget;
515 bool mfMergeForward;
516 /* When mChildrenToReparent is empty then mParentForTarget is non-null.
517 * In other words: they are used in different cases. */
518 const ComObjPtr<Medium> mParentForTarget;
519 MediaList mChildrenToReparent;
520 MediumLockList *mpMediumLockList;
521
522private:
523 virtual HRESULT handler();
524
525 AutoCaller mTargetCaller;
526 AutoCaller mParentForTargetCaller;
527 bool mfChildrenCaller;
528 bool mfKeepMediumLockList;
529};
530
531/**
532 * Thread function for time-consuming medium tasks.
533 *
534 * @param pvUser Pointer to the Medium::Task instance.
535 */
536/* static */
537DECLCALLBACK(int) Medium::Task::fntMediumTask(RTTHREAD aThread, void *pvUser)
538{
539 LogFlowFuncEnter();
540 AssertReturn(pvUser, (int)E_INVALIDARG);
541 Medium::Task *pTask = static_cast<Medium::Task *>(pvUser);
542
543 pTask->mThread = aThread;
544
545 HRESULT rc = pTask->handler();
546
547 /* complete the progress if run asynchronously */
548 if (pTask->isAsync())
549 {
550 if (!pTask->mProgress.isNull())
551 pTask->mProgress->notifyComplete(rc);
552 }
553
554 /* pTask is no longer needed, delete it. */
555 delete pTask;
556
557 LogFlowFunc(("rc=%Rhrc\n", rc));
558 LogFlowFuncLeave();
559
560 return (int)rc;
561}
562
563/**
564 * PFNVDPROGRESS callback handler for Task operations.
565 *
566 * @param pvUser Pointer to the Progress instance.
567 * @param uPercent Completetion precentage (0-100).
568 */
569/*static*/
570DECLCALLBACK(int) Medium::Task::vdProgressCall(void *pvUser, unsigned uPercent)
571{
572 Progress *that = static_cast<Progress *>(pvUser);
573
574 if (that != NULL)
575 {
576 /* update the progress object, capping it at 99% as the final percent
577 * is used for additional operations like setting the UUIDs and similar. */
578 HRESULT rc = that->SetCurrentOperationProgress(uPercent * 99 / 100);
579 if (FAILED(rc))
580 {
581 if (rc == E_FAIL)
582 return VERR_CANCELLED;
583 else
584 return VERR_INVALID_STATE;
585 }
586 }
587
588 return VINF_SUCCESS;
589}
590
591/**
592 * Implementation code for the "create base" task.
593 */
594HRESULT Medium::CreateBaseTask::handler()
595{
596 return mMedium->taskCreateBaseHandler(*this);
597}
598
599/**
600 * Implementation code for the "create diff" task.
601 */
602HRESULT Medium::CreateDiffTask::handler()
603{
604 return mMedium->taskCreateDiffHandler(*this);
605}
606
607/**
608 * Implementation code for the "clone" task.
609 */
610HRESULT Medium::CloneTask::handler()
611{
612 return mMedium->taskCloneHandler(*this);
613}
614
615/**
616 * Implementation code for the "compact" task.
617 */
618HRESULT Medium::CompactTask::handler()
619{
620 return mMedium->taskCompactHandler(*this);
621}
622
623/**
624 * Implementation code for the "reset" task.
625 */
626HRESULT Medium::ResetTask::handler()
627{
628 return mMedium->taskResetHandler(*this);
629}
630
631/**
632 * Implementation code for the "delete" task.
633 */
634HRESULT Medium::DeleteTask::handler()
635{
636 return mMedium->taskDeleteHandler(*this);
637}
638
639/**
640 * Implementation code for the "merge" task.
641 */
642HRESULT Medium::MergeTask::handler()
643{
644 return mMedium->taskMergeHandler(*this);
645}
646
647
648////////////////////////////////////////////////////////////////////////////////
649//
650// Medium constructor / destructor
651//
652////////////////////////////////////////////////////////////////////////////////
653
654DEFINE_EMPTY_CTOR_DTOR(Medium)
655
656HRESULT Medium::FinalConstruct()
657{
658 m = new Data;
659
660 /* Initialize the callbacks of the VD error interface */
661 m->vdIfCallsError.cbSize = sizeof(VDINTERFACEERROR);
662 m->vdIfCallsError.enmInterface = VDINTERFACETYPE_ERROR;
663 m->vdIfCallsError.pfnError = vdErrorCall;
664 m->vdIfCallsError.pfnMessage = NULL;
665
666 /* Initialize the callbacks of the VD config interface */
667 m->vdIfCallsConfig.cbSize = sizeof(VDINTERFACECONFIG);
668 m->vdIfCallsConfig.enmInterface = VDINTERFACETYPE_CONFIG;
669 m->vdIfCallsConfig.pfnAreKeysValid = vdConfigAreKeysValid;
670 m->vdIfCallsConfig.pfnQuerySize = vdConfigQuerySize;
671 m->vdIfCallsConfig.pfnQuery = vdConfigQuery;
672
673 /* Initialize the callbacks of the VD TCP interface (we always use the host
674 * IP stack for now) */
675 m->vdIfCallsTcpNet.cbSize = sizeof(VDINTERFACETCPNET);
676 m->vdIfCallsTcpNet.enmInterface = VDINTERFACETYPE_TCPNET;
677 m->vdIfCallsTcpNet.pfnSocketCreate = vdTcpSocketCreate;
678 m->vdIfCallsTcpNet.pfnSocketDestroy = vdTcpSocketDestroy;
679 m->vdIfCallsTcpNet.pfnClientConnect = vdTcpClientConnect;
680 m->vdIfCallsTcpNet.pfnClientClose = vdTcpClientClose;
681 m->vdIfCallsTcpNet.pfnIsClientConnected = vdTcpIsClientConnected;
682 m->vdIfCallsTcpNet.pfnSelectOne = vdTcpSelectOne;
683 m->vdIfCallsTcpNet.pfnRead = vdTcpRead;
684 m->vdIfCallsTcpNet.pfnWrite = vdTcpWrite;
685 m->vdIfCallsTcpNet.pfnSgWrite = vdTcpSgWrite;
686 m->vdIfCallsTcpNet.pfnFlush = vdTcpFlush;
687 m->vdIfCallsTcpNet.pfnSetSendCoalescing = vdTcpSetSendCoalescing;
688 m->vdIfCallsTcpNet.pfnGetLocalAddress = vdTcpGetLocalAddress;
689 m->vdIfCallsTcpNet.pfnGetPeerAddress = vdTcpGetPeerAddress;
690 m->vdIfCallsTcpNet.pfnSelectOneEx = NULL;
691 m->vdIfCallsTcpNet.pfnPoke = NULL;
692
693 /* Initialize the per-disk interface chain */
694 int vrc;
695 vrc = VDInterfaceAdd(&m->vdIfError,
696 "Medium::vdInterfaceError",
697 VDINTERFACETYPE_ERROR,
698 &m->vdIfCallsError, this, &m->vdDiskIfaces);
699 AssertRCReturn(vrc, E_FAIL);
700
701 vrc = VDInterfaceAdd(&m->vdIfConfig,
702 "Medium::vdInterfaceConfig",
703 VDINTERFACETYPE_CONFIG,
704 &m->vdIfCallsConfig, this, &m->vdDiskIfaces);
705 AssertRCReturn(vrc, E_FAIL);
706
707 vrc = VDInterfaceAdd(&m->vdIfTcpNet,
708 "Medium::vdInterfaceTcpNet",
709 VDINTERFACETYPE_TCPNET,
710 &m->vdIfCallsTcpNet, this, &m->vdDiskIfaces);
711 AssertRCReturn(vrc, E_FAIL);
712
713 vrc = RTSemEventMultiCreate(&m->queryInfoSem);
714 AssertRCReturn(vrc, E_FAIL);
715 vrc = RTSemEventMultiSignal(m->queryInfoSem);
716 AssertRCReturn(vrc, E_FAIL);
717
718 return S_OK;
719}
720
721void Medium::FinalRelease()
722{
723 uninit();
724
725 delete m;
726}
727
728/**
729 * Initializes an empty hard disk object without creating or opening an associated
730 * storage unit.
731 *
732 * This gets called by VirtualBox::CreateHardDisk().
733 *
734 * For hard disks that don't have the VD_CAP_CREATE_FIXED or
735 * VD_CAP_CREATE_DYNAMIC capability (and therefore cannot be created or deleted
736 * with the means of VirtualBox) the associated storage unit is assumed to be
737 * ready for use so the state of the hard disk object will be set to Created.
738 *
739 * @param aVirtualBox VirtualBox object.
740 * @param aLocation Storage unit location.
741 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
742 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
743 */
744HRESULT Medium::init(VirtualBox *aVirtualBox,
745 const Utf8Str &aFormat,
746 const Utf8Str &aLocation,
747 bool *pfNeedsSaveSettings)
748{
749 AssertReturn(aVirtualBox != NULL, E_FAIL);
750 AssertReturn(!aFormat.isEmpty(), E_FAIL);
751
752 /* Enclose the state transition NotReady->InInit->Ready */
753 AutoInitSpan autoInitSpan(this);
754 AssertReturn(autoInitSpan.isOk(), E_FAIL);
755
756 HRESULT rc = S_OK;
757
758 /* share VirtualBox weakly (parent remains NULL so far) */
759 unconst(m->pVirtualBox) = aVirtualBox;
760
761 /* no storage yet */
762 m->state = MediumState_NotCreated;
763
764 /* cannot be a host drive */
765 m->hostDrive = false;
766
767 /* No storage unit is created yet, no need to queryInfo() */
768
769 rc = setFormat(aFormat);
770 if (FAILED(rc)) return rc;
771
772 if (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
773 {
774 rc = setLocation(aLocation);
775 if (FAILED(rc)) return rc;
776 }
777 else
778 {
779 rc = setLocation(aLocation);
780 if (FAILED(rc)) return rc;
781 }
782
783 if (!(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateFixed
784 | MediumFormatCapabilities_CreateDynamic))
785 )
786 {
787 /* storage for hard disks of this format can neither be explicitly
788 * created by VirtualBox nor deleted, so we place the hard disk to
789 * Created state here and also add it to the registry */
790 m->state = MediumState_Created;
791 // create new UUID
792 unconst(m->id).create();
793
794 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
795 rc = m->pVirtualBox->registerHardDisk(this, pfNeedsSaveSettings);
796 }
797
798 /* Confirm a successful initialization when it's the case */
799 if (SUCCEEDED(rc))
800 autoInitSpan.setSucceeded();
801
802 return rc;
803}
804
805/**
806 * Initializes the medium object by opening the storage unit at the specified
807 * location. The enOpenMode parameter defines whether the medium will be opened
808 * read/write or read-only.
809 *
810 * This gets called by VirtualBox::OpenMedium() and also by
811 * Machine::AttachDevice() and createImplicitDiffs() when new diff
812 * images are created.
813 *
814 * For hard disks, the UUID, format and the parent of this medium will be
815 * determined when reading the medium storage unit. For DVD and floppy images,
816 * which have no UUIDs in their storage units, new UUIDs are created.
817 * If the detected or set parent is not known to VirtualBox, then this method
818 * will fail.
819 *
820 * @param aVirtualBox VirtualBox object.
821 * @param aLocation Storage unit location.
822 * @param enOpenMode Whether to open the medium read/write or read-only.
823 * @param aDeviceType Device type of medium.
824 */
825HRESULT Medium::init(VirtualBox *aVirtualBox,
826 const Utf8Str &aLocation,
827 HDDOpenMode enOpenMode,
828 DeviceType_T aDeviceType)
829{
830 AssertReturn(aVirtualBox, E_INVALIDARG);
831 AssertReturn(!aLocation.isEmpty(), E_INVALIDARG);
832
833 /* Enclose the state transition NotReady->InInit->Ready */
834 AutoInitSpan autoInitSpan(this);
835 AssertReturn(autoInitSpan.isOk(), E_FAIL);
836
837 HRESULT rc = S_OK;
838
839 /* share VirtualBox weakly (parent remains NULL so far) */
840 unconst(m->pVirtualBox) = aVirtualBox;
841
842 /* there must be a storage unit */
843 m->state = MediumState_Created;
844
845 /* remember device type for correct unregistering later */
846 m->devType = aDeviceType;
847
848 /* cannot be a host drive */
849 m->hostDrive = false;
850
851 /* remember the open mode (defaults to ReadWrite) */
852 m->hddOpenMode = enOpenMode;
853
854 if (aDeviceType == DeviceType_HardDisk)
855 rc = setLocation(aLocation);
856 else
857 rc = setLocation(aLocation, "RAW");
858 if (FAILED(rc)) return rc;
859
860 if ( aDeviceType == DeviceType_DVD
861 || aDeviceType == DeviceType_Floppy)
862 // create new UUID
863 unconst(m->id).create();
864
865 /* get all the information about the medium from the storage unit */
866 rc = queryInfo(false /* fSetImageId */, false /* fSetParentId */);
867
868 if (SUCCEEDED(rc))
869 {
870 /* if the storage unit is not accessible, it's not acceptable for the
871 * newly opened media so convert this into an error */
872 if (m->state == MediumState_Inaccessible)
873 {
874 Assert(!m->strLastAccessError.isEmpty());
875 rc = setError(E_FAIL, "%s", m->strLastAccessError.c_str());
876 }
877 else
878 {
879 AssertReturn(!m->id.isEmpty(), E_FAIL);
880
881 /* storage format must be detected by queryInfo() if the medium is accessible */
882 AssertReturn(!m->strFormat.isEmpty(), E_FAIL);
883 }
884 }
885
886 /* Confirm a successful initialization when it's the case */
887 if (SUCCEEDED(rc))
888 autoInitSpan.setSucceeded();
889
890 return rc;
891}
892
893/**
894 * Initializes the medium object by loading its data from the given settings
895 * node. In this mode, the medium will always be opened read/write.
896 *
897 * @param aVirtualBox VirtualBox object.
898 * @param aParent Parent medium disk or NULL for a root (base) medium.
899 * @param aDeviceType Device type of the medium.
900 * @param aNode Configuration settings.
901 *
902 * @note Locks VirtualBox for writing, the medium tree for writing.
903 */
904HRESULT Medium::init(VirtualBox *aVirtualBox,
905 Medium *aParent,
906 DeviceType_T aDeviceType,
907 const settings::Medium &data)
908{
909 using namespace settings;
910
911 AssertReturn(aVirtualBox, E_INVALIDARG);
912
913 /* Enclose the state transition NotReady->InInit->Ready */
914 AutoInitSpan autoInitSpan(this);
915 AssertReturn(autoInitSpan.isOk(), E_FAIL);
916
917 HRESULT rc = S_OK;
918
919 /* share VirtualBox and parent weakly */
920 unconst(m->pVirtualBox) = aVirtualBox;
921
922 /* register with VirtualBox/parent early, since uninit() will
923 * unconditionally unregister on failure */
924 if (aParent)
925 {
926 // differencing medium: add to parent
927 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
928 m->pParent = aParent;
929 aParent->m->llChildren.push_back(this);
930 }
931
932 /* see below why we don't call queryInfo() (and therefore treat the medium
933 * as inaccessible for now */
934 m->state = MediumState_Inaccessible;
935 m->strLastAccessError = tr("Accessibility check was not yet performed");
936
937 /* required */
938 unconst(m->id) = data.uuid;
939
940 /* assume not a host drive */
941 m->hostDrive = false;
942
943 /* optional */
944 m->strDescription = data.strDescription;
945
946 /* required */
947 if (aDeviceType == DeviceType_HardDisk)
948 {
949 AssertReturn(!data.strFormat.isEmpty(), E_FAIL);
950 rc = setFormat(data.strFormat);
951 if (FAILED(rc)) return rc;
952 }
953 else
954 {
955 /// @todo handle host drive settings here as well?
956 if (!data.strFormat.isEmpty())
957 rc = setFormat(data.strFormat);
958 else
959 rc = setFormat("RAW");
960 if (FAILED(rc)) return rc;
961 }
962
963 /* optional, only for diffs, default is false; we can only auto-reset
964 * diff media so they must have a parent */
965 if (aParent != NULL)
966 m->autoReset = data.fAutoReset;
967 else
968 m->autoReset = false;
969
970 /* properties (after setting the format as it populates the map). Note that
971 * if some properties are not supported but preseint in the settings file,
972 * they will still be read and accessible (for possible backward
973 * compatibility; we can also clean them up from the XML upon next
974 * XML format version change if we wish) */
975 for (settings::StringsMap::const_iterator it = data.properties.begin();
976 it != data.properties.end();
977 ++it)
978 {
979 const Utf8Str &name = it->first;
980 const Utf8Str &value = it->second;
981 m->mapProperties[name] = value;
982 }
983
984 /* required */
985 rc = setLocation(data.strLocation);
986 if (FAILED(rc)) return rc;
987
988 if (aDeviceType == DeviceType_HardDisk)
989 {
990 /* type is only for base hard disks */
991 if (m->pParent.isNull())
992 m->type = data.hdType;
993 }
994 else
995 m->type = MediumType_Writethrough;
996
997 /* remember device type for correct unregistering later */
998 m->devType = aDeviceType;
999
1000 LogFlowThisFunc(("m->strLocationFull='%s', m->strFormat=%s, m->id={%RTuuid}\n",
1001 m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw()));
1002
1003 /* Don't call queryInfo() for registered media to prevent the calling
1004 * thread (i.e. the VirtualBox server startup thread) from an unexpected
1005 * freeze but mark it as initially inaccessible instead. The vital UUID,
1006 * location and format properties are read from the registry file above; to
1007 * get the actual state and the rest of the data, the user will have to call
1008 * COMGETTER(State). */
1009
1010 AutoWriteLock treeLock(aVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1011
1012 /* load all children */
1013 for (settings::MediaList::const_iterator it = data.llChildren.begin();
1014 it != data.llChildren.end();
1015 ++it)
1016 {
1017 const settings::Medium &med = *it;
1018
1019 ComObjPtr<Medium> pHD;
1020 pHD.createObject();
1021 rc = pHD->init(aVirtualBox,
1022 this, // parent
1023 aDeviceType,
1024 med); // child data
1025 if (FAILED(rc)) break;
1026
1027 rc = m->pVirtualBox->registerHardDisk(pHD, NULL /*pfNeedsSaveSettings*/);
1028 if (FAILED(rc)) break;
1029 }
1030
1031 /* Confirm a successful initialization when it's the case */
1032 if (SUCCEEDED(rc))
1033 autoInitSpan.setSucceeded();
1034
1035 return rc;
1036}
1037
1038/**
1039 * Initializes the medium object by providing the host drive information.
1040 * Not used for anything but the host floppy/host DVD case.
1041 *
1042 * @param aVirtualBox VirtualBox object.
1043 * @param aDeviceType Device type of the medium.
1044 * @param aLocation Location of the host drive.
1045 * @param aDescription Comment for this host drive.
1046 *
1047 * @note Locks VirtualBox lock for writing.
1048 */
1049HRESULT Medium::init(VirtualBox *aVirtualBox,
1050 DeviceType_T aDeviceType,
1051 const Utf8Str &aLocation,
1052 const Utf8Str &aDescription /* = Utf8Str::Empty */)
1053{
1054 ComAssertRet(aDeviceType == DeviceType_DVD || aDeviceType == DeviceType_Floppy, E_INVALIDARG);
1055 ComAssertRet(!aLocation.isEmpty(), E_INVALIDARG);
1056
1057 /* Enclose the state transition NotReady->InInit->Ready */
1058 AutoInitSpan autoInitSpan(this);
1059 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1060
1061 /* share VirtualBox weakly (parent remains NULL so far) */
1062 unconst(m->pVirtualBox) = aVirtualBox;
1063
1064 /* fake up a UUID which is unique, but also reproducible */
1065 RTUUID uuid;
1066 RTUuidClear(&uuid);
1067 if (aDeviceType == DeviceType_DVD)
1068 memcpy(&uuid.au8[0], "DVD", 3);
1069 else
1070 memcpy(&uuid.au8[0], "FD", 2);
1071 /* use device name, adjusted to the end of uuid, shortened if necessary */
1072 size_t lenLocation = aLocation.length();
1073 if (lenLocation > 12)
1074 memcpy(&uuid.au8[4], aLocation.c_str() + (lenLocation - 12), 12);
1075 else
1076 memcpy(&uuid.au8[4 + 12 - lenLocation], aLocation.c_str(), lenLocation);
1077 unconst(m->id) = uuid;
1078
1079 m->type = MediumType_Writethrough;
1080 m->devType = aDeviceType;
1081 m->state = MediumState_Created;
1082 m->hostDrive = true;
1083 HRESULT rc = setFormat("RAW");
1084 if (FAILED(rc)) return rc;
1085 rc = setLocation(aLocation);
1086 if (FAILED(rc)) return rc;
1087 m->strDescription = aDescription;
1088
1089/// @todo generate uuid (similarly to host network interface uuid) from location and device type
1090
1091 autoInitSpan.setSucceeded();
1092 return S_OK;
1093}
1094
1095/**
1096 * Uninitializes the instance.
1097 *
1098 * Called either from FinalRelease() or by the parent when it gets destroyed.
1099 *
1100 * @note All children of this medium get uninitialized by calling their
1101 * uninit() methods.
1102 *
1103 * @note Caller must hold the tree lock of the medium tree this medium is on.
1104 */
1105void Medium::uninit()
1106{
1107 /* Enclose the state transition Ready->InUninit->NotReady */
1108 AutoUninitSpan autoUninitSpan(this);
1109 if (autoUninitSpan.uninitDone())
1110 return;
1111
1112 if (!m->formatObj.isNull())
1113 {
1114 /* remove the caller reference we added in setFormat() */
1115 m->formatObj->releaseCaller();
1116 m->formatObj.setNull();
1117 }
1118
1119 if (m->state == MediumState_Deleting)
1120 {
1121 /* we are being uninitialized after've been deleted by merge.
1122 * Reparenting has already been done so don't touch it here (we are
1123 * now orphans and removeDependentChild() will assert) */
1124 Assert(m->pParent.isNull());
1125 }
1126 else
1127 {
1128 MediaList::iterator it;
1129 for (it = m->llChildren.begin();
1130 it != m->llChildren.end();
1131 ++it)
1132 {
1133 Medium *pChild = *it;
1134 pChild->m->pParent.setNull();
1135 pChild->uninit();
1136 }
1137 m->llChildren.clear(); // this unsets all the ComPtrs and probably calls delete
1138
1139 if (m->pParent)
1140 {
1141 // this is a differencing disk: then remove it from the parent's children list
1142 deparent();
1143 }
1144 }
1145
1146 RTSemEventMultiSignal(m->queryInfoSem);
1147 RTSemEventMultiDestroy(m->queryInfoSem);
1148 m->queryInfoSem = NIL_RTSEMEVENTMULTI;
1149
1150 unconst(m->pVirtualBox) = NULL;
1151}
1152
1153/**
1154 * Internal helper that removes "this" from the list of children of its
1155 * parent. Used in uninit() and other places when reparenting is necessary.
1156 *
1157 * The caller must hold the medium tree lock!
1158 */
1159void Medium::deparent()
1160{
1161 MediaList &llParent = m->pParent->m->llChildren;
1162 for (MediaList::iterator it = llParent.begin();
1163 it != llParent.end();
1164 ++it)
1165 {
1166 Medium *pParentsChild = *it;
1167 if (this == pParentsChild)
1168 {
1169 llParent.erase(it);
1170 break;
1171 }
1172 }
1173 m->pParent.setNull();
1174}
1175
1176/**
1177 * Internal helper that removes "this" from the list of children of its
1178 * parent. Used in uninit() and other places when reparenting is necessary.
1179 *
1180 * The caller must hold the medium tree lock!
1181 */
1182void Medium::setParent(const ComObjPtr<Medium> &pParent)
1183{
1184 m->pParent = pParent;
1185 if (pParent)
1186 pParent->m->llChildren.push_back(this);
1187}
1188
1189
1190////////////////////////////////////////////////////////////////////////////////
1191//
1192// IMedium public methods
1193//
1194////////////////////////////////////////////////////////////////////////////////
1195
1196STDMETHODIMP Medium::COMGETTER(Id)(BSTR *aId)
1197{
1198 CheckComArgOutPointerValid(aId);
1199
1200 AutoCaller autoCaller(this);
1201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1202
1203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1204
1205 m->id.toUtf16().cloneTo(aId);
1206
1207 return S_OK;
1208}
1209
1210STDMETHODIMP Medium::COMGETTER(Description)(BSTR *aDescription)
1211{
1212 CheckComArgOutPointerValid(aDescription);
1213
1214 AutoCaller autoCaller(this);
1215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1216
1217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1218
1219 m->strDescription.cloneTo(aDescription);
1220
1221 return S_OK;
1222}
1223
1224STDMETHODIMP Medium::COMSETTER(Description)(IN_BSTR aDescription)
1225{
1226 AutoCaller autoCaller(this);
1227 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1228
1229// AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 /// @todo update m->description and save the global registry (and local
1232 /// registries of portable VMs referring to this medium), this will also
1233 /// require to add the mRegistered flag to data
1234
1235 NOREF(aDescription);
1236
1237 ReturnComNotImplemented();
1238}
1239
1240STDMETHODIMP Medium::COMGETTER(State)(MediumState_T *aState)
1241{
1242 CheckComArgOutPointerValid(aState);
1243
1244 AutoCaller autoCaller(this);
1245 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1246
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248 *aState = m->state;
1249
1250 return S_OK;
1251}
1252
1253STDMETHODIMP Medium::COMGETTER(Variant)(MediumVariant_T *aVariant)
1254{
1255 CheckComArgOutPointerValid(aVariant);
1256
1257 AutoCaller autoCaller(this);
1258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1259
1260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1261 *aVariant = m->variant;
1262
1263 return S_OK;
1264}
1265
1266
1267STDMETHODIMP Medium::COMGETTER(Location)(BSTR *aLocation)
1268{
1269 CheckComArgOutPointerValid(aLocation);
1270
1271 AutoCaller autoCaller(this);
1272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1273
1274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 m->strLocationFull.cloneTo(aLocation);
1277
1278 return S_OK;
1279}
1280
1281STDMETHODIMP Medium::COMSETTER(Location)(IN_BSTR aLocation)
1282{
1283 CheckComArgStrNotEmptyOrNull(aLocation);
1284
1285 AutoCaller autoCaller(this);
1286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1287
1288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1289
1290 /// @todo NEWMEDIA for file names, add the default extension if no extension
1291 /// is present (using the information from the VD backend which also implies
1292 /// that one more parameter should be passed to setLocation() requesting
1293 /// that functionality since it is only allwed when called from this method
1294
1295 /// @todo NEWMEDIA rename the file and set m->location on success, then save
1296 /// the global registry (and local registries of portable VMs referring to
1297 /// this medium), this will also require to add the mRegistered flag to data
1298
1299 ReturnComNotImplemented();
1300}
1301
1302STDMETHODIMP Medium::COMGETTER(Name)(BSTR *aName)
1303{
1304 CheckComArgOutPointerValid(aName);
1305
1306 AutoCaller autoCaller(this);
1307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1308
1309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1310
1311 getName().cloneTo(aName);
1312
1313 return S_OK;
1314}
1315
1316STDMETHODIMP Medium::COMGETTER(DeviceType)(DeviceType_T *aDeviceType)
1317{
1318 CheckComArgOutPointerValid(aDeviceType);
1319
1320 AutoCaller autoCaller(this);
1321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1322
1323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1324
1325 *aDeviceType = m->devType;
1326
1327 return S_OK;
1328}
1329
1330STDMETHODIMP Medium::COMGETTER(HostDrive)(BOOL *aHostDrive)
1331{
1332 CheckComArgOutPointerValid(aHostDrive);
1333
1334 AutoCaller autoCaller(this);
1335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1336
1337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1338
1339 *aHostDrive = m->hostDrive;
1340
1341 return S_OK;
1342}
1343
1344STDMETHODIMP Medium::COMGETTER(Size)(ULONG64 *aSize)
1345{
1346 CheckComArgOutPointerValid(aSize);
1347
1348 AutoCaller autoCaller(this);
1349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1350
1351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
1353 *aSize = m->size;
1354
1355 return S_OK;
1356}
1357
1358STDMETHODIMP Medium::COMGETTER(Format)(BSTR *aFormat)
1359{
1360 CheckComArgOutPointerValid(aFormat);
1361
1362 AutoCaller autoCaller(this);
1363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1364
1365 /* no need to lock, m->strFormat is const */
1366 m->strFormat.cloneTo(aFormat);
1367
1368 return S_OK;
1369}
1370
1371STDMETHODIMP Medium::COMGETTER(MediumFormat)(IMediumFormat **aMediumFormat)
1372{
1373 CheckComArgOutPointerValid(aMediumFormat);
1374
1375 AutoCaller autoCaller(this);
1376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1377
1378 /* no need to lock, m->formatObj is const */
1379 m->formatObj.queryInterfaceTo(aMediumFormat);
1380
1381 return S_OK;
1382}
1383
1384STDMETHODIMP Medium::COMGETTER(Type)(MediumType_T *aType)
1385{
1386 CheckComArgOutPointerValid(aType);
1387
1388 AutoCaller autoCaller(this);
1389 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1390
1391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 *aType = m->type;
1394
1395 return S_OK;
1396}
1397
1398STDMETHODIMP Medium::COMSETTER(Type)(MediumType_T aType)
1399{
1400 AutoCaller autoCaller(this);
1401 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1402
1403 // we access mParent and members
1404 AutoMultiWriteLock2 mlock(&m->pVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
1405
1406 switch (m->state)
1407 {
1408 case MediumState_Created:
1409 case MediumState_Inaccessible:
1410 break;
1411 default:
1412 return setStateError();
1413 }
1414
1415 if (m->type == aType)
1416 {
1417 /* Nothing to do */
1418 return S_OK;
1419 }
1420
1421 /* cannot change the type of a differencing medium */
1422 if (m->pParent)
1423 return setError(VBOX_E_INVALID_OBJECT_STATE,
1424 tr("Cannot change the type of medium '%s' because it is a differencing medium"),
1425 m->strLocationFull.c_str());
1426
1427 /* cannot change the type of a medium being in use by more than one VM */
1428 if (m->backRefs.size() > 1)
1429 return setError(VBOX_E_INVALID_OBJECT_STATE,
1430 tr("Cannot change the type of medium '%s' because it is attached to %d virtual machines"),
1431 m->strLocationFull.c_str(), m->backRefs.size());
1432
1433 switch (aType)
1434 {
1435 case MediumType_Normal:
1436 case MediumType_Immutable:
1437 {
1438 /* normal can be easily converted to immutable and vice versa even
1439 * if they have children as long as they are not attached to any
1440 * machine themselves */
1441 break;
1442 }
1443 case MediumType_Writethrough:
1444 case MediumType_Shareable:
1445 {
1446 /* cannot change to writethrough or shareable if there are children */
1447 if (getChildren().size() != 0)
1448 return setError(VBOX_E_OBJECT_IN_USE,
1449 tr("Cannot change type for medium '%s' since it has %d child media"),
1450 m->strLocationFull.c_str(), getChildren().size());
1451 if (aType == MediumType_Shareable)
1452 {
1453 MediumVariant_T variant = getVariant();
1454 if (!(variant & MediumVariant_Fixed))
1455 return setError(VBOX_E_INVALID_OBJECT_STATE,
1456 tr("Cannot change type for medium '%s' to 'Shareable' since it is a dynamic medium storage unit"),
1457 m->strLocationFull.c_str());
1458 }
1459 break;
1460 }
1461 default:
1462 AssertFailedReturn(E_FAIL);
1463 }
1464
1465 m->type = aType;
1466
1467 // save the global settings; for that we should hold only the VirtualBox lock
1468 mlock.release();
1469 AutoWriteLock alock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
1470 HRESULT rc = m->pVirtualBox->saveSettings();
1471
1472 return rc;
1473}
1474
1475STDMETHODIMP Medium::COMGETTER(Parent)(IMedium **aParent)
1476{
1477 CheckComArgOutPointerValid(aParent);
1478
1479 AutoCaller autoCaller(this);
1480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1481
1482 /* we access mParent */
1483 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1484
1485 m->pParent.queryInterfaceTo(aParent);
1486
1487 return S_OK;
1488}
1489
1490STDMETHODIMP Medium::COMGETTER(Children)(ComSafeArrayOut(IMedium *, aChildren))
1491{
1492 CheckComArgOutSafeArrayPointerValid(aChildren);
1493
1494 AutoCaller autoCaller(this);
1495 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1496
1497 /* we access children */
1498 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1499
1500 SafeIfaceArray<IMedium> children(this->getChildren());
1501 children.detachTo(ComSafeArrayOutArg(aChildren));
1502
1503 return S_OK;
1504}
1505
1506STDMETHODIMP Medium::COMGETTER(Base)(IMedium **aBase)
1507{
1508 CheckComArgOutPointerValid(aBase);
1509
1510 /* base() will do callers/locking */
1511
1512 getBase().queryInterfaceTo(aBase);
1513
1514 return S_OK;
1515}
1516
1517STDMETHODIMP Medium::COMGETTER(ReadOnly)(BOOL *aReadOnly)
1518{
1519 CheckComArgOutPointerValid(aReadOnly);
1520
1521 AutoCaller autoCaller(this);
1522 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1523
1524 /* isRadOnly() will do locking */
1525
1526 *aReadOnly = isReadOnly();
1527
1528 return S_OK;
1529}
1530
1531STDMETHODIMP Medium::COMGETTER(LogicalSize)(ULONG64 *aLogicalSize)
1532{
1533 CheckComArgOutPointerValid(aLogicalSize);
1534
1535 {
1536 AutoCaller autoCaller(this);
1537 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1538
1539 /* we access mParent */
1540 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1541
1542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1543
1544 if (m->pParent.isNull())
1545 {
1546 *aLogicalSize = m->logicalSize;
1547
1548 return S_OK;
1549 }
1550 }
1551
1552 /* We assume that some backend may decide to return a meaningless value in
1553 * response to VDGetSize() for differencing media and therefore always
1554 * ask the base medium ourselves. */
1555
1556 /* base() will do callers/locking */
1557
1558 return getBase()->COMGETTER(LogicalSize)(aLogicalSize);
1559}
1560
1561STDMETHODIMP Medium::COMGETTER(AutoReset)(BOOL *aAutoReset)
1562{
1563 CheckComArgOutPointerValid(aAutoReset);
1564
1565 AutoCaller autoCaller(this);
1566 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1567
1568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 if (m->pParent.isNull())
1571 *aAutoReset = FALSE;
1572 else
1573 *aAutoReset = m->autoReset;
1574
1575 return S_OK;
1576}
1577
1578STDMETHODIMP Medium::COMSETTER(AutoReset)(BOOL aAutoReset)
1579{
1580 AutoCaller autoCaller(this);
1581 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1582
1583 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1584
1585 if (m->pParent.isNull())
1586 return setError(VBOX_E_NOT_SUPPORTED,
1587 tr("Medium '%s' is not differencing"),
1588 m->strLocationFull.c_str());
1589
1590 if (m->autoReset != !!aAutoReset)
1591 {
1592 m->autoReset = !!aAutoReset;
1593
1594 // save the global settings; for that we should hold only the VirtualBox lock
1595 mlock.release();
1596 AutoWriteLock alock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
1597 return m->pVirtualBox->saveSettings();
1598 }
1599
1600 return S_OK;
1601}
1602STDMETHODIMP Medium::COMGETTER(LastAccessError)(BSTR *aLastAccessError)
1603{
1604 CheckComArgOutPointerValid(aLastAccessError);
1605
1606 AutoCaller autoCaller(this);
1607 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1608
1609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1610
1611 m->strLastAccessError.cloneTo(aLastAccessError);
1612
1613 return S_OK;
1614}
1615
1616STDMETHODIMP Medium::COMGETTER(MachineIds)(ComSafeArrayOut(BSTR,aMachineIds))
1617{
1618 CheckComArgOutSafeArrayPointerValid(aMachineIds);
1619
1620 AutoCaller autoCaller(this);
1621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1622
1623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 com::SafeArray<BSTR> machineIds;
1626
1627 if (m->backRefs.size() != 0)
1628 {
1629 machineIds.reset(m->backRefs.size());
1630
1631 size_t i = 0;
1632 for (BackRefList::const_iterator it = m->backRefs.begin();
1633 it != m->backRefs.end(); ++it, ++i)
1634 {
1635 it->machineId.toUtf16().detachTo(&machineIds[i]);
1636 }
1637 }
1638
1639 machineIds.detachTo(ComSafeArrayOutArg(aMachineIds));
1640
1641 return S_OK;
1642}
1643
1644STDMETHODIMP Medium::SetIDs(BOOL aSetImageId,
1645 IN_BSTR aImageId,
1646 BOOL aSetParentId,
1647 IN_BSTR aParentId)
1648{
1649 AutoCaller autoCaller(this);
1650 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1651
1652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1653
1654 Guid imageId, parentId;
1655 if (aSetImageId)
1656 {
1657 imageId = Guid(aImageId);
1658 if (imageId.isEmpty())
1659 return setError(E_INVALIDARG, tr("Argument %s is empty"), "aImageId");
1660 }
1661 if (aSetParentId)
1662 parentId = Guid(aParentId);
1663
1664 unconst(m->uuidImage) = imageId;
1665 unconst(m->uuidParentImage) = parentId;
1666
1667 HRESULT rc = queryInfo(!!aSetImageId /* fSetImageId */,
1668 !!aSetParentId /* fSetParentId */);
1669
1670 return rc;
1671}
1672
1673STDMETHODIMP Medium::RefreshState(MediumState_T *aState)
1674{
1675 CheckComArgOutPointerValid(aState);
1676
1677 AutoCaller autoCaller(this);
1678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1679
1680 /* queryInfo() locks this for writing. */
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 HRESULT rc = S_OK;
1684
1685 switch (m->state)
1686 {
1687 case MediumState_Created:
1688 case MediumState_Inaccessible:
1689 case MediumState_LockedRead:
1690 {
1691 rc = queryInfo(false /* fSetImageId */, false /* fSetParentId */);
1692 break;
1693 }
1694 default:
1695 break;
1696 }
1697
1698 *aState = m->state;
1699
1700 return rc;
1701}
1702
1703STDMETHODIMP Medium::GetSnapshotIds(IN_BSTR aMachineId,
1704 ComSafeArrayOut(BSTR, aSnapshotIds))
1705{
1706 CheckComArgExpr(aMachineId, Guid(aMachineId).isEmpty() == false);
1707 CheckComArgOutSafeArrayPointerValid(aSnapshotIds);
1708
1709 AutoCaller autoCaller(this);
1710 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1711
1712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1713
1714 com::SafeArray<BSTR> snapshotIds;
1715
1716 Guid id(aMachineId);
1717 for (BackRefList::const_iterator it = m->backRefs.begin();
1718 it != m->backRefs.end(); ++it)
1719 {
1720 if (it->machineId == id)
1721 {
1722 size_t size = it->llSnapshotIds.size();
1723
1724 /* if the medium is attached to the machine in the current state, we
1725 * return its ID as the first element of the array */
1726 if (it->fInCurState)
1727 ++size;
1728
1729 if (size > 0)
1730 {
1731 snapshotIds.reset(size);
1732
1733 size_t j = 0;
1734 if (it->fInCurState)
1735 it->machineId.toUtf16().detachTo(&snapshotIds[j++]);
1736
1737 for (BackRef::GuidList::const_iterator jt = it->llSnapshotIds.begin();
1738 jt != it->llSnapshotIds.end();
1739 ++jt, ++j)
1740 {
1741 (*jt).toUtf16().detachTo(&snapshotIds[j]);
1742 }
1743 }
1744
1745 break;
1746 }
1747 }
1748
1749 snapshotIds.detachTo(ComSafeArrayOutArg(aSnapshotIds));
1750
1751 return S_OK;
1752}
1753
1754/**
1755 * @note @a aState may be NULL if the state value is not needed (only for
1756 * in-process calls).
1757 */
1758STDMETHODIMP Medium::LockRead(MediumState_T *aState)
1759{
1760 AutoCaller autoCaller(this);
1761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1762
1763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1764
1765 /* Wait for a concurrently running queryInfo() to complete */
1766 while (m->queryInfoRunning)
1767 {
1768 alock.leave();
1769 RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
1770 alock.enter();
1771 }
1772
1773 /* return the current state before */
1774 if (aState)
1775 *aState = m->state;
1776
1777 HRESULT rc = S_OK;
1778
1779 switch (m->state)
1780 {
1781 case MediumState_Created:
1782 case MediumState_Inaccessible:
1783 case MediumState_LockedRead:
1784 {
1785 ++m->readers;
1786
1787 ComAssertMsgBreak(m->readers != 0, ("Counter overflow"), rc = E_FAIL);
1788
1789 /* Remember pre-lock state */
1790 if (m->state != MediumState_LockedRead)
1791 m->preLockState = m->state;
1792
1793 LogFlowThisFunc(("Okay - prev state=%d readers=%d\n", m->state, m->readers));
1794 m->state = MediumState_LockedRead;
1795
1796 break;
1797 }
1798 default:
1799 {
1800 LogFlowThisFunc(("Failing - state=%d\n", m->state));
1801 rc = setStateError();
1802 break;
1803 }
1804 }
1805
1806 return rc;
1807}
1808
1809/**
1810 * @note @a aState may be NULL if the state value is not needed (only for
1811 * in-process calls).
1812 */
1813STDMETHODIMP Medium::UnlockRead(MediumState_T *aState)
1814{
1815 AutoCaller autoCaller(this);
1816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1817
1818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 HRESULT rc = S_OK;
1821
1822 switch (m->state)
1823 {
1824 case MediumState_LockedRead:
1825 {
1826 Assert(m->readers != 0);
1827 --m->readers;
1828
1829 /* Reset the state after the last reader */
1830 if (m->readers == 0)
1831 {
1832 m->state = m->preLockState;
1833 /* There are cases where we inject the deleting state into
1834 * a medium locked for reading. Make sure #unmarkForDeletion()
1835 * gets the right state afterwards. */
1836 if (m->preLockState == MediumState_Deleting)
1837 m->preLockState = MediumState_Created;
1838 }
1839
1840 LogFlowThisFunc(("new state=%d\n", m->state));
1841 break;
1842 }
1843 default:
1844 {
1845 LogFlowThisFunc(("Failing - state=%d\n", m->state));
1846 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
1847 tr("Medium '%s' is not locked for reading"),
1848 m->strLocationFull.c_str());
1849 break;
1850 }
1851 }
1852
1853 /* return the current state after */
1854 if (aState)
1855 *aState = m->state;
1856
1857 return rc;
1858}
1859
1860/**
1861 * @note @a aState may be NULL if the state value is not needed (only for
1862 * in-process calls).
1863 */
1864STDMETHODIMP Medium::LockWrite(MediumState_T *aState)
1865{
1866 AutoCaller autoCaller(this);
1867 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1868
1869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 /* Wait for a concurrently running queryInfo() to complete */
1872 while (m->queryInfoRunning)
1873 {
1874 alock.leave();
1875 RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
1876 alock.enter();
1877 }
1878
1879 /* return the current state before */
1880 if (aState)
1881 *aState = m->state;
1882
1883 HRESULT rc = S_OK;
1884
1885 switch (m->state)
1886 {
1887 case MediumState_Created:
1888 case MediumState_Inaccessible:
1889 {
1890 m->preLockState = m->state;
1891
1892 LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
1893 m->state = MediumState_LockedWrite;
1894 break;
1895 }
1896 default:
1897 {
1898 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
1899 rc = setStateError();
1900 break;
1901 }
1902 }
1903
1904 return rc;
1905}
1906
1907/**
1908 * @note @a aState may be NULL if the state value is not needed (only for
1909 * in-process calls).
1910 */
1911STDMETHODIMP Medium::UnlockWrite(MediumState_T *aState)
1912{
1913 AutoCaller autoCaller(this);
1914 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1915
1916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1917
1918 HRESULT rc = S_OK;
1919
1920 switch (m->state)
1921 {
1922 case MediumState_LockedWrite:
1923 {
1924 m->state = m->preLockState;
1925 /* There are cases where we inject the deleting state into
1926 * a medium locked for writing. Make sure #unmarkForDeletion()
1927 * gets the right state afterwards. */
1928 if (m->preLockState == MediumState_Deleting)
1929 m->preLockState = MediumState_Created;
1930 LogFlowThisFunc(("new state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
1931 break;
1932 }
1933 default:
1934 {
1935 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
1936 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
1937 tr("Medium '%s' is not locked for writing"),
1938 m->strLocationFull.c_str());
1939 break;
1940 }
1941 }
1942
1943 /* return the current state after */
1944 if (aState)
1945 *aState = m->state;
1946
1947 return rc;
1948}
1949
1950STDMETHODIMP Medium::Close()
1951{
1952 AutoCaller autoCaller(this);
1953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1954
1955 // make a copy of VirtualBox pointer which gets nulled by uninit()
1956 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1957
1958 bool fNeedsSaveSettings = false;
1959 HRESULT rc = close(&fNeedsSaveSettings, autoCaller);
1960
1961 if (fNeedsSaveSettings)
1962 {
1963 AutoWriteLock vboxlock(pVirtualBox COMMA_LOCKVAL_SRC_POS);
1964 pVirtualBox->saveSettings();
1965 }
1966
1967 return rc;
1968}
1969
1970STDMETHODIMP Medium::GetProperty(IN_BSTR aName, BSTR *aValue)
1971{
1972 CheckComArgStrNotEmptyOrNull(aName);
1973 CheckComArgOutPointerValid(aValue);
1974
1975 AutoCaller autoCaller(this);
1976 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1977
1978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1979
1980 settings::StringsMap::const_iterator it = m->mapProperties.find(Utf8Str(aName));
1981 if (it == m->mapProperties.end())
1982 return setError(VBOX_E_OBJECT_NOT_FOUND,
1983 tr("Property '%ls' does not exist"), aName);
1984
1985 it->second.cloneTo(aValue);
1986
1987 return S_OK;
1988}
1989
1990STDMETHODIMP Medium::SetProperty(IN_BSTR aName, IN_BSTR aValue)
1991{
1992 CheckComArgStrNotEmptyOrNull(aName);
1993
1994 AutoCaller autoCaller(this);
1995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1996
1997 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1998
1999 switch (m->state)
2000 {
2001 case MediumState_Created:
2002 case MediumState_Inaccessible:
2003 break;
2004 default:
2005 return setStateError();
2006 }
2007
2008 settings::StringsMap::iterator it = m->mapProperties.find(Utf8Str(aName));
2009 if (it == m->mapProperties.end())
2010 return setError(VBOX_E_OBJECT_NOT_FOUND,
2011 tr("Property '%ls' does not exist"),
2012 aName);
2013
2014 it->second = aValue;
2015
2016 // save the global settings; for that we should hold only the VirtualBox lock
2017 mlock.release();
2018 AutoWriteLock alock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
2019 HRESULT rc = m->pVirtualBox->saveSettings();
2020
2021 return rc;
2022}
2023
2024STDMETHODIMP Medium::GetProperties(IN_BSTR aNames,
2025 ComSafeArrayOut(BSTR, aReturnNames),
2026 ComSafeArrayOut(BSTR, aReturnValues))
2027{
2028 CheckComArgOutSafeArrayPointerValid(aReturnNames);
2029 CheckComArgOutSafeArrayPointerValid(aReturnValues);
2030
2031 AutoCaller autoCaller(this);
2032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2033
2034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2035
2036 /// @todo make use of aNames according to the documentation
2037 NOREF(aNames);
2038
2039 com::SafeArray<BSTR> names(m->mapProperties.size());
2040 com::SafeArray<BSTR> values(m->mapProperties.size());
2041 size_t i = 0;
2042
2043 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
2044 it != m->mapProperties.end();
2045 ++it)
2046 {
2047 it->first.cloneTo(&names[i]);
2048 it->second.cloneTo(&values[i]);
2049 ++i;
2050 }
2051
2052 names.detachTo(ComSafeArrayOutArg(aReturnNames));
2053 values.detachTo(ComSafeArrayOutArg(aReturnValues));
2054
2055 return S_OK;
2056}
2057
2058STDMETHODIMP Medium::SetProperties(ComSafeArrayIn(IN_BSTR, aNames),
2059 ComSafeArrayIn(IN_BSTR, aValues))
2060{
2061 CheckComArgSafeArrayNotNull(aNames);
2062 CheckComArgSafeArrayNotNull(aValues);
2063
2064 AutoCaller autoCaller(this);
2065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2066
2067 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2068
2069 com::SafeArray<IN_BSTR> names(ComSafeArrayInArg(aNames));
2070 com::SafeArray<IN_BSTR> values(ComSafeArrayInArg(aValues));
2071
2072 /* first pass: validate names */
2073 for (size_t i = 0;
2074 i < names.size();
2075 ++i)
2076 {
2077 if (m->mapProperties.find(Utf8Str(names[i])) == m->mapProperties.end())
2078 return setError(VBOX_E_OBJECT_NOT_FOUND,
2079 tr("Property '%ls' does not exist"), names[i]);
2080 }
2081
2082 /* second pass: assign */
2083 for (size_t i = 0;
2084 i < names.size();
2085 ++i)
2086 {
2087 settings::StringsMap::iterator it = m->mapProperties.find(Utf8Str(names[i]));
2088 AssertReturn(it != m->mapProperties.end(), E_FAIL);
2089
2090 it->second = Utf8Str(values[i]);
2091 }
2092
2093 mlock.release();
2094
2095 // saveSettings needs vbox lock
2096 AutoWriteLock alock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
2097 HRESULT rc = m->pVirtualBox->saveSettings();
2098
2099 return rc;
2100}
2101
2102STDMETHODIMP Medium::CreateBaseStorage(ULONG64 aLogicalSize,
2103 MediumVariant_T aVariant,
2104 IProgress **aProgress)
2105{
2106 CheckComArgOutPointerValid(aProgress);
2107
2108 AutoCaller autoCaller(this);
2109 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2110
2111 HRESULT rc = S_OK;
2112 ComObjPtr <Progress> pProgress;
2113 Medium::Task *pTask = NULL;
2114
2115 try
2116 {
2117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2118
2119 aVariant = (MediumVariant_T)((unsigned)aVariant & (unsigned)~MediumVariant_Diff);
2120 if ( !(aVariant & MediumVariant_Fixed)
2121 && !(m->formatObj->getCapabilities() & MediumFormatCapabilities_CreateDynamic))
2122 throw setError(VBOX_E_NOT_SUPPORTED,
2123 tr("Medium format '%s' does not support dynamic storage creation"),
2124 m->strFormat.c_str());
2125 if ( (aVariant & MediumVariant_Fixed)
2126 && !(m->formatObj->getCapabilities() & MediumFormatCapabilities_CreateDynamic))
2127 throw setError(VBOX_E_NOT_SUPPORTED,
2128 tr("Medium format '%s' does not support fixed storage creation"),
2129 m->strFormat.c_str());
2130
2131 if (m->state != MediumState_NotCreated)
2132 throw setStateError();
2133
2134 pProgress.createObject();
2135 rc = pProgress->init(m->pVirtualBox,
2136 static_cast<IMedium*>(this),
2137 (aVariant & MediumVariant_Fixed)
2138 ? BstrFmt(tr("Creating fixed medium storage unit '%s'"), m->strLocationFull.c_str())
2139 : BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.c_str()),
2140 TRUE /* aCancelable */);
2141 if (FAILED(rc))
2142 throw rc;
2143
2144 /* setup task object to carry out the operation asynchronously */
2145 pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize,
2146 aVariant);
2147 rc = pTask->rc();
2148 AssertComRC(rc);
2149 if (FAILED(rc))
2150 throw rc;
2151
2152 m->state = MediumState_Creating;
2153 }
2154 catch (HRESULT aRC) { rc = aRC; }
2155
2156 if (SUCCEEDED(rc))
2157 {
2158 rc = startThread(pTask);
2159
2160 if (SUCCEEDED(rc))
2161 pProgress.queryInterfaceTo(aProgress);
2162 }
2163 else if (pTask != NULL)
2164 delete pTask;
2165
2166 return rc;
2167}
2168
2169STDMETHODIMP Medium::DeleteStorage(IProgress **aProgress)
2170{
2171 CheckComArgOutPointerValid(aProgress);
2172
2173 AutoCaller autoCaller(this);
2174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2175
2176 bool fNeedsSaveSettings = false;
2177 ComObjPtr<Progress> pProgress;
2178
2179 HRESULT rc = deleteStorage(&pProgress,
2180 false /* aWait */,
2181 &fNeedsSaveSettings);
2182 if (fNeedsSaveSettings)
2183 {
2184 AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
2185 m->pVirtualBox->saveSettings();
2186 }
2187
2188 if (SUCCEEDED(rc))
2189 pProgress.queryInterfaceTo(aProgress);
2190
2191 return rc;
2192}
2193
2194STDMETHODIMP Medium::CreateDiffStorage(IMedium *aTarget,
2195 MediumVariant_T aVariant,
2196 IProgress **aProgress)
2197{
2198 CheckComArgNotNull(aTarget);
2199 CheckComArgOutPointerValid(aProgress);
2200
2201 AutoCaller autoCaller(this);
2202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2203
2204 ComObjPtr<Medium> diff = static_cast<Medium*>(aTarget);
2205
2206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2207
2208 if (m->type == MediumType_Writethrough)
2209 return setError(VBOX_E_INVALID_OBJECT_STATE,
2210 tr("Medium type of '%s' is Writethrough"),
2211 m->strLocationFull.c_str());
2212 else if (m->type == MediumType_Shareable)
2213 return setError(VBOX_E_INVALID_OBJECT_STATE,
2214 tr("Medium type of '%s' is Shareable"),
2215 m->strLocationFull.c_str());
2216
2217 /* Apply the normal locking logic to the entire chain. */
2218 MediumLockList *pMediumLockList(new MediumLockList());
2219 HRESULT rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
2220 true /* fMediumLockWrite */,
2221 this,
2222 *pMediumLockList);
2223 if (FAILED(rc))
2224 {
2225 delete pMediumLockList;
2226 return rc;
2227 }
2228
2229 ComObjPtr <Progress> pProgress;
2230
2231 rc = createDiffStorage(diff, aVariant, pMediumLockList, &pProgress,
2232 false /* aWait */, NULL /* pfNeedsSaveSettings*/);
2233 if (FAILED(rc))
2234 delete pMediumLockList;
2235 else
2236 pProgress.queryInterfaceTo(aProgress);
2237
2238 return rc;
2239}
2240
2241STDMETHODIMP Medium::MergeTo(IMedium *aTarget, IProgress **aProgress)
2242{
2243 CheckComArgNotNull(aTarget);
2244 CheckComArgOutPointerValid(aProgress);
2245 ComAssertRet(aTarget != this, E_INVALIDARG);
2246
2247 AutoCaller autoCaller(this);
2248 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2249
2250 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget);
2251
2252 bool fMergeForward = false;
2253 ComObjPtr<Medium> pParentForTarget;
2254 MediaList childrenToReparent;
2255 MediumLockList *pMediumLockList = NULL;
2256
2257 HRESULT rc = S_OK;
2258
2259 rc = prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward,
2260 pParentForTarget, childrenToReparent, pMediumLockList);
2261 if (FAILED(rc)) return rc;
2262
2263 ComObjPtr <Progress> pProgress;
2264
2265 rc = mergeTo(pTarget, fMergeForward, pParentForTarget, childrenToReparent,
2266 pMediumLockList, &pProgress, false /* aWait */,
2267 NULL /* pfNeedsSaveSettings */);
2268 if (FAILED(rc))
2269 cancelMergeTo(childrenToReparent, pMediumLockList);
2270 else
2271 pProgress.queryInterfaceTo(aProgress);
2272
2273 return rc;
2274}
2275
2276STDMETHODIMP Medium::CloneTo(IMedium *aTarget,
2277 MediumVariant_T aVariant,
2278 IMedium *aParent,
2279 IProgress **aProgress)
2280{
2281 CheckComArgNotNull(aTarget);
2282 CheckComArgOutPointerValid(aProgress);
2283 ComAssertRet(aTarget != this, E_INVALIDARG);
2284
2285 AutoCaller autoCaller(this);
2286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2287
2288 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget);
2289 ComObjPtr<Medium> pParent;
2290 if (aParent)
2291 pParent = static_cast<Medium*>(aParent);
2292
2293 HRESULT rc = S_OK;
2294 ComObjPtr<Progress> pProgress;
2295 Medium::Task *pTask = NULL;
2296
2297 try
2298 {
2299 // locking: we need the tree lock first because we access parent pointers
2300 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2301 // and we need to write-lock the media involved
2302 AutoMultiWriteLock3 alock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
2303
2304 if ( pTarget->m->state != MediumState_NotCreated
2305 && pTarget->m->state != MediumState_Created)
2306 throw pTarget->setStateError();
2307
2308 /* Build the source lock list. */
2309 MediumLockList *pSourceMediumLockList(new MediumLockList());
2310 rc = createMediumLockList(true /* fFailIfInaccessible */,
2311 false /* fMediumLockWrite */,
2312 NULL,
2313 *pSourceMediumLockList);
2314 if (FAILED(rc))
2315 {
2316 delete pSourceMediumLockList;
2317 throw rc;
2318 }
2319
2320 /* Build the target lock list (including the to-be parent chain). */
2321 MediumLockList *pTargetMediumLockList(new MediumLockList());
2322 rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
2323 true /* fMediumLockWrite */,
2324 pParent,
2325 *pTargetMediumLockList);
2326 if (FAILED(rc))
2327 {
2328 delete pSourceMediumLockList;
2329 delete pTargetMediumLockList;
2330 throw rc;
2331 }
2332
2333 rc = pSourceMediumLockList->Lock();
2334 if (FAILED(rc))
2335 {
2336 delete pSourceMediumLockList;
2337 delete pTargetMediumLockList;
2338 throw setError(rc,
2339 tr("Failed to lock source media '%s'"),
2340 getLocationFull().c_str());
2341 }
2342 rc = pTargetMediumLockList->Lock();
2343 if (FAILED(rc))
2344 {
2345 delete pSourceMediumLockList;
2346 delete pTargetMediumLockList;
2347 throw setError(rc,
2348 tr("Failed to lock target media '%s'"),
2349 pTarget->getLocationFull().c_str());
2350 }
2351
2352 pProgress.createObject();
2353 rc = pProgress->init(m->pVirtualBox,
2354 static_cast <IMedium *>(this),
2355 BstrFmt(tr("Creating clone medium '%s'"), pTarget->m->strLocationFull.c_str()),
2356 TRUE /* aCancelable */);
2357 if (FAILED(rc))
2358 {
2359 delete pSourceMediumLockList;
2360 delete pTargetMediumLockList;
2361 throw rc;
2362 }
2363
2364 /* setup task object to carry out the operation asynchronously */
2365 pTask = new Medium::CloneTask(this, pProgress, pTarget, aVariant,
2366 pParent, pSourceMediumLockList,
2367 pTargetMediumLockList);
2368 rc = pTask->rc();
2369 AssertComRC(rc);
2370 if (FAILED(rc))
2371 throw rc;
2372
2373 if (pTarget->m->state == MediumState_NotCreated)
2374 pTarget->m->state = MediumState_Creating;
2375 }
2376 catch (HRESULT aRC) { rc = aRC; }
2377
2378 if (SUCCEEDED(rc))
2379 {
2380 rc = startThread(pTask);
2381
2382 if (SUCCEEDED(rc))
2383 pProgress.queryInterfaceTo(aProgress);
2384 }
2385 else if (pTask != NULL)
2386 delete pTask;
2387
2388 return rc;
2389}
2390
2391STDMETHODIMP Medium::Compact(IProgress **aProgress)
2392{
2393 CheckComArgOutPointerValid(aProgress);
2394
2395 AutoCaller autoCaller(this);
2396 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2397
2398 HRESULT rc = S_OK;
2399 ComObjPtr <Progress> pProgress;
2400 Medium::Task *pTask = NULL;
2401
2402 try
2403 {
2404 /* We need to lock both the current object, and the tree lock (would
2405 * cause a lock order violation otherwise) for createMediumLockList. */
2406 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
2407 this->lockHandle()
2408 COMMA_LOCKVAL_SRC_POS);
2409
2410 /* Build the medium lock list. */
2411 MediumLockList *pMediumLockList(new MediumLockList());
2412 rc = createMediumLockList(true /* fFailIfInaccessible */ ,
2413 true /* fMediumLockWrite */,
2414 NULL,
2415 *pMediumLockList);
2416 if (FAILED(rc))
2417 {
2418 delete pMediumLockList;
2419 throw rc;
2420 }
2421
2422 rc = pMediumLockList->Lock();
2423 if (FAILED(rc))
2424 {
2425 delete pMediumLockList;
2426 throw setError(rc,
2427 tr("Failed to lock media when compacting '%s'"),
2428 getLocationFull().c_str());
2429 }
2430
2431 pProgress.createObject();
2432 rc = pProgress->init(m->pVirtualBox,
2433 static_cast <IMedium *>(this),
2434 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()),
2435 TRUE /* aCancelable */);
2436 if (FAILED(rc))
2437 {
2438 delete pMediumLockList;
2439 throw rc;
2440 }
2441
2442 /* setup task object to carry out the operation asynchronously */
2443 pTask = new Medium::CompactTask(this, pProgress, pMediumLockList);
2444 rc = pTask->rc();
2445 AssertComRC(rc);
2446 if (FAILED(rc))
2447 throw rc;
2448 }
2449 catch (HRESULT aRC) { rc = aRC; }
2450
2451 if (SUCCEEDED(rc))
2452 {
2453 rc = startThread(pTask);
2454
2455 if (SUCCEEDED(rc))
2456 pProgress.queryInterfaceTo(aProgress);
2457 }
2458 else if (pTask != NULL)
2459 delete pTask;
2460
2461 return rc;
2462}
2463
2464STDMETHODIMP Medium::Resize(ULONG64 aLogicalSize, IProgress **aProgress)
2465{
2466 CheckComArgOutPointerValid(aProgress);
2467
2468 AutoCaller autoCaller(this);
2469 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2470
2471 NOREF(aLogicalSize);
2472 NOREF(aProgress);
2473 ReturnComNotImplemented();
2474}
2475
2476STDMETHODIMP Medium::Reset(IProgress **aProgress)
2477{
2478 CheckComArgOutPointerValid(aProgress);
2479
2480 AutoCaller autoCaller(this);
2481 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2482
2483 HRESULT rc = S_OK;
2484 ComObjPtr <Progress> pProgress;
2485 Medium::Task *pTask = NULL;
2486
2487 try
2488 {
2489 /* canClose() needs the tree lock */
2490 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
2491 this->lockHandle()
2492 COMMA_LOCKVAL_SRC_POS);
2493
2494 LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
2495
2496 if (m->pParent.isNull())
2497 throw setError(VBOX_E_NOT_SUPPORTED,
2498 tr("Medium type of '%s' is not differencing"),
2499 m->strLocationFull.c_str());
2500
2501 rc = canClose();
2502 if (FAILED(rc))
2503 throw rc;
2504
2505 /* Build the medium lock list. */
2506 MediumLockList *pMediumLockList(new MediumLockList());
2507 rc = createMediumLockList(true /* fFailIfInaccessible */,
2508 true /* fMediumLockWrite */,
2509 NULL,
2510 *pMediumLockList);
2511 if (FAILED(rc))
2512 {
2513 delete pMediumLockList;
2514 throw rc;
2515 }
2516
2517 rc = pMediumLockList->Lock();
2518 if (FAILED(rc))
2519 {
2520 delete pMediumLockList;
2521 throw setError(rc,
2522 tr("Failed to lock media when resetting '%s'"),
2523 getLocationFull().c_str());
2524 }
2525
2526 pProgress.createObject();
2527 rc = pProgress->init(m->pVirtualBox,
2528 static_cast<IMedium*>(this),
2529 BstrFmt(tr("Resetting differencing medium '%s'"), m->strLocationFull.c_str()),
2530 FALSE /* aCancelable */);
2531 if (FAILED(rc))
2532 throw rc;
2533
2534 /* setup task object to carry out the operation asynchronously */
2535 pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
2536 rc = pTask->rc();
2537 AssertComRC(rc);
2538 if (FAILED(rc))
2539 throw rc;
2540 }
2541 catch (HRESULT aRC) { rc = aRC; }
2542
2543 if (SUCCEEDED(rc))
2544 {
2545 rc = startThread(pTask);
2546
2547 if (SUCCEEDED(rc))
2548 pProgress.queryInterfaceTo(aProgress);
2549 }
2550 else
2551 {
2552 /* Note: on success, the task will unlock this */
2553 {
2554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2555 HRESULT rc2 = UnlockWrite(NULL);
2556 AssertComRC(rc2);
2557 }
2558 if (pTask != NULL)
2559 delete pTask;
2560 }
2561
2562 LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
2563
2564 return rc;
2565}
2566
2567////////////////////////////////////////////////////////////////////////////////
2568//
2569// Medium public internal methods
2570//
2571////////////////////////////////////////////////////////////////////////////////
2572
2573/**
2574 * Internal method to return the medium's parent medium. Must have caller + locking!
2575 * @return
2576 */
2577const ComObjPtr<Medium>& Medium::getParent() const
2578{
2579 return m->pParent;
2580}
2581
2582/**
2583 * Internal method to return the medium's list of child media. Must have caller + locking!
2584 * @return
2585 */
2586const MediaList& Medium::getChildren() const
2587{
2588 return m->llChildren;
2589}
2590
2591/**
2592 * Internal method to return the medium's registry machine (i.e. the machine in whose
2593 * machine XML this medium is listed), or NULL if the medium is registered globally.
2594 *
2595 * Must have caller + locking!
2596 *
2597 * @return
2598 */
2599const Guid& Medium::getRegistryMachineId() const
2600{
2601 return m->uuidRegistryMachine;
2602}
2603
2604/**
2605 * Internal method to return the medium's GUID. Must have caller + locking!
2606 * @return
2607 */
2608const Guid& Medium::getId() const
2609{
2610 return m->id;
2611}
2612
2613/**
2614 * Internal method to return the medium's state. Must have caller + locking!
2615 * @return
2616 */
2617MediumState_T Medium::getState() const
2618{
2619 return m->state;
2620}
2621
2622/**
2623 * Internal method to return the medium's variant. Must have caller + locking!
2624 * @return
2625 */
2626MediumVariant_T Medium::getVariant() const
2627{
2628 return m->variant;
2629}
2630
2631/**
2632 * Internal method which returns true if this medium represents a host drive.
2633 * @return
2634 */
2635bool Medium::isHostDrive() const
2636{
2637 return m->hostDrive;
2638}
2639
2640/**
2641 * Internal method to return the medium's location. Must have caller + locking!
2642 * @return
2643 */
2644const Utf8Str& Medium::getLocation() const
2645{
2646 return m->strLocation;
2647}
2648
2649/**
2650 * Internal method to return the medium's full location. Must have caller + locking!
2651 * @return
2652 */
2653const Utf8Str& Medium::getLocationFull() const
2654{
2655 return m->strLocationFull;
2656}
2657
2658/**
2659 * Internal method to return the medium's format string. Must have caller + locking!
2660 * @return
2661 */
2662const Utf8Str& Medium::getFormat() const
2663{
2664 return m->strFormat;
2665}
2666
2667/**
2668 * Internal method to return the medium's format object. Must have caller + locking!
2669 * @return
2670 */
2671const ComObjPtr<MediumFormat>& Medium::getMediumFormat() const
2672{
2673 return m->formatObj;
2674}
2675
2676/**
2677 * Internal method to return the medium's size. Must have caller + locking!
2678 * @return
2679 */
2680uint64_t Medium::getSize() const
2681{
2682 return m->size;
2683}
2684
2685/**
2686 * Adds the given machine and optionally the snapshot to the list of the objects
2687 * this medium is attached to.
2688 *
2689 * @param aMachineId Machine ID.
2690 * @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
2691 */
2692HRESULT Medium::addBackReference(const Guid &aMachineId,
2693 const Guid &aSnapshotId /*= Guid::Empty*/)
2694{
2695 AssertReturn(!aMachineId.isEmpty(), E_FAIL);
2696
2697 LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
2698
2699 AutoCaller autoCaller(this);
2700 AssertComRCReturnRC(autoCaller.rc());
2701
2702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 switch (m->state)
2705 {
2706 case MediumState_Created:
2707 case MediumState_Inaccessible:
2708 case MediumState_LockedRead:
2709 case MediumState_LockedWrite:
2710 break;
2711
2712 default:
2713 return setStateError();
2714 }
2715
2716 if (m->numCreateDiffTasks > 0)
2717 return setError(VBOX_E_OBJECT_IN_USE,
2718 tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created"),
2719 m->strLocationFull.c_str(),
2720 m->id.raw(),
2721 m->numCreateDiffTasks);
2722
2723 BackRefList::iterator it = std::find_if(m->backRefs.begin(),
2724 m->backRefs.end(),
2725 BackRef::EqualsTo(aMachineId));
2726 if (it == m->backRefs.end())
2727 {
2728 BackRef ref(aMachineId, aSnapshotId);
2729 m->backRefs.push_back(ref);
2730
2731 return S_OK;
2732 }
2733
2734 // if the caller has not supplied a snapshot ID, then we're attaching
2735 // to a machine a medium which represents the machine's current state,
2736 // so set the flag
2737 if (aSnapshotId.isEmpty())
2738 {
2739 /* sanity: no duplicate attachments */
2740 AssertReturn(!it->fInCurState, E_FAIL);
2741 it->fInCurState = true;
2742
2743 return S_OK;
2744 }
2745
2746 // otherwise: a snapshot medium is being attached
2747
2748 /* sanity: no duplicate attachments */
2749 for (BackRef::GuidList::const_iterator jt = it->llSnapshotIds.begin();
2750 jt != it->llSnapshotIds.end();
2751 ++jt)
2752 {
2753 const Guid &idOldSnapshot = *jt;
2754
2755 if (idOldSnapshot == aSnapshotId)
2756 {
2757#ifdef DEBUG
2758 dumpBackRefs();
2759#endif
2760 return setError(VBOX_E_OBJECT_IN_USE,
2761 tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
2762 m->strLocationFull.c_str(),
2763 m->id.raw(),
2764 aSnapshotId.raw(),
2765 idOldSnapshot.raw());
2766 }
2767 }
2768
2769 it->llSnapshotIds.push_back(aSnapshotId);
2770 it->fInCurState = false;
2771
2772 LogFlowThisFuncLeave();
2773
2774 return S_OK;
2775}
2776
2777/**
2778 * Removes the given machine and optionally the snapshot from the list of the
2779 * objects this medium is attached to.
2780 *
2781 * @param aMachineId Machine ID.
2782 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
2783 * attachment.
2784 */
2785HRESULT Medium::removeBackReference(const Guid &aMachineId,
2786 const Guid &aSnapshotId /*= Guid::Empty*/)
2787{
2788 AssertReturn(!aMachineId.isEmpty(), E_FAIL);
2789
2790 AutoCaller autoCaller(this);
2791 AssertComRCReturnRC(autoCaller.rc());
2792
2793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2794
2795 BackRefList::iterator it =
2796 std::find_if(m->backRefs.begin(), m->backRefs.end(),
2797 BackRef::EqualsTo(aMachineId));
2798 AssertReturn(it != m->backRefs.end(), E_FAIL);
2799
2800 if (aSnapshotId.isEmpty())
2801 {
2802 /* remove the current state attachment */
2803 it->fInCurState = false;
2804 }
2805 else
2806 {
2807 /* remove the snapshot attachment */
2808 BackRef::GuidList::iterator jt =
2809 std::find(it->llSnapshotIds.begin(), it->llSnapshotIds.end(), aSnapshotId);
2810
2811 AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
2812 it->llSnapshotIds.erase(jt);
2813 }
2814
2815 /* if the backref becomes empty, remove it */
2816 if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
2817 m->backRefs.erase(it);
2818
2819 return S_OK;
2820}
2821
2822/**
2823 * Internal method to return the medium's list of backrefs. Must have caller + locking!
2824 * @return
2825 */
2826const Guid* Medium::getFirstMachineBackrefId() const
2827{
2828 if (!m->backRefs.size())
2829 return NULL;
2830
2831 return &m->backRefs.front().machineId;
2832}
2833
2834const Guid* Medium::getFirstMachineBackrefSnapshotId() const
2835{
2836 if (!m->backRefs.size())
2837 return NULL;
2838
2839 const BackRef &ref = m->backRefs.front();
2840 if (!ref.llSnapshotIds.size())
2841 return NULL;
2842
2843 return &ref.llSnapshotIds.front();
2844}
2845
2846#ifdef DEBUG
2847/**
2848 * Debugging helper that gets called after VirtualBox initialization that writes all
2849 * machine backreferences to the debug log.
2850 */
2851void Medium::dumpBackRefs()
2852{
2853 AutoCaller autoCaller(this);
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
2857
2858 for (BackRefList::iterator it2 = m->backRefs.begin();
2859 it2 != m->backRefs.end();
2860 ++it2)
2861 {
2862 const BackRef &ref = *it2;
2863 LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d)\n", ref.machineId.raw(), ref.fInCurState));
2864
2865 for (BackRef::GuidList::const_iterator jt2 = it2->llSnapshotIds.begin();
2866 jt2 != it2->llSnapshotIds.end();
2867 ++jt2)
2868 {
2869 const Guid &id = *jt2;
2870 LogFlowThisFunc((" Backref from snapshot {%RTuuid}\n", id.raw()));
2871 }
2872 }
2873}
2874#endif
2875
2876/**
2877 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
2878 * of this media and updates it if necessary to reflect the new location.
2879 *
2880 * @param aOldPath Old path (full).
2881 * @param aNewPath New path (full).
2882 *
2883 * @note Locks this object for writing.
2884 */
2885HRESULT Medium::updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
2886{
2887 AssertReturn(!strOldPath.isEmpty(), E_FAIL);
2888 AssertReturn(!strNewPath.isEmpty(), E_FAIL);
2889
2890 AutoCaller autoCaller(this);
2891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2892
2893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
2896
2897 const char *pcszMediumPath = m->strLocationFull.c_str();
2898
2899 if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
2900 {
2901 Utf8Str newPath(strNewPath);
2902 newPath.append(pcszMediumPath + strOldPath.length());
2903 unconst(m->strLocationFull) = newPath;
2904
2905 Utf8Str path;
2906 m->pVirtualBox->copyPathRelativeToConfig(newPath, path);
2907 unconst(m->strLocation) = path;
2908
2909 LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
2910 }
2911
2912 return S_OK;
2913}
2914
2915/**
2916 * Returns the base medium of the media chain this medium is part of.
2917 *
2918 * The base medium is found by walking up the parent-child relationship axis.
2919 * If the medium doesn't have a parent (i.e. it's a base medium), it
2920 * returns itself in response to this method.
2921 *
2922 * @param aLevel Where to store the number of ancestors of this medium
2923 * (zero for the base), may be @c NULL.
2924 *
2925 * @note Locks medium tree for reading.
2926 */
2927ComObjPtr<Medium> Medium::getBase(uint32_t *aLevel /*= NULL*/)
2928{
2929 ComObjPtr<Medium> pBase;
2930 uint32_t level;
2931
2932 AutoCaller autoCaller(this);
2933 AssertReturn(autoCaller.isOk(), pBase);
2934
2935 /* we access mParent */
2936 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2937
2938 pBase = this;
2939 level = 0;
2940
2941 if (m->pParent)
2942 {
2943 for (;;)
2944 {
2945 AutoCaller baseCaller(pBase);
2946 AssertReturn(baseCaller.isOk(), pBase);
2947
2948 if (pBase->m->pParent.isNull())
2949 break;
2950
2951 pBase = pBase->m->pParent;
2952 ++level;
2953 }
2954 }
2955
2956 if (aLevel != NULL)
2957 *aLevel = level;
2958
2959 return pBase;
2960}
2961
2962/**
2963 * Returns @c true if this medium cannot be modified because it has
2964 * dependants (children) or is part of the snapshot. Related to the medium
2965 * type and posterity, not to the current media state.
2966 *
2967 * @note Locks this object and medium tree for reading.
2968 */
2969bool Medium::isReadOnly()
2970{
2971 AutoCaller autoCaller(this);
2972 AssertComRCReturn(autoCaller.rc(), false);
2973
2974 /* we access children */
2975 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2976
2977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2978
2979 switch (m->type)
2980 {
2981 case MediumType_Normal:
2982 {
2983 if (getChildren().size() != 0)
2984 return true;
2985
2986 for (BackRefList::const_iterator it = m->backRefs.begin();
2987 it != m->backRefs.end(); ++it)
2988 if (it->llSnapshotIds.size() != 0)
2989 return true;
2990
2991 return false;
2992 }
2993 case MediumType_Immutable:
2994 return true;
2995 case MediumType_Writethrough:
2996 case MediumType_Shareable:
2997 return false;
2998 default:
2999 break;
3000 }
3001
3002 AssertFailedReturn(false);
3003}
3004
3005/**
3006 * Saves medium data by appending a new child node to the given
3007 * parent XML settings node.
3008 *
3009 * @param data Settings struct to be updated.
3010 *
3011 * @note Locks this object, medium tree and children for reading.
3012 */
3013HRESULT Medium::saveSettings(settings::Medium &data)
3014{
3015 AutoCaller autoCaller(this);
3016 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3017
3018 /* we access mParent */
3019 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3020
3021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 data.uuid = m->id;
3024 data.strLocation = m->strLocation;
3025 data.strFormat = m->strFormat;
3026
3027 /* optional, only for diffs, default is false */
3028 if (m->pParent)
3029 data.fAutoReset = m->autoReset;
3030 else
3031 data.fAutoReset = false;
3032
3033 /* optional */
3034 data.strDescription = m->strDescription;
3035
3036 /* optional properties */
3037 data.properties.clear();
3038 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
3039 it != m->mapProperties.end();
3040 ++it)
3041 {
3042 /* only save properties that have non-default values */
3043 if (!it->second.isEmpty())
3044 {
3045 const Utf8Str &name = it->first;
3046 const Utf8Str &value = it->second;
3047 data.properties[name] = value;
3048 }
3049 }
3050
3051 /* only for base media */
3052 if (m->pParent.isNull())
3053 data.hdType = m->type;
3054
3055 /* save all children */
3056 for (MediaList::const_iterator it = getChildren().begin();
3057 it != getChildren().end();
3058 ++it)
3059 {
3060 settings::Medium med;
3061 HRESULT rc = (*it)->saveSettings(med);
3062 AssertComRCReturnRC(rc);
3063 data.llChildren.push_back(med);
3064 }
3065
3066 return S_OK;
3067}
3068
3069/**
3070 * Compares the location of this medium to the given location.
3071 *
3072 * The comparison takes the location details into account. For example, if the
3073 * location is a file in the host's filesystem, a case insensitive comparison
3074 * will be performed for case insensitive filesystems.
3075 *
3076 * @param aLocation Location to compare to (as is).
3077 * @param aResult Where to store the result of comparison: 0 if locations
3078 * are equal, 1 if this object's location is greater than
3079 * the specified location, and -1 otherwise.
3080 */
3081HRESULT Medium::compareLocationTo(const Utf8Str &strLocation, int &aResult)
3082{
3083 AutoCaller autoCaller(this);
3084 AssertComRCReturnRC(autoCaller.rc());
3085
3086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3087
3088 Utf8Str locationFull(m->strLocationFull);
3089
3090 /// @todo NEWMEDIA delegate the comparison to the backend?
3091
3092 if (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
3093 {
3094 Utf8Str location;
3095
3096 /* For locations represented by files, append the default path if
3097 * only the name is given, and then get the full path. */
3098 if (!RTPathHavePath(strLocation.c_str()))
3099 {
3100 m->pVirtualBox->getDefaultHardDiskFolder(location);
3101 location.append(RTPATH_DELIMITER);
3102 location.append(strLocation);
3103 }
3104 else
3105 location = strLocation;
3106
3107 int vrc = m->pVirtualBox->calculateFullPath(location, location);
3108 if (RT_FAILURE(vrc))
3109 return setError(VBOX_E_FILE_ERROR,
3110 tr("Invalid medium storage file location '%s' (%Rrc)"),
3111 location.c_str(),
3112 vrc);
3113
3114 aResult = RTPathCompare(locationFull.c_str(), location.c_str());
3115 }
3116 else
3117 aResult = locationFull.compare(strLocation);
3118
3119 return S_OK;
3120}
3121
3122/**
3123 * Constructs a medium lock list for this medium. The lock is not taken.
3124 *
3125 * @note Locks the medium tree for reading.
3126 *
3127 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
3128 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
3129 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
3130 * @param fMediumLockWrite Whether to associate a write lock with this medium.
3131 * @param pToBeParent Medium which will become the parent of this medium.
3132 * @param mediumLockList Where to store the resulting list.
3133 */
3134HRESULT Medium::createMediumLockList(bool fFailIfInaccessible,
3135 bool fMediumLockWrite,
3136 Medium *pToBeParent,
3137 MediumLockList &mediumLockList)
3138{
3139 AutoCaller autoCaller(this);
3140 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3141
3142 HRESULT rc = S_OK;
3143
3144 /* we access parent medium objects */
3145 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3146
3147 /* paranoid sanity checking if the medium has a to-be parent medium */
3148 if (pToBeParent)
3149 {
3150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3151 ComAssertRet(getParent().isNull(), E_FAIL);
3152 ComAssertRet(getChildren().size() == 0, E_FAIL);
3153 }
3154
3155 ErrorInfoKeeper eik;
3156 MultiResult mrc(S_OK);
3157
3158 ComObjPtr<Medium> pMedium = this;
3159 while (!pMedium.isNull())
3160 {
3161 // need write lock for RefreshState if medium is inaccessible
3162 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
3163
3164 /* Accessibility check must be first, otherwise locking interferes
3165 * with getting the medium state. Lock lists are not created for
3166 * fun, and thus getting the medium status is no luxury. */
3167 MediumState_T mediumState = pMedium->getState();
3168 if (mediumState == MediumState_Inaccessible)
3169 {
3170 rc = pMedium->RefreshState(&mediumState);
3171 if (FAILED(rc)) return rc;
3172
3173 if (mediumState == MediumState_Inaccessible)
3174 {
3175 // ignore inaccessible ISO media and silently return S_OK,
3176 // otherwise VM startup (esp. restore) may fail without good reason
3177 if (!fFailIfInaccessible)
3178 return S_OK;
3179
3180 // otherwise report an error
3181 Bstr error;
3182 rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
3183 if (FAILED(rc)) return rc;
3184
3185 /* collect multiple errors */
3186 eik.restore();
3187 Assert(!error.isEmpty());
3188 mrc = setError(E_FAIL,
3189 "%ls",
3190 error.raw());
3191 // error message will be something like
3192 // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
3193 eik.fetch();
3194 }
3195 }
3196
3197 if (pMedium == this)
3198 mediumLockList.Prepend(pMedium, fMediumLockWrite);
3199 else
3200 mediumLockList.Prepend(pMedium, false);
3201
3202 pMedium = pMedium->getParent();
3203 if (pMedium.isNull() && pToBeParent)
3204 {
3205 pMedium = pToBeParent;
3206 pToBeParent = NULL;
3207 }
3208 }
3209
3210 return mrc;
3211}
3212
3213/**
3214 * Returns a preferred format for differencing media.
3215 */
3216Utf8Str Medium::getPreferredDiffFormat()
3217{
3218 AutoCaller autoCaller(this);
3219 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
3220
3221 /* check that our own format supports diffs */
3222 if (!(m->formatObj->getCapabilities() & MediumFormatCapabilities_Differencing))
3223 {
3224 /* use the default format if not */
3225 Utf8Str tmp;
3226 m->pVirtualBox->getDefaultHardDiskFormat(tmp);
3227 return tmp;
3228 }
3229
3230 /* m->strFormat is const, no need to lock */
3231 return m->strFormat;
3232}
3233
3234/**
3235 * Returns the medium device type. Must have caller + locking!
3236 * @return
3237 */
3238DeviceType_T Medium::getDeviceType() const
3239{
3240 return m->devType;
3241}
3242
3243/**
3244 * Returns the medium type. Must have caller + locking!
3245 * @return
3246 */
3247MediumType_T Medium::getType() const
3248{
3249 return m->type;
3250}
3251
3252/**
3253 * Returns a short version of the location attribute.
3254 *
3255 * @note Must be called from under this object's read or write lock.
3256 */
3257Utf8Str Medium::getName()
3258{
3259 Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
3260 return name;
3261}
3262
3263/**
3264 * Sets the value of m->strLocation and calculates the value of m->strLocationFull.
3265 *
3266 * Treats non-FS-path locations specially, and prepends the default medium
3267 * folder if the given location string does not contain any path information
3268 * at all.
3269 *
3270 * Also, if the specified location is a file path that ends with '/' then the
3271 * file name part will be generated by this method automatically in the format
3272 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
3273 * and assign to this medium, and <ext> is the default extension for this
3274 * medium's storage format. Note that this procedure requires the media state to
3275 * be NotCreated and will return a failure otherwise.
3276 *
3277 * @param aLocation Location of the storage unit. If the location is a FS-path,
3278 * then it can be relative to the VirtualBox home directory.
3279 * @param aFormat Optional fallback format if it is an import and the format
3280 * cannot be determined.
3281 *
3282 * @note Must be called from under this object's write lock.
3283 */
3284HRESULT Medium::setLocation(const Utf8Str &aLocation,
3285 const Utf8Str &aFormat /* = Utf8Str::Empty */)
3286{
3287 AssertReturn(!aLocation.isEmpty(), E_FAIL);
3288
3289 AutoCaller autoCaller(this);
3290 AssertComRCReturnRC(autoCaller.rc());
3291
3292 /* formatObj may be null only when initializing from an existing path and
3293 * no format is known yet */
3294 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
3295 || ( autoCaller.state() == InInit
3296 && m->state != MediumState_NotCreated
3297 && m->id.isEmpty()
3298 && m->strFormat.isEmpty()
3299 && m->formatObj.isNull()),
3300 E_FAIL);
3301
3302 /* are we dealing with a new medium constructed using the existing
3303 * location? */
3304 bool isImport = m->strFormat.isEmpty();
3305
3306 if ( isImport
3307 || ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
3308 && !m->hostDrive))
3309 {
3310 Guid id;
3311
3312 Utf8Str location(aLocation);
3313
3314 if (m->state == MediumState_NotCreated)
3315 {
3316 /* must be a file (formatObj must be already known) */
3317 Assert(m->formatObj->getCapabilities() & MediumFormatCapabilities_File);
3318
3319 if (RTPathFilename(location.c_str()) == NULL)
3320 {
3321 /* no file name is given (either an empty string or ends with a
3322 * slash), generate a new UUID + file name if the state allows
3323 * this */
3324
3325 ComAssertMsgRet(!m->formatObj->getFileExtensions().empty(),
3326 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
3327 E_FAIL);
3328
3329 Utf8Str strExt = m->formatObj->getFileExtensions().front();
3330 ComAssertMsgRet(!strExt.isEmpty(),
3331 ("Default extension must not be empty\n"),
3332 E_FAIL);
3333
3334 id.create();
3335
3336 location = Utf8StrFmt("%s{%RTuuid}.%s",
3337 location.c_str(), id.raw(), strExt.c_str());
3338 }
3339 }
3340
3341 /* append the default folder if no path is given */
3342 if (!RTPathHavePath(location.c_str()))
3343 {
3344 Utf8Str tmp;
3345 m->pVirtualBox->getDefaultHardDiskFolder(tmp);
3346 tmp.append(RTPATH_DELIMITER);
3347 tmp.append(location);
3348 location = tmp;
3349 }
3350
3351 /* get the full file name */
3352 Utf8Str locationFull;
3353 int vrc = m->pVirtualBox->calculateFullPath(location, locationFull);
3354 if (RT_FAILURE(vrc))
3355 return setError(VBOX_E_FILE_ERROR,
3356 tr("Invalid medium storage file location '%s' (%Rrc)"),
3357 location.c_str(), vrc);
3358
3359 /* detect the backend from the storage unit if importing */
3360 if (isImport)
3361 {
3362 char *backendName = NULL;
3363
3364 /* is it a file? */
3365 {
3366 RTFILE file;
3367 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
3368 if (RT_SUCCESS(vrc))
3369 RTFileClose(file);
3370 }
3371 if (RT_SUCCESS(vrc))
3372 {
3373 vrc = VDGetFormat(NULL, locationFull.c_str(), &backendName);
3374 }
3375 else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
3376 {
3377 /* assume it's not a file, restore the original location */
3378 location = locationFull = aLocation;
3379 vrc = VDGetFormat(NULL, locationFull.c_str(), &backendName);
3380 }
3381
3382 if (RT_FAILURE(vrc))
3383 {
3384 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
3385 return setError(VBOX_E_FILE_ERROR,
3386 tr("Could not find file for the medium '%s' (%Rrc)"),
3387 locationFull.c_str(), vrc);
3388 else if (aFormat.isEmpty())
3389 return setError(VBOX_E_IPRT_ERROR,
3390 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
3391 locationFull.c_str(), vrc);
3392 else
3393 {
3394 HRESULT rc = setFormat(aFormat);
3395 /* setFormat() must not fail since we've just used the backend so
3396 * the format object must be there */
3397 AssertComRCReturnRC(rc);
3398 }
3399 }
3400 else
3401 {
3402 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
3403
3404 HRESULT rc = setFormat(backendName);
3405 RTStrFree(backendName);
3406
3407 /* setFormat() must not fail since we've just used the backend so
3408 * the format object must be there */
3409 AssertComRCReturnRC(rc);
3410 }
3411 }
3412
3413 /* is it still a file? */
3414 if (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
3415 {
3416 m->strLocation = location;
3417 m->strLocationFull = locationFull;
3418
3419 if (m->state == MediumState_NotCreated)
3420 {
3421 /* assign a new UUID (this UUID will be used when calling
3422 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
3423 * also do that if we didn't generate it to make sure it is
3424 * either generated by us or reset to null */
3425 unconst(m->id) = id;
3426 }
3427 }
3428 else
3429 {
3430 m->strLocation = locationFull;
3431 m->strLocationFull = locationFull;
3432 }
3433 }
3434 else
3435 {
3436 m->strLocation = aLocation;
3437 m->strLocationFull = aLocation;
3438 }
3439
3440 return S_OK;
3441}
3442
3443/**
3444 * Queries information from the medium.
3445 *
3446 * As a result of this call, the accessibility state and data members such as
3447 * size and description will be updated with the current information.
3448 *
3449 * @note This method may block during a system I/O call that checks storage
3450 * accessibility.
3451 *
3452 * @note Locks medium tree for reading and writing (for new diff media checked
3453 * for the first time). Locks mParent for reading. Locks this object for
3454 * writing.
3455 *
3456 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
3457 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs())
3458 * @return
3459 */
3460HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
3461{
3462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3463
3464 if ( m->state != MediumState_Created
3465 && m->state != MediumState_Inaccessible
3466 && m->state != MediumState_LockedRead)
3467 return E_FAIL;
3468
3469 HRESULT rc = S_OK;
3470
3471 int vrc = VINF_SUCCESS;
3472
3473 /* check if a blocking queryInfo() call is in progress on some other thread,
3474 * and wait for it to finish if so instead of querying data ourselves */
3475 if (m->queryInfoRunning)
3476 {
3477 Assert( m->state == MediumState_LockedRead
3478 || m->state == MediumState_LockedWrite);
3479
3480 alock.leave();
3481 vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
3482 alock.enter();
3483
3484 AssertRC(vrc);
3485
3486 return S_OK;
3487 }
3488
3489 bool success = false;
3490 Utf8Str lastAccessError;
3491
3492 /* are we dealing with a new medium constructed using the existing
3493 * location? */
3494 bool isImport = m->id.isEmpty();
3495 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
3496
3497 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
3498 * media because that would prevent necessary modifications
3499 * when opening media of some third-party formats for the first
3500 * time in VirtualBox (such as VMDK for which VDOpen() needs to
3501 * generate an UUID if it is missing) */
3502 if ( (m->hddOpenMode == OpenReadOnly)
3503 || !isImport
3504 )
3505 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
3506
3507 /* Open shareable medium with the appropriate flags */
3508 if (m->type == MediumType_Shareable)
3509 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
3510
3511 /* Lock the medium, which makes the behavior much more consistent */
3512 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY || VD_OPEN_FLAGS_SHAREABLE))
3513 rc = LockRead(NULL);
3514 else
3515 rc = LockWrite(NULL);
3516 if (FAILED(rc)) return rc;
3517
3518 /* Copies of the input state fields which are not read-only,
3519 * as we're dropping the lock. CAUTION: be extremely careful what
3520 * you do with the contents of this medium object, as you will
3521 * create races if there are concurrent changes. */
3522 Utf8Str format(m->strFormat);
3523 Utf8Str location(m->strLocationFull);
3524 ComObjPtr<MediumFormat> formatObj = m->formatObj;
3525
3526 /* "Output" values which can't be set because the lock isn't held
3527 * at the time the values are determined. */
3528 Guid mediumId = m->id;
3529 uint64_t mediumSize = 0;
3530 uint64_t mediumLogicalSize = 0;
3531
3532 /* leave the lock before a lengthy operation */
3533 vrc = RTSemEventMultiReset(m->queryInfoSem);
3534 AssertRCReturn(vrc, E_FAIL);
3535 m->queryInfoRunning = true;
3536 alock.leave();
3537
3538 try
3539 {
3540 /* skip accessibility checks for host drives */
3541 if (m->hostDrive)
3542 {
3543 success = true;
3544 throw S_OK;
3545 }
3546
3547 PVBOXHDD hdd;
3548 vrc = VDCreate(m->vdDiskIfaces, &hdd);
3549 ComAssertRCThrow(vrc, E_FAIL);
3550
3551 try
3552 {
3553 /** @todo This kind of opening of media is assuming that diff
3554 * media can be opened as base media. Should be documented that
3555 * it must work for all medium format backends. */
3556 vrc = VDOpen(hdd,
3557 format.c_str(),
3558 location.c_str(),
3559 uOpenFlags,
3560 m->vdDiskIfaces);
3561 if (RT_FAILURE(vrc))
3562 {
3563 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
3564 location.c_str(), vdError(vrc).c_str());
3565 throw S_OK;
3566 }
3567
3568 if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
3569 {
3570 /* Modify the UUIDs if necessary. The associated fields are
3571 * not modified by other code, so no need to copy. */
3572 if (fSetImageId)
3573 {
3574 vrc = VDSetUuid(hdd, 0, m->uuidImage);
3575 ComAssertRCThrow(vrc, E_FAIL);
3576 }
3577 if (fSetParentId)
3578 {
3579 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage);
3580 ComAssertRCThrow(vrc, E_FAIL);
3581 }
3582 /* zap the information, these are no long-term members */
3583 unconst(m->uuidImage).clear();
3584 unconst(m->uuidParentImage).clear();
3585
3586 /* check the UUID */
3587 RTUUID uuid;
3588 vrc = VDGetUuid(hdd, 0, &uuid);
3589 ComAssertRCThrow(vrc, E_FAIL);
3590
3591 if (isImport)
3592 {
3593 mediumId = uuid;
3594
3595 if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
3596 // only when importing a VDMK that has no UUID, create one in memory
3597 mediumId.create();
3598 }
3599 else
3600 {
3601 Assert(!mediumId.isEmpty());
3602
3603 if (mediumId != uuid)
3604 {
3605 lastAccessError = Utf8StrFmt(
3606 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
3607 &uuid,
3608 location.c_str(),
3609 mediumId.raw(),
3610 m->pVirtualBox->settingsFilePath().c_str());
3611 throw S_OK;
3612 }
3613 }
3614 }
3615 else
3616 {
3617 /* the backend does not support storing UUIDs within the
3618 * underlying storage so use what we store in XML */
3619
3620 /* generate an UUID for an imported UUID-less medium */
3621 if (isImport)
3622 {
3623 if (fSetImageId)
3624 mediumId = m->uuidImage;
3625 else
3626 mediumId.create();
3627 }
3628 }
3629
3630 /* get the medium variant */
3631 unsigned uImageFlags;
3632 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
3633 ComAssertRCThrow(vrc, E_FAIL);
3634 m->variant = (MediumVariant_T)uImageFlags;
3635
3636 /* check/get the parent uuid and update corresponding state */
3637 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
3638 {
3639 RTUUID parentId;
3640 vrc = VDGetParentUuid(hdd, 0, &parentId);
3641 ComAssertRCThrow(vrc, E_FAIL);
3642
3643 if (isImport)
3644 {
3645 /* the parent must be known to us. Note that we freely
3646 * call locking methods of mVirtualBox and parent, as all
3647 * relevant locks must be already held. There may be no
3648 * concurrent access to the just opened medium on other
3649 * threads yet (and init() will fail if this method reports
3650 * MediumState_Inaccessible) */
3651
3652 Guid id = parentId;
3653 ComObjPtr<Medium> pParent;
3654 rc = m->pVirtualBox->findHardDisk(&id, Utf8Str::Empty, false /* aSetError */, &pParent);
3655 if (FAILED(rc))
3656 {
3657 lastAccessError = Utf8StrFmt(
3658 tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
3659 &parentId, location.c_str(),
3660 m->pVirtualBox->settingsFilePath().c_str());
3661 throw S_OK;
3662 }
3663
3664 /* we set mParent & children() */
3665 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3666
3667 Assert(m->pParent.isNull());
3668 m->pParent = pParent;
3669 m->pParent->m->llChildren.push_back(this);
3670 }
3671 else
3672 {
3673 /* we access mParent */
3674 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3675
3676 /* check that parent UUIDs match. Note that there's no need
3677 * for the parent's AutoCaller (our lifetime is bound to
3678 * it) */
3679
3680 if (m->pParent.isNull())
3681 {
3682 lastAccessError = Utf8StrFmt(
3683 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
3684 location.c_str(),
3685 m->pVirtualBox->settingsFilePath().c_str());
3686 throw S_OK;
3687 }
3688
3689 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
3690 if ( m->pParent->getState() != MediumState_Inaccessible
3691 && m->pParent->getId() != parentId)
3692 {
3693 lastAccessError = Utf8StrFmt(
3694 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
3695 &parentId, location.c_str(),
3696 m->pParent->getId().raw(),
3697 m->pVirtualBox->settingsFilePath().c_str());
3698 throw S_OK;
3699 }
3700
3701 /// @todo NEWMEDIA what to do if the parent is not
3702 /// accessible while the diff is? Probably nothing. The
3703 /// real code will detect the mismatch anyway.
3704 }
3705 }
3706
3707 mediumSize = VDGetFileSize(hdd, 0);
3708 mediumLogicalSize = VDGetSize(hdd, 0) / _1M;
3709
3710 success = true;
3711 }
3712 catch (HRESULT aRC)
3713 {
3714 rc = aRC;
3715 }
3716
3717 VDDestroy(hdd);
3718
3719 }
3720 catch (HRESULT aRC)
3721 {
3722 rc = aRC;
3723 }
3724
3725 alock.enter();
3726
3727 if (isImport)
3728 unconst(m->id) = mediumId;
3729
3730 if (success)
3731 {
3732 m->size = mediumSize;
3733 m->logicalSize = mediumLogicalSize;
3734 m->strLastAccessError.setNull();
3735 }
3736 else
3737 {
3738 m->strLastAccessError = lastAccessError;
3739 LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
3740 location.c_str(), m->strLastAccessError.c_str(),
3741 rc, vrc));
3742 }
3743
3744 /* inform other callers if there are any */
3745 RTSemEventMultiSignal(m->queryInfoSem);
3746 m->queryInfoRunning = false;
3747
3748 /* Set the proper state according to the result of the check */
3749 if (success)
3750 m->preLockState = MediumState_Created;
3751 else
3752 m->preLockState = MediumState_Inaccessible;
3753
3754 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY || VD_OPEN_FLAGS_SHAREABLE))
3755 rc = UnlockRead(NULL);
3756 else
3757 rc = UnlockWrite(NULL);
3758 if (FAILED(rc)) return rc;
3759
3760 return rc;
3761}
3762
3763/**
3764 * Sets the extended error info according to the current media state.
3765 *
3766 * @note Must be called from under this object's write or read lock.
3767 */
3768HRESULT Medium::setStateError()
3769{
3770 HRESULT rc = E_FAIL;
3771
3772 switch (m->state)
3773 {
3774 case MediumState_NotCreated:
3775 {
3776 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3777 tr("Storage for the medium '%s' is not created"),
3778 m->strLocationFull.c_str());
3779 break;
3780 }
3781 case MediumState_Created:
3782 {
3783 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3784 tr("Storage for the medium '%s' is already created"),
3785 m->strLocationFull.c_str());
3786 break;
3787 }
3788 case MediumState_LockedRead:
3789 {
3790 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3791 tr("Medium '%s' is locked for reading by another task"),
3792 m->strLocationFull.c_str());
3793 break;
3794 }
3795 case MediumState_LockedWrite:
3796 {
3797 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3798 tr("Medium '%s' is locked for writing by another task"),
3799 m->strLocationFull.c_str());
3800 break;
3801 }
3802 case MediumState_Inaccessible:
3803 {
3804 /* be in sync with Console::powerUpThread() */
3805 if (!m->strLastAccessError.isEmpty())
3806 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3807 tr("Medium '%s' is not accessible. %s"),
3808 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
3809 else
3810 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3811 tr("Medium '%s' is not accessible"),
3812 m->strLocationFull.c_str());
3813 break;
3814 }
3815 case MediumState_Creating:
3816 {
3817 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3818 tr("Storage for the medium '%s' is being created"),
3819 m->strLocationFull.c_str());
3820 break;
3821 }
3822 case MediumState_Deleting:
3823 {
3824 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3825 tr("Storage for the medium '%s' is being deleted"),
3826 m->strLocationFull.c_str());
3827 break;
3828 }
3829 default:
3830 {
3831 AssertFailed();
3832 break;
3833 }
3834 }
3835
3836 return rc;
3837}
3838
3839/**
3840 * Implementation for the public Medium::Close() with the exception of calling
3841 * VirtualBox::saveSettings(), in case someone wants to call this for several
3842 * media.
3843 *
3844 * After this returns with success, uninit() has been called on the medium, and
3845 * the object is no longer usable ("not ready" state).
3846 *
3847 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3848 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3849 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
3850 * and this parameter is ignored.
3851 * @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here
3852 * upon which the Medium instance gets uninitialized.
3853 * @return
3854 */
3855HRESULT Medium::close(bool *pfNeedsSaveSettings, AutoCaller &autoCaller)
3856{
3857 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
3858 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
3859 this->lockHandle()
3860 COMMA_LOCKVAL_SRC_POS);
3861
3862 LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str()));
3863
3864 bool wasCreated = true;
3865
3866 switch (m->state)
3867 {
3868 case MediumState_NotCreated:
3869 wasCreated = false;
3870 break;
3871 case MediumState_Created:
3872 case MediumState_Inaccessible:
3873 break;
3874 default:
3875 return setStateError();
3876 }
3877
3878 if (m->backRefs.size() != 0)
3879 return setError(VBOX_E_OBJECT_IN_USE,
3880 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
3881 m->strLocationFull.c_str(), m->backRefs.size());
3882
3883 // perform extra media-dependent close checks
3884 HRESULT rc = canClose();
3885 if (FAILED(rc)) return rc;
3886
3887 if (wasCreated)
3888 {
3889 // remove from the list of known media before performing actual
3890 // uninitialization (to keep the media registry consistent on
3891 // failure to do so)
3892 rc = unregisterWithVirtualBox(pfNeedsSaveSettings);
3893 if (FAILED(rc)) return rc;
3894 }
3895
3896 // leave the AutoCaller, as otherwise uninit() will simply hang
3897 autoCaller.release();
3898
3899 // Keep the locks held until after uninit, as otherwise the consistency
3900 // of the medium tree cannot be guaranteed.
3901 uninit();
3902
3903 LogFlowFuncLeave();
3904
3905 return rc;
3906}
3907
3908/**
3909 * Deletes the medium storage unit.
3910 *
3911 * If @a aProgress is not NULL but the object it points to is @c null then a new
3912 * progress object will be created and assigned to @a *aProgress on success,
3913 * otherwise the existing progress object is used. If Progress is NULL, then no
3914 * progress object is created/used at all.
3915 *
3916 * When @a aWait is @c false, this method will create a thread to perform the
3917 * delete operation asynchronously and will return immediately. Otherwise, it
3918 * will perform the operation on the calling thread and will not return to the
3919 * caller until the operation is completed. Note that @a aProgress cannot be
3920 * NULL when @a aWait is @c false (this method will assert in this case).
3921 *
3922 * @param aProgress Where to find/store a Progress object to track operation
3923 * completion.
3924 * @param aWait @c true if this method should block instead of creating
3925 * an asynchronous thread.
3926 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3927 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3928 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
3929 * and this parameter is ignored.
3930 *
3931 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
3932 * writing.
3933 */
3934HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
3935 bool aWait,
3936 bool *pfNeedsSaveSettings)
3937{
3938 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
3939
3940 AutoCaller autoCaller(this);
3941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3942
3943 HRESULT rc = S_OK;
3944 ComObjPtr<Progress> pProgress;
3945 Medium::Task *pTask = NULL;
3946
3947 try
3948 {
3949 /* we're accessing the media tree, and canClose() needs it too */
3950 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
3951 this->lockHandle()
3952 COMMA_LOCKVAL_SRC_POS);
3953 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
3954
3955 if ( !(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
3956 | MediumFormatCapabilities_CreateFixed)))
3957 throw setError(VBOX_E_NOT_SUPPORTED,
3958 tr("Medium format '%s' does not support storage deletion"),
3959 m->strFormat.c_str());
3960
3961 /* Note that we are fine with Inaccessible state too: a) for symmetry
3962 * with create calls and b) because it doesn't really harm to try, if
3963 * it is really inaccessible, the delete operation will fail anyway.
3964 * Accepting Inaccessible state is especially important because all
3965 * registered media are initially Inaccessible upon VBoxSVC startup
3966 * until COMGETTER(RefreshState) is called. Accept Deleting state
3967 * because some callers need to put the medium in this state early
3968 * to prevent races. */
3969 switch (m->state)
3970 {
3971 case MediumState_Created:
3972 case MediumState_Deleting:
3973 case MediumState_Inaccessible:
3974 break;
3975 default:
3976 throw setStateError();
3977 }
3978
3979 if (m->backRefs.size() != 0)
3980 {
3981 Utf8Str strMachines;
3982 for (BackRefList::const_iterator it = m->backRefs.begin();
3983 it != m->backRefs.end();
3984 ++it)
3985 {
3986 const BackRef &b = *it;
3987 if (strMachines.length())
3988 strMachines.append(", ");
3989 strMachines.append(b.machineId.toString().c_str());
3990 }
3991#ifdef DEBUG
3992 dumpBackRefs();
3993#endif
3994 throw setError(VBOX_E_OBJECT_IN_USE,
3995 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
3996 m->strLocationFull.c_str(),
3997 m->backRefs.size(),
3998 strMachines.c_str());
3999 }
4000
4001 rc = canClose();
4002 if (FAILED(rc))
4003 throw rc;
4004
4005 /* go to Deleting state, so that the medium is not actually locked */
4006 if (m->state != MediumState_Deleting)
4007 {
4008 rc = markForDeletion();
4009 if (FAILED(rc))
4010 throw rc;
4011 }
4012
4013 /* Build the medium lock list. */
4014 MediumLockList *pMediumLockList(new MediumLockList());
4015 rc = createMediumLockList(true /* fFailIfInaccessible */,
4016 true /* fMediumLockWrite */,
4017 NULL,
4018 *pMediumLockList);
4019 if (FAILED(rc))
4020 {
4021 delete pMediumLockList;
4022 throw rc;
4023 }
4024
4025 rc = pMediumLockList->Lock();
4026 if (FAILED(rc))
4027 {
4028 delete pMediumLockList;
4029 throw setError(rc,
4030 tr("Failed to lock media when deleting '%s'"),
4031 getLocationFull().c_str());
4032 }
4033
4034 /* try to remove from the list of known media before performing
4035 * actual deletion (we favor the consistency of the media registry
4036 * which would have been broken if unregisterWithVirtualBox() failed
4037 * after we successfully deleted the storage) */
4038 rc = unregisterWithVirtualBox(pfNeedsSaveSettings);
4039 if (FAILED(rc))
4040 throw rc;
4041 // no longer need lock
4042 multilock.release();
4043
4044 if (aProgress != NULL)
4045 {
4046 /* use the existing progress object... */
4047 pProgress = *aProgress;
4048
4049 /* ...but create a new one if it is null */
4050 if (pProgress.isNull())
4051 {
4052 pProgress.createObject();
4053 rc = pProgress->init(m->pVirtualBox,
4054 static_cast<IMedium*>(this),
4055 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()),
4056 FALSE /* aCancelable */);
4057 if (FAILED(rc))
4058 throw rc;
4059 }
4060 }
4061
4062 /* setup task object to carry out the operation sync/async */
4063 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
4064 rc = pTask->rc();
4065 AssertComRC(rc);
4066 if (FAILED(rc))
4067 throw rc;
4068 }
4069 catch (HRESULT aRC) { rc = aRC; }
4070
4071 if (SUCCEEDED(rc))
4072 {
4073 if (aWait)
4074 rc = runNow(pTask, NULL /* pfNeedsSaveSettings*/);
4075 else
4076 rc = startThread(pTask);
4077
4078 if (SUCCEEDED(rc) && aProgress != NULL)
4079 *aProgress = pProgress;
4080
4081 }
4082 else
4083 {
4084 if (pTask)
4085 delete pTask;
4086
4087 /* Undo deleting state if necessary. */
4088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4089 unmarkForDeletion();
4090 }
4091
4092 return rc;
4093}
4094
4095/**
4096 * Mark a medium for deletion.
4097 *
4098 * @note Caller must hold the write lock on this medium!
4099 */
4100HRESULT Medium::markForDeletion()
4101{
4102 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4103 switch (m->state)
4104 {
4105 case MediumState_Created:
4106 case MediumState_Inaccessible:
4107 m->preLockState = m->state;
4108 m->state = MediumState_Deleting;
4109 return S_OK;
4110 default:
4111 return setStateError();
4112 }
4113}
4114
4115/**
4116 * Removes the "mark for deletion".
4117 *
4118 * @note Caller must hold the write lock on this medium!
4119 */
4120HRESULT Medium::unmarkForDeletion()
4121{
4122 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4123 switch (m->state)
4124 {
4125 case MediumState_Deleting:
4126 m->state = m->preLockState;
4127 return S_OK;
4128 default:
4129 return setStateError();
4130 }
4131}
4132
4133/**
4134 * Mark a medium for deletion which is in locked state.
4135 *
4136 * @note Caller must hold the write lock on this medium!
4137 */
4138HRESULT Medium::markLockedForDeletion()
4139{
4140 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4141 if ( ( m->state == MediumState_LockedRead
4142 || m->state == MediumState_LockedWrite)
4143 && m->preLockState == MediumState_Created)
4144 {
4145 m->preLockState = MediumState_Deleting;
4146 return S_OK;
4147 }
4148 else
4149 return setStateError();
4150}
4151
4152/**
4153 * Removes the "mark for deletion" for a medium in locked state.
4154 *
4155 * @note Caller must hold the write lock on this medium!
4156 */
4157HRESULT Medium::unmarkLockedForDeletion()
4158{
4159 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4160 if ( ( m->state == MediumState_LockedRead
4161 || m->state == MediumState_LockedWrite)
4162 && m->preLockState == MediumState_Deleting)
4163 {
4164 m->preLockState = MediumState_Created;
4165 return S_OK;
4166 }
4167 else
4168 return setStateError();
4169}
4170
4171/**
4172 * Creates a new differencing storage unit using the format of the given target
4173 * medium and the location. Note that @c aTarget must be NotCreated.
4174 *
4175 * The @a aMediumLockList parameter contains the associated medium lock list,
4176 * which must be in locked state. If @a aWait is @c true then the caller is
4177 * responsible for unlocking.
4178 *
4179 * If @a aProgress is not NULL but the object it points to is @c null then a
4180 * new progress object will be created and assigned to @a *aProgress on
4181 * success, otherwise the existing progress object is used. If @a aProgress is
4182 * NULL, then no progress object is created/used at all.
4183 *
4184 * When @a aWait is @c false, this method will create a thread to perform the
4185 * create operation asynchronously and will return immediately. Otherwise, it
4186 * will perform the operation on the calling thread and will not return to the
4187 * caller until the operation is completed. Note that @a aProgress cannot be
4188 * NULL when @a aWait is @c false (this method will assert in this case).
4189 *
4190 * @param aTarget Target medium.
4191 * @param aVariant Precise medium variant to create.
4192 * @param aMediumLockList List of media which should be locked.
4193 * @param aProgress Where to find/store a Progress object to track
4194 * operation completion.
4195 * @param aWait @c true if this method should block instead of
4196 * creating an asynchronous thread.
4197 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been
4198 * initialized to false and that will be set to true
4199 * by this function if the caller should invoke
4200 * VirtualBox::saveSettings() because the global
4201 * settings have changed. This only works in "wait"
4202 * mode; otherwise saveSettings is called
4203 * automatically by the thread that was created,
4204 * and this parameter is ignored.
4205 *
4206 * @note Locks this object and @a aTarget for writing.
4207 */
4208HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
4209 MediumVariant_T aVariant,
4210 MediumLockList *aMediumLockList,
4211 ComObjPtr<Progress> *aProgress,
4212 bool aWait,
4213 bool *pfNeedsSaveSettings)
4214{
4215 AssertReturn(!aTarget.isNull(), E_FAIL);
4216 AssertReturn(aMediumLockList, E_FAIL);
4217 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4218
4219 AutoCaller autoCaller(this);
4220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4221
4222 AutoCaller targetCaller(aTarget);
4223 if (FAILED(targetCaller.rc())) return targetCaller.rc();
4224
4225 HRESULT rc = S_OK;
4226 ComObjPtr<Progress> pProgress;
4227 Medium::Task *pTask = NULL;
4228
4229 try
4230 {
4231 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
4232
4233 ComAssertThrow( m->type != MediumType_Writethrough
4234 && m->type != MediumType_Shareable, E_FAIL);
4235 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
4236
4237 if (aTarget->m->state != MediumState_NotCreated)
4238 throw aTarget->setStateError();
4239
4240 /* Check that the medium is not attached to the current state of
4241 * any VM referring to it. */
4242 for (BackRefList::const_iterator it = m->backRefs.begin();
4243 it != m->backRefs.end();
4244 ++it)
4245 {
4246 if (it->fInCurState)
4247 {
4248 /* Note: when a VM snapshot is being taken, all normal media
4249 * attached to the VM in the current state will be, as an
4250 * exception, also associated with the snapshot which is about
4251 * to create (see SnapshotMachine::init()) before deassociating
4252 * them from the current state (which takes place only on
4253 * success in Machine::fixupHardDisks()), so that the size of
4254 * snapshotIds will be 1 in this case. The extra condition is
4255 * used to filter out this legal situation. */
4256 if (it->llSnapshotIds.size() == 0)
4257 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4258 tr("Medium '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing media based on it may be created until it is detached"),
4259 m->strLocationFull.c_str(), it->machineId.raw());
4260
4261 Assert(it->llSnapshotIds.size() == 1);
4262 }
4263 }
4264
4265 if (aProgress != NULL)
4266 {
4267 /* use the existing progress object... */
4268 pProgress = *aProgress;
4269
4270 /* ...but create a new one if it is null */
4271 if (pProgress.isNull())
4272 {
4273 pProgress.createObject();
4274 rc = pProgress->init(m->pVirtualBox,
4275 static_cast<IMedium*>(this),
4276 BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()),
4277 TRUE /* aCancelable */);
4278 if (FAILED(rc))
4279 throw rc;
4280 }
4281 }
4282
4283 /* setup task object to carry out the operation sync/async */
4284 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
4285 aMediumLockList,
4286 aWait /* fKeepMediumLockList */);
4287 rc = pTask->rc();
4288 AssertComRC(rc);
4289 if (FAILED(rc))
4290 throw rc;
4291
4292 /* register a task (it will deregister itself when done) */
4293 ++m->numCreateDiffTasks;
4294 Assert(m->numCreateDiffTasks != 0); /* overflow? */
4295
4296 aTarget->m->state = MediumState_Creating;
4297 }
4298 catch (HRESULT aRC) { rc = aRC; }
4299
4300 if (SUCCEEDED(rc))
4301 {
4302 if (aWait)
4303 rc = runNow(pTask, pfNeedsSaveSettings);
4304 else
4305 rc = startThread(pTask);
4306
4307 if (SUCCEEDED(rc) && aProgress != NULL)
4308 *aProgress = pProgress;
4309 }
4310 else if (pTask != NULL)
4311 delete pTask;
4312
4313 return rc;
4314}
4315
4316/**
4317 * Prepares this (source) medium, target medium and all intermediate media
4318 * for the merge operation.
4319 *
4320 * This method is to be called prior to calling the #mergeTo() to perform
4321 * necessary consistency checks and place involved media to appropriate
4322 * states. If #mergeTo() is not called or fails, the state modifications
4323 * performed by this method must be undone by #cancelMergeTo().
4324 *
4325 * See #mergeTo() for more information about merging.
4326 *
4327 * @param pTarget Target medium.
4328 * @param aMachineId Allowed machine attachment. NULL means do not check.
4329 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
4330 * do not check.
4331 * @param fLockMedia Flag whether to lock the medium lock list or not.
4332 * If set to false and the medium lock list locking fails
4333 * later you must call #cancelMergeTo().
4334 * @param fMergeForward Resulting merge direction (out).
4335 * @param pParentForTarget New parent for target medium after merge (out).
4336 * @param aChildrenToReparent List of children of the source which will have
4337 * to be reparented to the target after merge (out).
4338 * @param aMediumLockList Medium locking information (out).
4339 *
4340 * @note Locks medium tree for reading. Locks this object, aTarget and all
4341 * intermediate media for writing.
4342 */
4343HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
4344 const Guid *aMachineId,
4345 const Guid *aSnapshotId,
4346 bool fLockMedia,
4347 bool &fMergeForward,
4348 ComObjPtr<Medium> &pParentForTarget,
4349 MediaList &aChildrenToReparent,
4350 MediumLockList * &aMediumLockList)
4351{
4352 AssertReturn(pTarget != NULL, E_FAIL);
4353 AssertReturn(pTarget != this, E_FAIL);
4354
4355 AutoCaller autoCaller(this);
4356 AssertComRCReturnRC(autoCaller.rc());
4357
4358 AutoCaller targetCaller(pTarget);
4359 AssertComRCReturnRC(targetCaller.rc());
4360
4361 HRESULT rc = S_OK;
4362 fMergeForward = false;
4363 pParentForTarget.setNull();
4364 aChildrenToReparent.clear();
4365 Assert(aMediumLockList == NULL);
4366 aMediumLockList = NULL;
4367
4368 try
4369 {
4370 // locking: we need the tree lock first because we access parent pointers
4371 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4372
4373 /* more sanity checking and figuring out the merge direction */
4374 ComObjPtr<Medium> pMedium = getParent();
4375 while (!pMedium.isNull() && pMedium != pTarget)
4376 pMedium = pMedium->getParent();
4377 if (pMedium == pTarget)
4378 fMergeForward = false;
4379 else
4380 {
4381 pMedium = pTarget->getParent();
4382 while (!pMedium.isNull() && pMedium != this)
4383 pMedium = pMedium->getParent();
4384 if (pMedium == this)
4385 fMergeForward = true;
4386 else
4387 {
4388 Utf8Str tgtLoc;
4389 {
4390 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4391 tgtLoc = pTarget->getLocationFull();
4392 }
4393
4394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4395 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4396 tr("Media '%s' and '%s' are unrelated"),
4397 m->strLocationFull.c_str(), tgtLoc.c_str());
4398 }
4399 }
4400
4401 /* Build the lock list. */
4402 aMediumLockList = new MediumLockList();
4403 if (fMergeForward)
4404 rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
4405 true /* fMediumLockWrite */,
4406 NULL,
4407 *aMediumLockList);
4408 else
4409 rc = createMediumLockList(true /* fFailIfInaccessible */,
4410 false /* fMediumLockWrite */,
4411 NULL,
4412 *aMediumLockList);
4413 if (FAILED(rc))
4414 throw rc;
4415
4416 /* Sanity checking, must be after lock list creation as it depends on
4417 * valid medium states. The medium objects must be accessible. Only
4418 * do this if immediate locking is requested, otherwise it fails when
4419 * we construct a medium lock list for an already running VM. Snapshot
4420 * deletion uses this to simplify its life. */
4421 if (fLockMedia)
4422 {
4423 {
4424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4425 if (m->state != MediumState_Created)
4426 throw setStateError();
4427 }
4428 {
4429 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4430 if (pTarget->m->state != MediumState_Created)
4431 throw pTarget->setStateError();
4432 }
4433 }
4434
4435 /* check medium attachment and other sanity conditions */
4436 if (fMergeForward)
4437 {
4438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4439 if (getChildren().size() > 1)
4440 {
4441 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4442 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4443 m->strLocationFull.c_str(), getChildren().size());
4444 }
4445 /* One backreference is only allowed if the machine ID is not empty
4446 * and it matches the machine the medium is attached to (including
4447 * the snapshot ID if not empty). */
4448 if ( m->backRefs.size() != 0
4449 && ( !aMachineId
4450 || m->backRefs.size() != 1
4451 || aMachineId->isEmpty()
4452 || *getFirstMachineBackrefId() != *aMachineId
4453 || ( (!aSnapshotId || !aSnapshotId->isEmpty())
4454 && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
4455 throw setError(VBOX_E_OBJECT_IN_USE,
4456 tr("Medium '%s' is attached to %d virtual machines"),
4457 m->strLocationFull.c_str(), m->backRefs.size());
4458 if (m->type == MediumType_Immutable)
4459 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4460 tr("Medium '%s' is immutable"),
4461 m->strLocationFull.c_str());
4462 }
4463 else
4464 {
4465 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4466 if (pTarget->getChildren().size() > 1)
4467 {
4468 throw setError(VBOX_E_OBJECT_IN_USE,
4469 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4470 pTarget->m->strLocationFull.c_str(),
4471 pTarget->getChildren().size());
4472 }
4473 if (pTarget->m->type == MediumType_Immutable)
4474 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4475 tr("Medium '%s' is immutable"),
4476 pTarget->m->strLocationFull.c_str());
4477 }
4478 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
4479 ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
4480 for (pLast = pLastIntermediate;
4481 !pLast.isNull() && pLast != pTarget && pLast != this;
4482 pLast = pLast->getParent())
4483 {
4484 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4485 if (pLast->getChildren().size() > 1)
4486 {
4487 throw setError(VBOX_E_OBJECT_IN_USE,
4488 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4489 pLast->m->strLocationFull.c_str(),
4490 pLast->getChildren().size());
4491 }
4492 if (pLast->m->backRefs.size() != 0)
4493 throw setError(VBOX_E_OBJECT_IN_USE,
4494 tr("Medium '%s' is attached to %d virtual machines"),
4495 pLast->m->strLocationFull.c_str(),
4496 pLast->m->backRefs.size());
4497
4498 }
4499
4500 /* Update medium states appropriately */
4501 if (m->state == MediumState_Created)
4502 {
4503 rc = markForDeletion();
4504 if (FAILED(rc))
4505 throw rc;
4506 }
4507 else
4508 {
4509 if (fLockMedia)
4510 throw setStateError();
4511 else if ( m->state == MediumState_LockedWrite
4512 || m->state == MediumState_LockedRead)
4513 {
4514 /* Either mark it for deletiion in locked state or allow
4515 * others to have done so. */
4516 if (m->preLockState == MediumState_Created)
4517 markLockedForDeletion();
4518 else if (m->preLockState != MediumState_Deleting)
4519 throw setStateError();
4520 }
4521 else
4522 throw setStateError();
4523 }
4524
4525 if (fMergeForward)
4526 {
4527 /* we will need parent to reparent target */
4528 pParentForTarget = m->pParent;
4529 }
4530 else
4531 {
4532 /* we will need to reparent children of the source */
4533 for (MediaList::const_iterator it = getChildren().begin();
4534 it != getChildren().end();
4535 ++it)
4536 {
4537 pMedium = *it;
4538 if (fLockMedia)
4539 {
4540 rc = pMedium->LockWrite(NULL);
4541 if (FAILED(rc))
4542 throw rc;
4543 }
4544
4545 aChildrenToReparent.push_back(pMedium);
4546 }
4547 }
4548 for (pLast = pLastIntermediate;
4549 !pLast.isNull() && pLast != pTarget && pLast != this;
4550 pLast = pLast->getParent())
4551 {
4552 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4553 if (pLast->m->state == MediumState_Created)
4554 {
4555 rc = pLast->markForDeletion();
4556 if (FAILED(rc))
4557 throw rc;
4558 }
4559 else
4560 throw pLast->setStateError();
4561 }
4562
4563 /* Tweak the lock list in the backward merge case, as the target
4564 * isn't marked to be locked for writing yet. */
4565 if (!fMergeForward)
4566 {
4567 MediumLockList::Base::iterator lockListBegin =
4568 aMediumLockList->GetBegin();
4569 MediumLockList::Base::iterator lockListEnd =
4570 aMediumLockList->GetEnd();
4571 lockListEnd--;
4572 for (MediumLockList::Base::iterator it = lockListBegin;
4573 it != lockListEnd;
4574 ++it)
4575 {
4576 MediumLock &mediumLock = *it;
4577 if (mediumLock.GetMedium() == pTarget)
4578 {
4579 HRESULT rc2 = mediumLock.UpdateLock(true);
4580 AssertComRC(rc2);
4581 break;
4582 }
4583 }
4584 }
4585
4586 if (fLockMedia)
4587 {
4588 rc = aMediumLockList->Lock();
4589 if (FAILED(rc))
4590 {
4591 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4592 throw setError(rc,
4593 tr("Failed to lock media when merging to '%s'"),
4594 pTarget->getLocationFull().c_str());
4595 }
4596 }
4597 }
4598 catch (HRESULT aRC) { rc = aRC; }
4599
4600 if (FAILED(rc))
4601 {
4602 delete aMediumLockList;
4603 aMediumLockList = NULL;
4604 }
4605
4606 return rc;
4607}
4608
4609/**
4610 * Merges this medium to the specified medium which must be either its
4611 * direct ancestor or descendant.
4612 *
4613 * Given this medium is SOURCE and the specified medium is TARGET, we will
4614 * get two variants of the merge operation:
4615 *
4616 * forward merge
4617 * ------------------------->
4618 * [Extra] <- SOURCE <- Intermediate <- TARGET
4619 * Any Del Del LockWr
4620 *
4621 *
4622 * backward merge
4623 * <-------------------------
4624 * TARGET <- Intermediate <- SOURCE <- [Extra]
4625 * LockWr Del Del LockWr
4626 *
4627 * Each diagram shows the involved media on the media chain where
4628 * SOURCE and TARGET belong. Under each medium there is a state value which
4629 * the medium must have at a time of the mergeTo() call.
4630 *
4631 * The media in the square braces may be absent (e.g. when the forward
4632 * operation takes place and SOURCE is the base medium, or when the backward
4633 * merge operation takes place and TARGET is the last child in the chain) but if
4634 * they present they are involved too as shown.
4635 *
4636 * Neither the source medium nor intermediate media may be attached to
4637 * any VM directly or in the snapshot, otherwise this method will assert.
4638 *
4639 * The #prepareMergeTo() method must be called prior to this method to place all
4640 * involved to necessary states and perform other consistency checks.
4641 *
4642 * If @a aWait is @c true then this method will perform the operation on the
4643 * calling thread and will not return to the caller until the operation is
4644 * completed. When this method succeeds, all intermediate medium objects in
4645 * the chain will be uninitialized, the state of the target medium (and all
4646 * involved extra media) will be restored. @a aMediumLockList will not be
4647 * deleted, whether the operation is successful or not. The caller has to do
4648 * this if appropriate. Note that this (source) medium is not uninitialized
4649 * because of possible AutoCaller instances held by the caller of this method
4650 * on the current thread. It's therefore the responsibility of the caller to
4651 * call Medium::uninit() after releasing all callers.
4652 *
4653 * If @a aWait is @c false then this method will create a thread to perform the
4654 * operation asynchronously and will return immediately. If the operation
4655 * succeeds, the thread will uninitialize the source medium object and all
4656 * intermediate medium objects in the chain, reset the state of the target
4657 * medium (and all involved extra media) and delete @a aMediumLockList.
4658 * If the operation fails, the thread will only reset the states of all
4659 * involved media and delete @a aMediumLockList.
4660 *
4661 * When this method fails (regardless of the @a aWait mode), it is a caller's
4662 * responsiblity to undo state changes and delete @a aMediumLockList using
4663 * #cancelMergeTo().
4664 *
4665 * If @a aProgress is not NULL but the object it points to is @c null then a new
4666 * progress object will be created and assigned to @a *aProgress on success,
4667 * otherwise the existing progress object is used. If Progress is NULL, then no
4668 * progress object is created/used at all. Note that @a aProgress cannot be
4669 * NULL when @a aWait is @c false (this method will assert in this case).
4670 *
4671 * @param pTarget Target medium.
4672 * @param fMergeForward Merge direction.
4673 * @param pParentForTarget New parent for target medium after merge.
4674 * @param aChildrenToReparent List of children of the source which will have
4675 * to be reparented to the target after merge.
4676 * @param aMediumLockList Medium locking information.
4677 * @param aProgress Where to find/store a Progress object to track operation
4678 * completion.
4679 * @param aWait @c true if this method should block instead of creating
4680 * an asynchronous thread.
4681 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
4682 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
4683 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
4684 * and this parameter is ignored.
4685 *
4686 * @note Locks the tree lock for writing. Locks the media from the chain
4687 * for writing.
4688 */
4689HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
4690 bool fMergeForward,
4691 const ComObjPtr<Medium> &pParentForTarget,
4692 const MediaList &aChildrenToReparent,
4693 MediumLockList *aMediumLockList,
4694 ComObjPtr <Progress> *aProgress,
4695 bool aWait,
4696 bool *pfNeedsSaveSettings)
4697{
4698 AssertReturn(pTarget != NULL, E_FAIL);
4699 AssertReturn(pTarget != this, E_FAIL);
4700 AssertReturn(aMediumLockList != NULL, E_FAIL);
4701 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4702
4703 AutoCaller autoCaller(this);
4704 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4705
4706 AutoCaller targetCaller(pTarget);
4707 AssertComRCReturnRC(targetCaller.rc());
4708
4709 HRESULT rc = S_OK;
4710 ComObjPtr <Progress> pProgress;
4711 Medium::Task *pTask = NULL;
4712
4713 try
4714 {
4715 if (aProgress != NULL)
4716 {
4717 /* use the existing progress object... */
4718 pProgress = *aProgress;
4719
4720 /* ...but create a new one if it is null */
4721 if (pProgress.isNull())
4722 {
4723 Utf8Str tgtName;
4724 {
4725 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4726 tgtName = pTarget->getName();
4727 }
4728
4729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4730
4731 pProgress.createObject();
4732 rc = pProgress->init(m->pVirtualBox,
4733 static_cast<IMedium*>(this),
4734 BstrFmt(tr("Merging medium '%s' to '%s'"),
4735 getName().c_str(),
4736 tgtName.c_str()),
4737 TRUE /* aCancelable */);
4738 if (FAILED(rc))
4739 throw rc;
4740 }
4741 }
4742
4743 /* setup task object to carry out the operation sync/async */
4744 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
4745 pParentForTarget, aChildrenToReparent,
4746 pProgress, aMediumLockList,
4747 aWait /* fKeepMediumLockList */);
4748 rc = pTask->rc();
4749 AssertComRC(rc);
4750 if (FAILED(rc))
4751 throw rc;
4752 }
4753 catch (HRESULT aRC) { rc = aRC; }
4754
4755 if (SUCCEEDED(rc))
4756 {
4757 if (aWait)
4758 rc = runNow(pTask, pfNeedsSaveSettings);
4759 else
4760 rc = startThread(pTask);
4761
4762 if (SUCCEEDED(rc) && aProgress != NULL)
4763 *aProgress = pProgress;
4764 }
4765 else if (pTask != NULL)
4766 delete pTask;
4767
4768 return rc;
4769}
4770
4771/**
4772 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
4773 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
4774 * the medium objects in @a aChildrenToReparent.
4775 *
4776 * @param aChildrenToReparent List of children of the source which will have
4777 * to be reparented to the target after merge.
4778 * @param aMediumLockList Medium locking information.
4779 *
4780 * @note Locks the media from the chain for writing.
4781 */
4782void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
4783 MediumLockList *aMediumLockList)
4784{
4785 AutoCaller autoCaller(this);
4786 AssertComRCReturnVoid(autoCaller.rc());
4787
4788 AssertReturnVoid(aMediumLockList != NULL);
4789
4790 /* Revert media marked for deletion to previous state. */
4791 HRESULT rc;
4792 MediumLockList::Base::const_iterator mediumListBegin =
4793 aMediumLockList->GetBegin();
4794 MediumLockList::Base::const_iterator mediumListEnd =
4795 aMediumLockList->GetEnd();
4796 for (MediumLockList::Base::const_iterator it = mediumListBegin;
4797 it != mediumListEnd;
4798 ++it)
4799 {
4800 const MediumLock &mediumLock = *it;
4801 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4802 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4803
4804 if (pMedium->m->state == MediumState_Deleting)
4805 {
4806 rc = pMedium->unmarkForDeletion();
4807 AssertComRC(rc);
4808 }
4809 }
4810
4811 /* the destructor will do the work */
4812 delete aMediumLockList;
4813
4814 /* unlock the children which had to be reparented */
4815 for (MediaList::const_iterator it = aChildrenToReparent.begin();
4816 it != aChildrenToReparent.end();
4817 ++it)
4818 {
4819 const ComObjPtr<Medium> &pMedium = *it;
4820
4821 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4822 pMedium->UnlockWrite(NULL);
4823 }
4824}
4825
4826////////////////////////////////////////////////////////////////////////////////
4827//
4828// Private methods
4829//
4830////////////////////////////////////////////////////////////////////////////////
4831
4832/**
4833 * Performs extra checks if the medium can be closed and returns S_OK in
4834 * this case. Otherwise, returns a respective error message. Called by
4835 * Close() under the medium tree lock and the medium lock.
4836 *
4837 * @note Also reused by Medium::Reset().
4838 *
4839 * @note Caller must hold the media tree write lock!
4840 */
4841HRESULT Medium::canClose()
4842{
4843 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4844
4845 if (getChildren().size() != 0)
4846 return setError(VBOX_E_OBJECT_IN_USE,
4847 tr("Cannot close medium '%s' because it has %d child media"),
4848 m->strLocationFull.c_str(), getChildren().size());
4849
4850 return S_OK;
4851}
4852
4853/**
4854 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
4855 *
4856 * This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending
4857 * on the device type of this medium.
4858 *
4859 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
4860 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
4861 *
4862 * @note Caller must have locked the media tree lock for writing!
4863 */
4864HRESULT Medium::unregisterWithVirtualBox(bool *pfNeedsSaveSettings)
4865{
4866 /* Note that we need to de-associate ourselves from the parent to let
4867 * unregisterHardDisk() properly save the registry */
4868
4869 /* we modify mParent and access children */
4870 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4871
4872 Medium *pParentBackup = m->pParent;
4873 AssertReturn(getChildren().size() == 0, E_FAIL);
4874 if (m->pParent)
4875 deparent();
4876
4877 HRESULT rc = E_FAIL;
4878 switch (m->devType)
4879 {
4880 case DeviceType_DVD:
4881 rc = m->pVirtualBox->unregisterImage(this, DeviceType_DVD, pfNeedsSaveSettings);
4882 break;
4883
4884 case DeviceType_Floppy:
4885 rc = m->pVirtualBox->unregisterImage(this, DeviceType_Floppy, pfNeedsSaveSettings);
4886 break;
4887
4888 case DeviceType_HardDisk:
4889 rc = m->pVirtualBox->unregisterHardDisk(this, pfNeedsSaveSettings);
4890 break;
4891
4892 default:
4893 break;
4894 }
4895
4896 if (FAILED(rc))
4897 {
4898 if (pParentBackup)
4899 {
4900 // re-associate with the parent as we are still relatives in the registry
4901 m->pParent = pParentBackup;
4902 m->pParent->m->llChildren.push_back(this);
4903 }
4904 }
4905
4906 return rc;
4907}
4908
4909/**
4910 * Checks that the format ID is valid and sets it on success.
4911 *
4912 * Note that this method will caller-reference the format object on success!
4913 * This reference must be released somewhere to let the MediumFormat object be
4914 * uninitialized.
4915 *
4916 * @note Must be called from under this object's write lock.
4917 */
4918HRESULT Medium::setFormat(const Utf8Str &aFormat)
4919{
4920 /* get the format object first */
4921 {
4922 SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
4923 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
4924
4925 unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
4926 if (m->formatObj.isNull())
4927 return setError(E_INVALIDARG,
4928 tr("Invalid medium storage format '%s'"),
4929 aFormat.c_str());
4930
4931 /* reference the format permanently to prevent its unexpected
4932 * uninitialization */
4933 HRESULT rc = m->formatObj->addCaller();
4934 AssertComRCReturnRC(rc);
4935
4936 /* get properties (preinsert them as keys in the map). Note that the
4937 * map doesn't grow over the object life time since the set of
4938 * properties is meant to be constant. */
4939
4940 Assert(m->mapProperties.empty());
4941
4942 for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
4943 it != m->formatObj->getProperties().end();
4944 ++it)
4945 {
4946 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
4947 }
4948 }
4949
4950 unconst(m->strFormat) = aFormat;
4951
4952 return S_OK;
4953}
4954
4955/**
4956 * Returns the last error message collected by the vdErrorCall callback and
4957 * resets it.
4958 *
4959 * The error message is returned prepended with a dot and a space, like this:
4960 * <code>
4961 * ". <error_text> (%Rrc)"
4962 * </code>
4963 * to make it easily appendable to a more general error message. The @c %Rrc
4964 * format string is given @a aVRC as an argument.
4965 *
4966 * If there is no last error message collected by vdErrorCall or if it is a
4967 * null or empty string, then this function returns the following text:
4968 * <code>
4969 * " (%Rrc)"
4970 * </code>
4971 *
4972 * @note Doesn't do any object locking; it is assumed that the caller makes sure
4973 * the callback isn't called by more than one thread at a time.
4974 *
4975 * @param aVRC VBox error code to use when no error message is provided.
4976 */
4977Utf8Str Medium::vdError(int aVRC)
4978{
4979 Utf8Str error;
4980
4981 if (m->vdError.isEmpty())
4982 error = Utf8StrFmt(" (%Rrc)", aVRC);
4983 else
4984 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
4985
4986 m->vdError.setNull();
4987
4988 return error;
4989}
4990
4991/**
4992 * Error message callback.
4993 *
4994 * Puts the reported error message to the m->vdError field.
4995 *
4996 * @note Doesn't do any object locking; it is assumed that the caller makes sure
4997 * the callback isn't called by more than one thread at a time.
4998 *
4999 * @param pvUser The opaque data passed on container creation.
5000 * @param rc The VBox error code.
5001 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
5002 * @param pszFormat Error message format string.
5003 * @param va Error message arguments.
5004 */
5005/*static*/
5006DECLCALLBACK(void) Medium::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
5007 const char *pszFormat, va_list va)
5008{
5009 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
5010
5011 Medium *that = static_cast<Medium*>(pvUser);
5012 AssertReturnVoid(that != NULL);
5013
5014 if (that->m->vdError.isEmpty())
5015 that->m->vdError =
5016 Utf8StrFmt("%s (%Rrc)", Utf8StrFmtVA(pszFormat, va).c_str(), rc);
5017 else
5018 that->m->vdError =
5019 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
5020 Utf8StrFmtVA(pszFormat, va).c_str(), rc);
5021}
5022
5023/* static */
5024DECLCALLBACK(bool) Medium::vdConfigAreKeysValid(void *pvUser,
5025 const char * /* pszzValid */)
5026{
5027 Medium *that = static_cast<Medium*>(pvUser);
5028 AssertReturn(that != NULL, false);
5029
5030 /* we always return true since the only keys we have are those found in
5031 * VDBACKENDINFO */
5032 return true;
5033}
5034
5035/* static */
5036DECLCALLBACK(int) Medium::vdConfigQuerySize(void *pvUser,
5037 const char *pszName,
5038 size_t *pcbValue)
5039{
5040 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
5041
5042 Medium *that = static_cast<Medium*>(pvUser);
5043 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
5044
5045 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
5046 if (it == that->m->mapProperties.end())
5047 return VERR_CFGM_VALUE_NOT_FOUND;
5048
5049 /* we interpret null values as "no value" in Medium */
5050 if (it->second.isEmpty())
5051 return VERR_CFGM_VALUE_NOT_FOUND;
5052
5053 *pcbValue = it->second.length() + 1 /* include terminator */;
5054
5055 return VINF_SUCCESS;
5056}
5057
5058/* static */
5059DECLCALLBACK(int) Medium::vdConfigQuery(void *pvUser,
5060 const char *pszName,
5061 char *pszValue,
5062 size_t cchValue)
5063{
5064 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
5065
5066 Medium *that = static_cast<Medium*>(pvUser);
5067 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
5068
5069 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
5070 if (it == that->m->mapProperties.end())
5071 return VERR_CFGM_VALUE_NOT_FOUND;
5072
5073 /* we interpret null values as "no value" in Medium */
5074 if (it->second.isEmpty())
5075 return VERR_CFGM_VALUE_NOT_FOUND;
5076
5077 const Utf8Str &value = it->second;
5078 if (value.length() >= cchValue)
5079 return VERR_CFGM_NOT_ENOUGH_SPACE;
5080
5081 memcpy(pszValue, value.c_str(), value.length() + 1);
5082
5083 return VINF_SUCCESS;
5084}
5085
5086DECLCALLBACK(int) Medium::vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
5087{
5088 PVDSOCKETINT pSocketInt = NULL;
5089
5090 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
5091 return VERR_NOT_SUPPORTED;
5092
5093 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
5094 if (!pSocketInt)
5095 return VERR_NO_MEMORY;
5096
5097 pSocketInt->hSocket = NIL_RTSOCKET;
5098 *pSock = pSocketInt;
5099 return VINF_SUCCESS;
5100}
5101
5102DECLCALLBACK(int) Medium::vdTcpSocketDestroy(VDSOCKET Sock)
5103{
5104 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5105
5106 if (pSocketInt->hSocket != NIL_RTSOCKET)
5107 RTTcpClientClose(pSocketInt->hSocket);
5108
5109 RTMemFree(pSocketInt);
5110
5111 return VINF_SUCCESS;
5112}
5113
5114DECLCALLBACK(int) Medium::vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
5115{
5116 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5117
5118 return RTTcpClientConnect(pszAddress, uPort, &pSocketInt->hSocket);
5119}
5120
5121DECLCALLBACK(int) Medium::vdTcpClientClose(VDSOCKET Sock)
5122{
5123 int rc = VINF_SUCCESS;
5124 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5125
5126 rc = RTTcpClientClose(pSocketInt->hSocket);
5127 pSocketInt->hSocket = NIL_RTSOCKET;
5128 return rc;
5129}
5130
5131DECLCALLBACK(bool) Medium::vdTcpIsClientConnected(VDSOCKET Sock)
5132{
5133 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5134 return pSocketInt->hSocket != NIL_RTSOCKET;
5135}
5136
5137DECLCALLBACK(int) Medium::vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
5138{
5139 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5140 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
5141}
5142
5143DECLCALLBACK(int) Medium::vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
5144{
5145 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5146 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
5147}
5148
5149DECLCALLBACK(int) Medium::vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
5150{
5151 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5152 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
5153}
5154
5155DECLCALLBACK(int) Medium::vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
5156{
5157 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5158 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
5159}
5160
5161DECLCALLBACK(int) Medium::vdTcpFlush(VDSOCKET Sock)
5162{
5163 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5164 return RTTcpFlush(pSocketInt->hSocket);
5165}
5166
5167DECLCALLBACK(int) Medium::vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
5168{
5169 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5170 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
5171}
5172
5173DECLCALLBACK(int) Medium::vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
5174{
5175 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5176 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
5177}
5178
5179DECLCALLBACK(int) Medium::vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
5180{
5181 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5182 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
5183}
5184
5185
5186/**
5187 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
5188 *
5189 * @note When the task is executed by this method, IProgress::notifyComplete()
5190 * is automatically called for the progress object associated with this
5191 * task when the task is finished to signal the operation completion for
5192 * other threads asynchronously waiting for it.
5193 */
5194HRESULT Medium::startThread(Medium::Task *pTask)
5195{
5196#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
5197 /* Extreme paranoia: The calling thread should not hold the medium
5198 * tree lock or any medium lock. Since there is no separate lock class
5199 * for medium objects be even more strict: no other object locks. */
5200 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
5201 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
5202#endif
5203
5204 /// @todo use a more descriptive task name
5205 int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
5206 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
5207 "Medium::Task");
5208 if (RT_FAILURE(vrc))
5209 {
5210 delete pTask;
5211 return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n", vrc);
5212 }
5213
5214 return S_OK;
5215}
5216
5217/**
5218 * Fix the parent UUID of all children to point to this medium as their
5219 * parent.
5220 */
5221HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
5222{
5223 MediumLockList mediumLockList;
5224 HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
5225 false /* fMediumLockWrite */,
5226 this,
5227 mediumLockList);
5228 AssertComRCReturnRC(rc);
5229
5230 try
5231 {
5232 PVBOXHDD hdd;
5233 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5234 ComAssertRCThrow(vrc, E_FAIL);
5235
5236 try
5237 {
5238 MediumLockList::Base::iterator lockListBegin =
5239 mediumLockList.GetBegin();
5240 MediumLockList::Base::iterator lockListEnd =
5241 mediumLockList.GetEnd();
5242 for (MediumLockList::Base::iterator it = lockListBegin;
5243 it != lockListEnd;
5244 ++it)
5245 {
5246 MediumLock &mediumLock = *it;
5247 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5248 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5249
5250 // open the medium
5251 vrc = VDOpen(hdd,
5252 pMedium->m->strFormat.c_str(),
5253 pMedium->m->strLocationFull.c_str(),
5254 VD_OPEN_FLAGS_READONLY,
5255 pMedium->m->vdDiskIfaces);
5256 if (RT_FAILURE(vrc))
5257 throw vrc;
5258 }
5259
5260 for (MediaList::const_iterator it = childrenToReparent.begin();
5261 it != childrenToReparent.end();
5262 ++it)
5263 {
5264 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
5265 vrc = VDOpen(hdd,
5266 (*it)->m->strFormat.c_str(),
5267 (*it)->m->strLocationFull.c_str(),
5268 VD_OPEN_FLAGS_INFO,
5269 (*it)->m->vdDiskIfaces);
5270 if (RT_FAILURE(vrc))
5271 throw vrc;
5272
5273 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id);
5274 if (RT_FAILURE(vrc))
5275 throw vrc;
5276
5277 vrc = VDClose(hdd, false /* fDelete */);
5278 if (RT_FAILURE(vrc))
5279 throw vrc;
5280
5281 (*it)->UnlockWrite(NULL);
5282 }
5283 }
5284 catch (HRESULT aRC) { rc = aRC; }
5285 catch (int aVRC)
5286 {
5287 throw setError(E_FAIL,
5288 tr("Could not update medium UUID references to parent '%s' (%s)"),
5289 m->strLocationFull.c_str(),
5290 vdError(aVRC).c_str());
5291 }
5292
5293 VDDestroy(hdd);
5294 }
5295 catch (HRESULT aRC) { rc = aRC; }
5296
5297 return rc;
5298}
5299
5300/**
5301 * Runs Medium::Task::handler() on the current thread instead of creating
5302 * a new one.
5303 *
5304 * This call implies that it is made on another temporary thread created for
5305 * some asynchronous task. Avoid calling it from a normal thread since the task
5306 * operations are potentially lengthy and will block the calling thread in this
5307 * case.
5308 *
5309 * @note When the task is executed by this method, IProgress::notifyComplete()
5310 * is not called for the progress object associated with this task when
5311 * the task is finished. Instead, the result of the operation is returned
5312 * by this method directly and it's the caller's responsibility to
5313 * complete the progress object in this case.
5314 */
5315HRESULT Medium::runNow(Medium::Task *pTask,
5316 bool *pfNeedsSaveSettings)
5317{
5318#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
5319 /* Extreme paranoia: The calling thread should not hold the medium
5320 * tree lock or any medium lock. Since there is no separate lock class
5321 * for medium objects be even more strict: no other object locks. */
5322 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
5323 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
5324#endif
5325
5326 pTask->m_pfNeedsSaveSettings = pfNeedsSaveSettings;
5327
5328 /* NIL_RTTHREAD indicates synchronous call. */
5329 return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
5330}
5331
5332/**
5333 * Implementation code for the "create base" task.
5334 *
5335 * This only gets started from Medium::CreateBaseStorage() and always runs
5336 * asynchronously. As a result, we always save the VirtualBox.xml file when
5337 * we're done here.
5338 *
5339 * @param task
5340 * @return
5341 */
5342HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
5343{
5344 HRESULT rc = S_OK;
5345
5346 /* these parameters we need after creation */
5347 uint64_t size = 0, logicalSize = 0;
5348 MediumVariant_T variant = MediumVariant_Standard;
5349 bool fGenerateUuid = false;
5350
5351 try
5352 {
5353 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
5354
5355 /* The object may request a specific UUID (through a special form of
5356 * the setLocation() argument). Otherwise we have to generate it */
5357 Guid id = m->id;
5358 fGenerateUuid = id.isEmpty();
5359 if (fGenerateUuid)
5360 {
5361 id.create();
5362 /* VirtualBox::registerHardDisk() will need UUID */
5363 unconst(m->id) = id;
5364 }
5365
5366 Utf8Str format(m->strFormat);
5367 Utf8Str location(m->strLocationFull);
5368 uint64_t capabilities = m->formatObj->getCapabilities();
5369 ComAssertThrow(capabilities & ( VD_CAP_CREATE_FIXED
5370 | VD_CAP_CREATE_DYNAMIC), E_FAIL);
5371 Assert(m->state == MediumState_Creating);
5372
5373 PVBOXHDD hdd;
5374 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5375 ComAssertRCThrow(vrc, E_FAIL);
5376
5377 /* unlock before the potentially lengthy operation */
5378 thisLock.release();
5379
5380 try
5381 {
5382 /* ensure the directory exists */
5383 rc = VirtualBox::ensureFilePathExists(location);
5384 if (FAILED(rc))
5385 throw rc;
5386
5387 PDMMEDIAGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
5388
5389 vrc = VDCreateBase(hdd,
5390 format.c_str(),
5391 location.c_str(),
5392 task.mSize * _1M,
5393 task.mVariant,
5394 NULL,
5395 &geo,
5396 &geo,
5397 id.raw(),
5398 VD_OPEN_FLAGS_NORMAL,
5399 NULL,
5400 task.mVDOperationIfaces);
5401 if (RT_FAILURE(vrc))
5402 throw setError(VBOX_E_FILE_ERROR,
5403 tr("Could not create the medium storage unit '%s'%s"),
5404 location.c_str(), vdError(vrc).c_str());
5405
5406 size = VDGetFileSize(hdd, 0);
5407 logicalSize = VDGetSize(hdd, 0) / _1M;
5408 unsigned uImageFlags;
5409 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5410 if (RT_SUCCESS(vrc))
5411 variant = (MediumVariant_T)uImageFlags;
5412 }
5413 catch (HRESULT aRC) { rc = aRC; }
5414
5415 VDDestroy(hdd);
5416 }
5417 catch (HRESULT aRC) { rc = aRC; }
5418
5419 if (SUCCEEDED(rc))
5420 {
5421 /* register with mVirtualBox as the last step and move to
5422 * Created state only on success (leaving an orphan file is
5423 * better than breaking media registry consistency) */
5424 bool fNeedsSaveSettings = false;
5425 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5426 rc = m->pVirtualBox->registerHardDisk(this, &fNeedsSaveSettings);
5427 treeLock.release();
5428
5429 if (fNeedsSaveSettings)
5430 {
5431 AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
5432 m->pVirtualBox->saveSettings();
5433 }
5434 }
5435
5436 // reenter the lock before changing state
5437 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
5438
5439 if (SUCCEEDED(rc))
5440 {
5441 m->state = MediumState_Created;
5442
5443 m->size = size;
5444 m->logicalSize = logicalSize;
5445 m->variant = variant;
5446 }
5447 else
5448 {
5449 /* back to NotCreated on failure */
5450 m->state = MediumState_NotCreated;
5451
5452 /* reset UUID to prevent it from being reused next time */
5453 if (fGenerateUuid)
5454 unconst(m->id).clear();
5455 }
5456
5457 return rc;
5458}
5459
5460/**
5461 * Implementation code for the "create diff" task.
5462 *
5463 * This task always gets started from Medium::createDiffStorage() and can run
5464 * synchronously or asynchronously depending on the "wait" parameter passed to
5465 * that function. If we run synchronously, the caller expects the bool
5466 * *pfNeedsSaveSettings to be set before returning; otherwise (in asynchronous
5467 * mode), we save the settings ourselves.
5468 *
5469 * @param task
5470 * @return
5471 */
5472HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
5473{
5474 HRESULT rc = S_OK;
5475
5476 bool fNeedsSaveSettings = false;
5477
5478 const ComObjPtr<Medium> &pTarget = task.mTarget;
5479
5480 uint64_t size = 0, logicalSize = 0;
5481 MediumVariant_T variant = MediumVariant_Standard;
5482 bool fGenerateUuid = false;
5483
5484 try
5485 {
5486 /* Lock both in {parent,child} order. */
5487 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
5488
5489 /* The object may request a specific UUID (through a special form of
5490 * the setLocation() argument). Otherwise we have to generate it */
5491 Guid targetId = pTarget->m->id;
5492 fGenerateUuid = targetId.isEmpty();
5493 if (fGenerateUuid)
5494 {
5495 targetId.create();
5496 /* VirtualBox::registerHardDisk() will need UUID */
5497 unconst(pTarget->m->id) = targetId;
5498 }
5499
5500 Guid id = m->id;
5501
5502 Utf8Str targetFormat(pTarget->m->strFormat);
5503 Utf8Str targetLocation(pTarget->m->strLocationFull);
5504 uint64_t capabilities = m->formatObj->getCapabilities();
5505 ComAssertThrow(capabilities & VD_CAP_CREATE_DYNAMIC, E_FAIL);
5506
5507 Assert(pTarget->m->state == MediumState_Creating);
5508 Assert(m->state == MediumState_LockedRead);
5509
5510 PVBOXHDD hdd;
5511 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5512 ComAssertRCThrow(vrc, E_FAIL);
5513
5514 /* the two media are now protected by their non-default states;
5515 * unlock the media before the potentially lengthy operation */
5516 mediaLock.release();
5517
5518 try
5519 {
5520 /* Open all media in the target chain but the last. */
5521 MediumLockList::Base::const_iterator targetListBegin =
5522 task.mpMediumLockList->GetBegin();
5523 MediumLockList::Base::const_iterator targetListEnd =
5524 task.mpMediumLockList->GetEnd();
5525 for (MediumLockList::Base::const_iterator it = targetListBegin;
5526 it != targetListEnd;
5527 ++it)
5528 {
5529 const MediumLock &mediumLock = *it;
5530 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5531
5532 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5533
5534 /* Skip over the target diff medium */
5535 if (pMedium->m->state == MediumState_Creating)
5536 continue;
5537
5538 /* sanity check */
5539 Assert(pMedium->m->state == MediumState_LockedRead);
5540
5541 /* Open all media in appropriate mode. */
5542 vrc = VDOpen(hdd,
5543 pMedium->m->strFormat.c_str(),
5544 pMedium->m->strLocationFull.c_str(),
5545 VD_OPEN_FLAGS_READONLY,
5546 pMedium->m->vdDiskIfaces);
5547 if (RT_FAILURE(vrc))
5548 throw setError(VBOX_E_FILE_ERROR,
5549 tr("Could not open the medium storage unit '%s'%s"),
5550 pMedium->m->strLocationFull.c_str(),
5551 vdError(vrc).c_str());
5552 }
5553
5554 /* ensure the target directory exists */
5555 rc = VirtualBox::ensureFilePathExists(targetLocation);
5556 if (FAILED(rc))
5557 throw rc;
5558
5559 vrc = VDCreateDiff(hdd,
5560 targetFormat.c_str(),
5561 targetLocation.c_str(),
5562 task.mVariant | VD_IMAGE_FLAGS_DIFF,
5563 NULL,
5564 targetId.raw(),
5565 id.raw(),
5566 VD_OPEN_FLAGS_NORMAL,
5567 pTarget->m->vdDiskIfaces,
5568 task.mVDOperationIfaces);
5569 if (RT_FAILURE(vrc))
5570 throw setError(VBOX_E_FILE_ERROR,
5571 tr("Could not create the differencing medium storage unit '%s'%s"),
5572 targetLocation.c_str(), vdError(vrc).c_str());
5573
5574 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
5575 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE) / _1M;
5576 unsigned uImageFlags;
5577 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5578 if (RT_SUCCESS(vrc))
5579 variant = (MediumVariant_T)uImageFlags;
5580 }
5581 catch (HRESULT aRC) { rc = aRC; }
5582
5583 VDDestroy(hdd);
5584 }
5585 catch (HRESULT aRC) { rc = aRC; }
5586
5587 if (SUCCEEDED(rc))
5588 {
5589 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5590
5591 Assert(pTarget->m->pParent.isNull());
5592
5593 /* associate the child with the parent */
5594 pTarget->m->pParent = this;
5595 m->llChildren.push_back(pTarget);
5596
5597 /** @todo r=klaus neither target nor base() are locked,
5598 * potential race! */
5599 /* diffs for immutable media are auto-reset by default */
5600 pTarget->m->autoReset = (getBase()->m->type == MediumType_Immutable);
5601
5602 /* register with mVirtualBox as the last step and move to
5603 * Created state only on success (leaving an orphan file is
5604 * better than breaking media registry consistency) */
5605 rc = m->pVirtualBox->registerHardDisk(pTarget, &fNeedsSaveSettings);
5606
5607 if (FAILED(rc))
5608 /* break the parent association on failure to register */
5609 deparent();
5610 }
5611
5612 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
5613
5614 if (SUCCEEDED(rc))
5615 {
5616 pTarget->m->state = MediumState_Created;
5617
5618 pTarget->m->size = size;
5619 pTarget->m->logicalSize = logicalSize;
5620 pTarget->m->variant = variant;
5621 }
5622 else
5623 {
5624 /* back to NotCreated on failure */
5625 pTarget->m->state = MediumState_NotCreated;
5626
5627 pTarget->m->autoReset = false;
5628
5629 /* reset UUID to prevent it from being reused next time */
5630 if (fGenerateUuid)
5631 unconst(pTarget->m->id).clear();
5632 }
5633
5634 // deregister the task registered in createDiffStorage()
5635 Assert(m->numCreateDiffTasks != 0);
5636 --m->numCreateDiffTasks;
5637
5638 if (task.isAsync())
5639 {
5640 if (fNeedsSaveSettings)
5641 {
5642 // save the global settings; for that we should hold only the VirtualBox lock
5643 mediaLock.release();
5644 AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
5645 m->pVirtualBox->saveSettings();
5646 }
5647 }
5648 else
5649 // synchronous mode: report save settings result to caller
5650 if (task.m_pfNeedsSaveSettings)
5651 *task.m_pfNeedsSaveSettings = fNeedsSaveSettings;
5652
5653 /* Note that in sync mode, it's the caller's responsibility to
5654 * unlock the medium. */
5655
5656 return rc;
5657}
5658
5659/**
5660 * Implementation code for the "merge" task.
5661 *
5662 * This task always gets started from Medium::mergeTo() and can run
5663 * synchronously or asynchrously depending on the "wait" parameter passed to
5664 * that function. If we run synchronously, the caller expects the bool
5665 * *pfNeedsSaveSettings to be set before returning; otherwise (in asynchronous
5666 * mode), we save the settings ourselves.
5667 *
5668 * @param task
5669 * @return
5670 */
5671HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
5672{
5673 HRESULT rc = S_OK;
5674
5675 const ComObjPtr<Medium> &pTarget = task.mTarget;
5676
5677 try
5678 {
5679 PVBOXHDD hdd;
5680 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5681 ComAssertRCThrow(vrc, E_FAIL);
5682
5683 try
5684 {
5685 // Similar code appears in SessionMachine::onlineMergeMedium, so
5686 // if you make any changes below check whether they are applicable
5687 // in that context as well.
5688
5689 unsigned uTargetIdx = VD_LAST_IMAGE;
5690 unsigned uSourceIdx = VD_LAST_IMAGE;
5691 /* Open all media in the chain. */
5692 MediumLockList::Base::iterator lockListBegin =
5693 task.mpMediumLockList->GetBegin();
5694 MediumLockList::Base::iterator lockListEnd =
5695 task.mpMediumLockList->GetEnd();
5696 unsigned i = 0;
5697 for (MediumLockList::Base::iterator it = lockListBegin;
5698 it != lockListEnd;
5699 ++it)
5700 {
5701 MediumLock &mediumLock = *it;
5702 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5703
5704 if (pMedium == this)
5705 uSourceIdx = i;
5706 else if (pMedium == pTarget)
5707 uTargetIdx = i;
5708
5709 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5710
5711 /*
5712 * complex sanity (sane complexity)
5713 *
5714 * The current medium must be in the Deleting (medium is merged)
5715 * or LockedRead (parent medium) state if it is not the target.
5716 * If it is the target it must be in the LockedWrite state.
5717 */
5718 Assert( ( pMedium != pTarget
5719 && ( pMedium->m->state == MediumState_Deleting
5720 || pMedium->m->state == MediumState_LockedRead))
5721 || ( pMedium == pTarget
5722 && pMedium->m->state == MediumState_LockedWrite));
5723
5724 /*
5725 * Medium must be the target, in the LockedRead state
5726 * or Deleting state where it is not allowed to be attached
5727 * to a virtual machine.
5728 */
5729 Assert( pMedium == pTarget
5730 || pMedium->m->state == MediumState_LockedRead
5731 || ( pMedium->m->backRefs.size() == 0
5732 && pMedium->m->state == MediumState_Deleting));
5733 /* The source medium must be in Deleting state. */
5734 Assert( pMedium != this
5735 || pMedium->m->state == MediumState_Deleting);
5736
5737 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
5738
5739 if ( pMedium->m->state == MediumState_LockedRead
5740 || pMedium->m->state == MediumState_Deleting)
5741 uOpenFlags = VD_OPEN_FLAGS_READONLY;
5742 if (pMedium->m->type == MediumType_Shareable)
5743 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
5744
5745 /* Open the medium */
5746 vrc = VDOpen(hdd,
5747 pMedium->m->strFormat.c_str(),
5748 pMedium->m->strLocationFull.c_str(),
5749 uOpenFlags,
5750 pMedium->m->vdDiskIfaces);
5751 if (RT_FAILURE(vrc))
5752 throw vrc;
5753
5754 i++;
5755 }
5756
5757 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
5758 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
5759
5760 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
5761 task.mVDOperationIfaces);
5762 if (RT_FAILURE(vrc))
5763 throw vrc;
5764
5765 /* update parent UUIDs */
5766 if (!task.mfMergeForward)
5767 {
5768 /* we need to update UUIDs of all source's children
5769 * which cannot be part of the container at once so
5770 * add each one in there individually */
5771 if (task.mChildrenToReparent.size() > 0)
5772 {
5773 for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
5774 it != task.mChildrenToReparent.end();
5775 ++it)
5776 {
5777 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
5778 vrc = VDOpen(hdd,
5779 (*it)->m->strFormat.c_str(),
5780 (*it)->m->strLocationFull.c_str(),
5781 VD_OPEN_FLAGS_INFO,
5782 (*it)->m->vdDiskIfaces);
5783 if (RT_FAILURE(vrc))
5784 throw vrc;
5785
5786 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
5787 pTarget->m->id);
5788 if (RT_FAILURE(vrc))
5789 throw vrc;
5790
5791 vrc = VDClose(hdd, false /* fDelete */);
5792 if (RT_FAILURE(vrc))
5793 throw vrc;
5794
5795 (*it)->UnlockWrite(NULL);
5796 }
5797 }
5798 }
5799 }
5800 catch (HRESULT aRC) { rc = aRC; }
5801 catch (int aVRC)
5802 {
5803 throw setError(VBOX_E_FILE_ERROR,
5804 tr("Could not merge the medium '%s' to '%s'%s"),
5805 m->strLocationFull.c_str(),
5806 pTarget->m->strLocationFull.c_str(),
5807 vdError(aVRC).c_str());
5808 }
5809
5810 VDDestroy(hdd);
5811 }
5812 catch (HRESULT aRC) { rc = aRC; }
5813
5814 HRESULT rc2;
5815
5816 if (SUCCEEDED(rc))
5817 {
5818 /* all media but the target were successfully deleted by
5819 * VDMerge; reparent the last one and uninitialize deleted media. */
5820
5821 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5822
5823 if (task.mfMergeForward)
5824 {
5825 /* first, unregister the target since it may become a base
5826 * medium which needs re-registration */
5827 rc2 = m->pVirtualBox->unregisterHardDisk(pTarget, NULL /*&fNeedsSaveSettings*/);
5828 AssertComRC(rc2);
5829
5830 /* then, reparent it and disconnect the deleted branch at
5831 * both ends (chain->parent() is source's parent) */
5832 pTarget->deparent();
5833 pTarget->m->pParent = task.mParentForTarget;
5834 if (pTarget->m->pParent)
5835 {
5836 pTarget->m->pParent->m->llChildren.push_back(pTarget);
5837 deparent();
5838 }
5839
5840 /* then, register again */
5841 rc2 = m->pVirtualBox->registerHardDisk(pTarget, NULL /*&fNeedsSaveSettings*/);
5842 AssertComRC(rc2);
5843 }
5844 else
5845 {
5846 Assert(pTarget->getChildren().size() == 1);
5847 Medium *targetChild = pTarget->getChildren().front();
5848
5849 /* disconnect the deleted branch at the elder end */
5850 targetChild->deparent();
5851
5852 /* reparent source's children and disconnect the deleted
5853 * branch at the younger end */
5854 if (task.mChildrenToReparent.size() > 0)
5855 {
5856 /* obey {parent,child} lock order */
5857 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
5858
5859 for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
5860 it != task.mChildrenToReparent.end();
5861 it++)
5862 {
5863 Medium *pMedium = *it;
5864 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
5865
5866 pMedium->deparent(); // removes pMedium from source
5867 pMedium->setParent(pTarget);
5868 }
5869 }
5870 }
5871
5872 /* unregister and uninitialize all media removed by the merge */
5873 MediumLockList::Base::iterator lockListBegin =
5874 task.mpMediumLockList->GetBegin();
5875 MediumLockList::Base::iterator lockListEnd =
5876 task.mpMediumLockList->GetEnd();
5877 for (MediumLockList::Base::iterator it = lockListBegin;
5878 it != lockListEnd;
5879 )
5880 {
5881 MediumLock &mediumLock = *it;
5882 /* Create a real copy of the medium pointer, as the medium
5883 * lock deletion below would invalidate the referenced object. */
5884 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
5885
5886 /* The target and all media not merged (readonly) are skipped */
5887 if ( pMedium == pTarget
5888 || pMedium->m->state == MediumState_LockedRead)
5889 {
5890 ++it;
5891 continue;
5892 }
5893
5894 rc2 = pMedium->m->pVirtualBox->unregisterHardDisk(pMedium,
5895 NULL /*pfNeedsSaveSettings*/);
5896 AssertComRC(rc2);
5897
5898 /* now, uninitialize the deleted medium (note that
5899 * due to the Deleting state, uninit() will not touch
5900 * the parent-child relationship so we need to
5901 * uninitialize each disk individually) */
5902
5903 /* note that the operation initiator medium (which is
5904 * normally also the source medium) is a special case
5905 * -- there is one more caller added by Task to it which
5906 * we must release. Also, if we are in sync mode, the
5907 * caller may still hold an AutoCaller instance for it
5908 * and therefore we cannot uninit() it (it's therefore
5909 * the caller's responsibility) */
5910 if (pMedium == this)
5911 {
5912 Assert(getChildren().size() == 0);
5913 Assert(m->backRefs.size() == 0);
5914 task.mMediumCaller.release();
5915 }
5916
5917 /* Delete the medium lock list entry, which also releases the
5918 * caller added by MergeChain before uninit() and updates the
5919 * iterator to point to the right place. */
5920 rc2 = task.mpMediumLockList->RemoveByIterator(it);
5921 AssertComRC(rc2);
5922
5923 if (task.isAsync() || pMedium != this)
5924 pMedium->uninit();
5925 }
5926 }
5927
5928 if (task.isAsync())
5929 {
5930 // in asynchronous mode, save settings now
5931 // for that we should hold only the VirtualBox lock
5932 AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
5933 m->pVirtualBox->saveSettings();
5934 }
5935 else
5936 // synchronous mode: report save settings result to caller
5937 if (task.m_pfNeedsSaveSettings)
5938 *task.m_pfNeedsSaveSettings = true;
5939
5940 if (FAILED(rc))
5941 {
5942 /* Here we come if either VDMerge() failed (in which case we
5943 * assume that it tried to do everything to make a further
5944 * retry possible -- e.g. not deleted intermediate media
5945 * and so on) or VirtualBox::saveSettings() failed (where we
5946 * should have the original tree but with intermediate storage
5947 * units deleted by VDMerge()). We have to only restore states
5948 * (through the MergeChain dtor) unless we are run synchronously
5949 * in which case it's the responsibility of the caller as stated
5950 * in the mergeTo() docs. The latter also implies that we
5951 * don't own the merge chain, so release it in this case. */
5952 if (task.isAsync())
5953 {
5954 Assert(task.mChildrenToReparent.size() == 0);
5955 cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
5956 }
5957 }
5958
5959 return rc;
5960}
5961
5962/**
5963 * Implementation code for the "clone" task.
5964 *
5965 * This only gets started from Medium::CloneTo() and always runs asynchronously.
5966 * As a result, we always save the VirtualBox.xml file when we're done here.
5967 *
5968 * @param task
5969 * @return
5970 */
5971HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
5972{
5973 HRESULT rc = S_OK;
5974
5975 const ComObjPtr<Medium> &pTarget = task.mTarget;
5976 const ComObjPtr<Medium> &pParent = task.mParent;
5977
5978 bool fCreatingTarget = false;
5979
5980 uint64_t size = 0, logicalSize = 0;
5981 MediumVariant_T variant = MediumVariant_Standard;
5982 bool fGenerateUuid = false;
5983
5984 try
5985 {
5986 /* Lock all in {parent,child} order. The lock is also used as a
5987 * signal from the task initiator (which releases it only after
5988 * RTThreadCreate()) that we can start the job. */
5989 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
5990
5991 fCreatingTarget = pTarget->m->state == MediumState_Creating;
5992
5993 /* The object may request a specific UUID (through a special form of
5994 * the setLocation() argument). Otherwise we have to generate it */
5995 Guid targetId = pTarget->m->id;
5996 fGenerateUuid = targetId.isEmpty();
5997 if (fGenerateUuid)
5998 {
5999 targetId.create();
6000 /* VirtualBox::registerHardDisk() will need UUID */
6001 unconst(pTarget->m->id) = targetId;
6002 }
6003
6004 PVBOXHDD hdd;
6005 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6006 ComAssertRCThrow(vrc, E_FAIL);
6007
6008 try
6009 {
6010 /* Open all media in the source chain. */
6011 MediumLockList::Base::const_iterator sourceListBegin =
6012 task.mpSourceMediumLockList->GetBegin();
6013 MediumLockList::Base::const_iterator sourceListEnd =
6014 task.mpSourceMediumLockList->GetEnd();
6015 for (MediumLockList::Base::const_iterator it = sourceListBegin;
6016 it != sourceListEnd;
6017 ++it)
6018 {
6019 const MediumLock &mediumLock = *it;
6020 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6021 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6022
6023 /* sanity check */
6024 Assert(pMedium->m->state == MediumState_LockedRead);
6025
6026 /** Open all media in read-only mode. */
6027 vrc = VDOpen(hdd,
6028 pMedium->m->strFormat.c_str(),
6029 pMedium->m->strLocationFull.c_str(),
6030 VD_OPEN_FLAGS_READONLY,
6031 pMedium->m->vdDiskIfaces);
6032 if (RT_FAILURE(vrc))
6033 throw setError(VBOX_E_FILE_ERROR,
6034 tr("Could not open the medium storage unit '%s'%s"),
6035 pMedium->m->strLocationFull.c_str(),
6036 vdError(vrc).c_str());
6037 }
6038
6039 Utf8Str targetFormat(pTarget->m->strFormat);
6040 Utf8Str targetLocation(pTarget->m->strLocationFull);
6041
6042 Assert( pTarget->m->state == MediumState_Creating
6043 || pTarget->m->state == MediumState_LockedWrite);
6044 Assert(m->state == MediumState_LockedRead);
6045 Assert(pParent.isNull() || pParent->m->state == MediumState_LockedRead);
6046
6047 /* unlock before the potentially lengthy operation */
6048 thisLock.release();
6049
6050 /* ensure the target directory exists */
6051 rc = VirtualBox::ensureFilePathExists(targetLocation);
6052 if (FAILED(rc))
6053 throw rc;
6054
6055 PVBOXHDD targetHdd;
6056 vrc = VDCreate(m->vdDiskIfaces, &targetHdd);
6057 ComAssertRCThrow(vrc, E_FAIL);
6058
6059 try
6060 {
6061 /* Open all media in the target chain. */
6062 MediumLockList::Base::const_iterator targetListBegin =
6063 task.mpTargetMediumLockList->GetBegin();
6064 MediumLockList::Base::const_iterator targetListEnd =
6065 task.mpTargetMediumLockList->GetEnd();
6066 for (MediumLockList::Base::const_iterator it = targetListBegin;
6067 it != targetListEnd;
6068 ++it)
6069 {
6070 const MediumLock &mediumLock = *it;
6071 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6072
6073 /* If the target medium is not created yet there's no
6074 * reason to open it. */
6075 if (pMedium == pTarget && fCreatingTarget)
6076 continue;
6077
6078 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6079
6080 /* sanity check */
6081 Assert( pMedium->m->state == MediumState_LockedRead
6082 || pMedium->m->state == MediumState_LockedWrite);
6083
6084 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
6085 if (pMedium->m->state != MediumState_LockedWrite)
6086 uOpenFlags = VD_OPEN_FLAGS_READONLY;
6087 if (pMedium->m->type == MediumType_Shareable)
6088 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6089
6090 /* Open all media in appropriate mode. */
6091 vrc = VDOpen(targetHdd,
6092 pMedium->m->strFormat.c_str(),
6093 pMedium->m->strLocationFull.c_str(),
6094 uOpenFlags,
6095 pMedium->m->vdDiskIfaces);
6096 if (RT_FAILURE(vrc))
6097 throw setError(VBOX_E_FILE_ERROR,
6098 tr("Could not open the medium storage unit '%s'%s"),
6099 pMedium->m->strLocationFull.c_str(),
6100 vdError(vrc).c_str());
6101 }
6102
6103 /** @todo r=klaus target isn't locked, race getting the state */
6104 vrc = VDCopy(hdd,
6105 VD_LAST_IMAGE,
6106 targetHdd,
6107 targetFormat.c_str(),
6108 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
6109 false,
6110 0,
6111 task.mVariant,
6112 targetId.raw(),
6113 NULL,
6114 pTarget->m->vdDiskIfaces,
6115 task.mVDOperationIfaces);
6116 if (RT_FAILURE(vrc))
6117 throw setError(VBOX_E_FILE_ERROR,
6118 tr("Could not create the clone medium '%s'%s"),
6119 targetLocation.c_str(), vdError(vrc).c_str());
6120
6121 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
6122 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE) / _1M;
6123 unsigned uImageFlags;
6124 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
6125 if (RT_SUCCESS(vrc))
6126 variant = (MediumVariant_T)uImageFlags;
6127 }
6128 catch (HRESULT aRC) { rc = aRC; }
6129
6130 VDDestroy(targetHdd);
6131 }
6132 catch (HRESULT aRC) { rc = aRC; }
6133
6134 VDDestroy(hdd);
6135 }
6136 catch (HRESULT aRC) { rc = aRC; }
6137
6138 /* Only do the parent changes for newly created media. */
6139 if (SUCCEEDED(rc) && fCreatingTarget)
6140 {
6141 /* we set mParent & children() */
6142 AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6143
6144 Assert(pTarget->m->pParent.isNull());
6145
6146 if (pParent)
6147 {
6148 /* associate the clone with the parent and deassociate
6149 * from VirtualBox */
6150 pTarget->m->pParent = pParent;
6151 pParent->m->llChildren.push_back(pTarget);
6152
6153 /* register with mVirtualBox as the last step and move to
6154 * Created state only on success (leaving an orphan file is
6155 * better than breaking media registry consistency) */
6156 rc = pParent->m->pVirtualBox->registerHardDisk(pTarget, NULL /* pfNeedsSaveSettings */);
6157
6158 if (FAILED(rc))
6159 /* break parent association on failure to register */
6160 pTarget->deparent(); // removes target from parent
6161 }
6162 else
6163 {
6164 /* just register */
6165 rc = m->pVirtualBox->registerHardDisk(pTarget, NULL /* pfNeedsSaveSettings */);
6166 }
6167 }
6168
6169 if (fCreatingTarget)
6170 {
6171 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
6172
6173 if (SUCCEEDED(rc))
6174 {
6175 pTarget->m->state = MediumState_Created;
6176
6177 pTarget->m->size = size;
6178 pTarget->m->logicalSize = logicalSize;
6179 pTarget->m->variant = variant;
6180 }
6181 else
6182 {
6183 /* back to NotCreated on failure */
6184 pTarget->m->state = MediumState_NotCreated;
6185
6186 /* reset UUID to prevent it from being reused next time */
6187 if (fGenerateUuid)
6188 unconst(pTarget->m->id).clear();
6189 }
6190 }
6191
6192 // now, at the end of this task (always asynchronous), save the settings
6193 {
6194 AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
6195 m->pVirtualBox->saveSettings();
6196 }
6197
6198 /* Everything is explicitly unlocked when the task exits,
6199 * as the task destruction also destroys the source chain. */
6200
6201 /* Make sure the source chain is released early. It could happen
6202 * that we get a deadlock in Appliance::Import when Medium::Close
6203 * is called & the source chain is released at the same time. */
6204 task.mpSourceMediumLockList->Clear();
6205
6206 return rc;
6207}
6208
6209/**
6210 * Implementation code for the "delete" task.
6211 *
6212 * This task always gets started from Medium::deleteStorage() and can run
6213 * synchronously or asynchrously depending on the "wait" parameter passed to
6214 * that function.
6215 *
6216 * @param task
6217 * @return
6218 */
6219HRESULT Medium::taskDeleteHandler(Medium::DeleteTask &task)
6220{
6221 NOREF(task);
6222 HRESULT rc = S_OK;
6223
6224 try
6225 {
6226 /* The lock is also used as a signal from the task initiator (which
6227 * releases it only after RTThreadCreate()) that we can start the job */
6228 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6229
6230 PVBOXHDD hdd;
6231 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6232 ComAssertRCThrow(vrc, E_FAIL);
6233
6234 Utf8Str format(m->strFormat);
6235 Utf8Str location(m->strLocationFull);
6236
6237 /* unlock before the potentially lengthy operation */
6238 Assert(m->state == MediumState_Deleting);
6239 thisLock.release();
6240
6241 try
6242 {
6243 vrc = VDOpen(hdd,
6244 format.c_str(),
6245 location.c_str(),
6246 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
6247 m->vdDiskIfaces);
6248 if (RT_SUCCESS(vrc))
6249 vrc = VDClose(hdd, true /* fDelete */);
6250
6251 if (RT_FAILURE(vrc))
6252 throw setError(VBOX_E_FILE_ERROR,
6253 tr("Could not delete the medium storage unit '%s'%s"),
6254 location.c_str(), vdError(vrc).c_str());
6255
6256 }
6257 catch (HRESULT aRC) { rc = aRC; }
6258
6259 VDDestroy(hdd);
6260 }
6261 catch (HRESULT aRC) { rc = aRC; }
6262
6263 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6264
6265 /* go to the NotCreated state even on failure since the storage
6266 * may have been already partially deleted and cannot be used any
6267 * more. One will be able to manually re-open the storage if really
6268 * needed to re-register it. */
6269 m->state = MediumState_NotCreated;
6270
6271 /* Reset UUID to prevent Create* from reusing it again */
6272 unconst(m->id).clear();
6273
6274 return rc;
6275}
6276
6277/**
6278 * Implementation code for the "reset" task.
6279 *
6280 * This always gets started asynchronously from Medium::Reset().
6281 *
6282 * @param task
6283 * @return
6284 */
6285HRESULT Medium::taskResetHandler(Medium::ResetTask &task)
6286{
6287 HRESULT rc = S_OK;
6288
6289 uint64_t size = 0, logicalSize = 0;
6290 MediumVariant_T variant = MediumVariant_Standard;
6291
6292 try
6293 {
6294 /* The lock is also used as a signal from the task initiator (which
6295 * releases it only after RTThreadCreate()) that we can start the job */
6296 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6297
6298 /// @todo Below we use a pair of delete/create operations to reset
6299 /// the diff contents but the most efficient way will of course be
6300 /// to add a VDResetDiff() API call
6301
6302 PVBOXHDD hdd;
6303 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6304 ComAssertRCThrow(vrc, E_FAIL);
6305
6306 Guid id = m->id;
6307 Utf8Str format(m->strFormat);
6308 Utf8Str location(m->strLocationFull);
6309
6310 Medium *pParent = m->pParent;
6311 Guid parentId = pParent->m->id;
6312 Utf8Str parentFormat(pParent->m->strFormat);
6313 Utf8Str parentLocation(pParent->m->strLocationFull);
6314
6315 Assert(m->state == MediumState_LockedWrite);
6316
6317 /* unlock before the potentially lengthy operation */
6318 thisLock.release();
6319
6320 try
6321 {
6322 /* Open all media in the target chain but the last. */
6323 MediumLockList::Base::const_iterator targetListBegin =
6324 task.mpMediumLockList->GetBegin();
6325 MediumLockList::Base::const_iterator targetListEnd =
6326 task.mpMediumLockList->GetEnd();
6327 for (MediumLockList::Base::const_iterator it = targetListBegin;
6328 it != targetListEnd;
6329 ++it)
6330 {
6331 const MediumLock &mediumLock = *it;
6332 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6333
6334 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6335
6336 /* sanity check, "this" is checked above */
6337 Assert( pMedium == this
6338 || pMedium->m->state == MediumState_LockedRead);
6339
6340 /* Open all media in appropriate mode. */
6341 vrc = VDOpen(hdd,
6342 pMedium->m->strFormat.c_str(),
6343 pMedium->m->strLocationFull.c_str(),
6344 VD_OPEN_FLAGS_READONLY,
6345 pMedium->m->vdDiskIfaces);
6346 if (RT_FAILURE(vrc))
6347 throw setError(VBOX_E_FILE_ERROR,
6348 tr("Could not open the medium storage unit '%s'%s"),
6349 pMedium->m->strLocationFull.c_str(),
6350 vdError(vrc).c_str());
6351
6352 /* Done when we hit the media which should be reset */
6353 if (pMedium == this)
6354 break;
6355 }
6356
6357 /* first, delete the storage unit */
6358 vrc = VDClose(hdd, true /* fDelete */);
6359 if (RT_FAILURE(vrc))
6360 throw setError(VBOX_E_FILE_ERROR,
6361 tr("Could not delete the medium storage unit '%s'%s"),
6362 location.c_str(), vdError(vrc).c_str());
6363
6364 /* next, create it again */
6365 vrc = VDOpen(hdd,
6366 parentFormat.c_str(),
6367 parentLocation.c_str(),
6368 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
6369 m->vdDiskIfaces);
6370 if (RT_FAILURE(vrc))
6371 throw setError(VBOX_E_FILE_ERROR,
6372 tr("Could not open the medium storage unit '%s'%s"),
6373 parentLocation.c_str(), vdError(vrc).c_str());
6374
6375 vrc = VDCreateDiff(hdd,
6376 format.c_str(),
6377 location.c_str(),
6378 /// @todo use the same medium variant as before
6379 VD_IMAGE_FLAGS_NONE,
6380 NULL,
6381 id.raw(),
6382 parentId.raw(),
6383 VD_OPEN_FLAGS_NORMAL,
6384 m->vdDiskIfaces,
6385 task.mVDOperationIfaces);
6386 if (RT_FAILURE(vrc))
6387 throw setError(VBOX_E_FILE_ERROR,
6388 tr("Could not create the differencing medium storage unit '%s'%s"),
6389 location.c_str(), vdError(vrc).c_str());
6390
6391 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
6392 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE) / _1M;
6393 unsigned uImageFlags;
6394 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6395 if (RT_SUCCESS(vrc))
6396 variant = (MediumVariant_T)uImageFlags;
6397 }
6398 catch (HRESULT aRC) { rc = aRC; }
6399
6400 VDDestroy(hdd);
6401 }
6402 catch (HRESULT aRC) { rc = aRC; }
6403
6404 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6405
6406 m->size = size;
6407 m->logicalSize = logicalSize;
6408 m->variant = variant;
6409
6410 if (task.isAsync())
6411 {
6412 /* unlock ourselves when done */
6413 HRESULT rc2 = UnlockWrite(NULL);
6414 AssertComRC(rc2);
6415 }
6416
6417 /* Note that in sync mode, it's the caller's responsibility to
6418 * unlock the medium. */
6419
6420 return rc;
6421}
6422
6423/**
6424 * Implementation code for the "compact" task.
6425 *
6426 * @param task
6427 * @return
6428 */
6429HRESULT Medium::taskCompactHandler(Medium::CompactTask &task)
6430{
6431 HRESULT rc = S_OK;
6432
6433 /* Lock all in {parent,child} order. The lock is also used as a
6434 * signal from the task initiator (which releases it only after
6435 * RTThreadCreate()) that we can start the job. */
6436 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6437
6438 try
6439 {
6440 PVBOXHDD hdd;
6441 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6442 ComAssertRCThrow(vrc, E_FAIL);
6443
6444 try
6445 {
6446 /* Open all media in the chain. */
6447 MediumLockList::Base::const_iterator mediumListBegin =
6448 task.mpMediumLockList->GetBegin();
6449 MediumLockList::Base::const_iterator mediumListEnd =
6450 task.mpMediumLockList->GetEnd();
6451 MediumLockList::Base::const_iterator mediumListLast =
6452 mediumListEnd;
6453 mediumListLast--;
6454 for (MediumLockList::Base::const_iterator it = mediumListBegin;
6455 it != mediumListEnd;
6456 ++it)
6457 {
6458 const MediumLock &mediumLock = *it;
6459 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6460 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6461
6462 /* sanity check */
6463 if (it == mediumListLast)
6464 Assert(pMedium->m->state == MediumState_LockedWrite);
6465 else
6466 Assert(pMedium->m->state == MediumState_LockedRead);
6467
6468 /* Open all media but last in read-only mode. Do not handle
6469 * shareable media, as compaction and sharing are mutually
6470 * exclusive. */
6471 vrc = VDOpen(hdd,
6472 pMedium->m->strFormat.c_str(),
6473 pMedium->m->strLocationFull.c_str(),
6474 (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
6475 pMedium->m->vdDiskIfaces);
6476 if (RT_FAILURE(vrc))
6477 throw setError(VBOX_E_FILE_ERROR,
6478 tr("Could not open the medium storage unit '%s'%s"),
6479 pMedium->m->strLocationFull.c_str(),
6480 vdError(vrc).c_str());
6481 }
6482
6483 Assert(m->state == MediumState_LockedWrite);
6484
6485 Utf8Str location(m->strLocationFull);
6486
6487 /* unlock before the potentially lengthy operation */
6488 thisLock.release();
6489
6490 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
6491 if (RT_FAILURE(vrc))
6492 {
6493 if (vrc == VERR_NOT_SUPPORTED)
6494 throw setError(VBOX_E_NOT_SUPPORTED,
6495 tr("Compacting is not yet supported for medium '%s'"),
6496 location.c_str());
6497 else if (vrc == VERR_NOT_IMPLEMENTED)
6498 throw setError(E_NOTIMPL,
6499 tr("Compacting is not implemented, medium '%s'"),
6500 location.c_str());
6501 else
6502 throw setError(VBOX_E_FILE_ERROR,
6503 tr("Could not compact medium '%s'%s"),
6504 location.c_str(),
6505 vdError(vrc).c_str());
6506 }
6507 }
6508 catch (HRESULT aRC) { rc = aRC; }
6509
6510 VDDestroy(hdd);
6511 }
6512 catch (HRESULT aRC) { rc = aRC; }
6513
6514 /* Everything is explicitly unlocked when the task exits,
6515 * as the task destruction also destroys the media chain. */
6516
6517 return rc;
6518}
6519
6520/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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