VirtualBox

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

Last change on this file since 31480 was 31480, checked in by vboxsync, 15 years ago

Main: use Utf8Str for media properties (also fixes incorrect length assumption in conversation with media backend)

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

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