VirtualBox

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

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

Main: merge IVirtualBox::FindHardDisk, GetHardDisk, FindDVDImage, GetDVDImage, FindFloppyImage and GetFloppyImage into one IVirtualBox::findMedium method

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 211.6 KB
Line 
1/* $Id: MediumImpl.cpp 31562 2010-08-11 10:02:40Z 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::StringsMap 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::StringsMap::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.c_str(), m->strFormat.c_str(), 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.c_str() + (lenLocation - 12), 12);
1088 else
1089 memcpy(&uuid.au8[4 + 12 - lenLocation], aLocation.c_str(), 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.c_str());
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.c_str(), 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.c_str(), 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.c_str());
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.c_str());
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.c_str());
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.c_str());
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::StringsMap::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::StringsMap::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::StringsMap::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::StringsMap::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.c_str());
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.c_str());
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.c_str())
2123 : BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.c_str()),
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.c_str());
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.c_str());
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().c_str());
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().c_str());
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.c_str()),
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().c_str());
2413 }
2414
2415 pProgress.createObject();
2416 rc = pProgress->init(m->pVirtualBox,
2417 static_cast <IMedium *>(this),
2418 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()),
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.c_str());
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().c_str());
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.c_str()),
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.c_str(),
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.c_str(),
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.c_str()));
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.c_str()));
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.c_str()));
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::StringsMap::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 Utf8Str &strLocation, 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;
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(strLocation.c_str()))
3083 {
3084 m->pVirtualBox->getDefaultHardDiskFolder(location);
3085 location.append(RTPATH_DELIMITER);
3086 location.append(strLocation);
3087 }
3088 else
3089 location = strLocation;
3090
3091 int vrc = m->pVirtualBox->calculateFullPath(location, location);
3092 if (RT_FAILURE(vrc))
3093 return setError(VBOX_E_FILE_ERROR,
3094 tr("Invalid medium storage file location '%s' (%Rrc)"),
3095 location.c_str(),
3096 vrc);
3097
3098 aResult = RTPathCompare(locationFull.c_str(), location.c_str());
3099 }
3100 else
3101 aResult = locationFull.compare(strLocation);
3102
3103 return S_OK;
3104}
3105
3106/**
3107 * Constructs a medium lock list for this medium. The lock is not taken.
3108 *
3109 * @note Locks the medium tree for reading.
3110 *
3111 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
3112 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
3113 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
3114 * @param fMediumLockWrite Whether to associate a write lock with this medium.
3115 * @param pToBeParent Medium which will become the parent of this medium.
3116 * @param mediumLockList Where to store the resulting list.
3117 */
3118HRESULT Medium::createMediumLockList(bool fFailIfInaccessible,
3119 bool fMediumLockWrite,
3120 Medium *pToBeParent,
3121 MediumLockList &mediumLockList)
3122{
3123 AutoCaller autoCaller(this);
3124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3125
3126 HRESULT rc = S_OK;
3127
3128 /* we access parent medium objects */
3129 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3130
3131 /* paranoid sanity checking if the medium has a to-be parent medium */
3132 if (pToBeParent)
3133 {
3134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3135 ComAssertRet(getParent().isNull(), E_FAIL);
3136 ComAssertRet(getChildren().size() == 0, E_FAIL);
3137 }
3138
3139 ErrorInfoKeeper eik;
3140 MultiResult mrc(S_OK);
3141
3142 ComObjPtr<Medium> pMedium = this;
3143 while (!pMedium.isNull())
3144 {
3145 // need write lock for RefreshState if medium is inaccessible
3146 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
3147
3148 /* Accessibility check must be first, otherwise locking interferes
3149 * with getting the medium state. Lock lists are not created for
3150 * fun, and thus getting the medium status is no luxury. */
3151 MediumState_T mediumState = pMedium->getState();
3152 if (mediumState == MediumState_Inaccessible)
3153 {
3154 rc = pMedium->RefreshState(&mediumState);
3155 if (FAILED(rc)) return rc;
3156
3157 if (mediumState == MediumState_Inaccessible)
3158 {
3159 // ignore inaccessible ISO media and silently return S_OK,
3160 // otherwise VM startup (esp. restore) may fail without good reason
3161 if (!fFailIfInaccessible)
3162 return S_OK;
3163
3164 // otherwise report an error
3165 Bstr error;
3166 rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
3167 if (FAILED(rc)) return rc;
3168
3169 /* collect multiple errors */
3170 eik.restore();
3171 Assert(!error.isEmpty());
3172 mrc = setError(E_FAIL,
3173 "%ls",
3174 error.raw());
3175 // error message will be something like
3176 // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
3177 eik.fetch();
3178 }
3179 }
3180
3181 if (pMedium == this)
3182 mediumLockList.Prepend(pMedium, fMediumLockWrite);
3183 else
3184 mediumLockList.Prepend(pMedium, false);
3185
3186 pMedium = pMedium->getParent();
3187 if (pMedium.isNull() && pToBeParent)
3188 {
3189 pMedium = pToBeParent;
3190 pToBeParent = NULL;
3191 }
3192 }
3193
3194 return mrc;
3195}
3196
3197/**
3198 * Returns a preferred format for differencing media.
3199 */
3200Utf8Str Medium::getPreferredDiffFormat()
3201{
3202 AutoCaller autoCaller(this);
3203 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
3204
3205 /* check that our own format supports diffs */
3206 if (!(m->formatObj->getCapabilities() & MediumFormatCapabilities_Differencing))
3207 {
3208 /* use the default format if not */
3209 Utf8Str tmp;
3210 m->pVirtualBox->getDefaultHardDiskFormat(tmp);
3211 return tmp;
3212 }
3213
3214 /* m->strFormat is const, no need to lock */
3215 return m->strFormat;
3216}
3217
3218/**
3219 * Returns the medium device type. Must have caller + locking!
3220 * @return
3221 */
3222DeviceType_T Medium::getDeviceType() const
3223{
3224 return m->devType;
3225}
3226
3227/**
3228 * Returns the medium type. Must have caller + locking!
3229 * @return
3230 */
3231MediumType_T Medium::getType() const
3232{
3233 return m->type;
3234}
3235
3236/**
3237 * Returns a short version of the location attribute.
3238 *
3239 * @note Must be called from under this object's read or write lock.
3240 */
3241Utf8Str Medium::getName()
3242{
3243 Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
3244 return name;
3245}
3246
3247/**
3248 * Sets the value of m->strLocation and calculates the value of m->strLocationFull.
3249 *
3250 * Treats non-FS-path locations specially, and prepends the default medium
3251 * folder if the given location string does not contain any path information
3252 * at all.
3253 *
3254 * Also, if the specified location is a file path that ends with '/' then the
3255 * file name part will be generated by this method automatically in the format
3256 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
3257 * and assign to this medium, and <ext> is the default extension for this
3258 * medium's storage format. Note that this procedure requires the media state to
3259 * be NotCreated and will return a failure otherwise.
3260 *
3261 * @param aLocation Location of the storage unit. If the location is a FS-path,
3262 * then it can be relative to the VirtualBox home directory.
3263 * @param aFormat Optional fallback format if it is an import and the format
3264 * cannot be determined.
3265 *
3266 * @note Must be called from under this object's write lock.
3267 */
3268HRESULT Medium::setLocation(const Utf8Str &aLocation,
3269 const Utf8Str &aFormat /* = Utf8Str::Empty */)
3270{
3271 AssertReturn(!aLocation.isEmpty(), E_FAIL);
3272
3273 AutoCaller autoCaller(this);
3274 AssertComRCReturnRC(autoCaller.rc());
3275
3276 /* formatObj may be null only when initializing from an existing path and
3277 * no format is known yet */
3278 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
3279 || ( autoCaller.state() == InInit
3280 && m->state != MediumState_NotCreated
3281 && m->id.isEmpty()
3282 && m->strFormat.isEmpty()
3283 && m->formatObj.isNull()),
3284 E_FAIL);
3285
3286 /* are we dealing with a new medium constructed using the existing
3287 * location? */
3288 bool isImport = m->strFormat.isEmpty();
3289
3290 if ( isImport
3291 || ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
3292 && !m->hostDrive))
3293 {
3294 Guid id;
3295
3296 Utf8Str location(aLocation);
3297
3298 if (m->state == MediumState_NotCreated)
3299 {
3300 /* must be a file (formatObj must be already known) */
3301 Assert(m->formatObj->getCapabilities() & MediumFormatCapabilities_File);
3302
3303 if (RTPathFilename(location.c_str()) == NULL)
3304 {
3305 /* no file name is given (either an empty string or ends with a
3306 * slash), generate a new UUID + file name if the state allows
3307 * this */
3308
3309 ComAssertMsgRet(!m->formatObj->getFileExtensions().empty(),
3310 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
3311 E_FAIL);
3312
3313 Utf8Str strExt = m->formatObj->getFileExtensions().front();
3314 ComAssertMsgRet(!strExt.isEmpty(),
3315 ("Default extension must not be empty\n"),
3316 E_FAIL);
3317
3318 id.create();
3319
3320 location = Utf8StrFmt("%s{%RTuuid}.%s",
3321 location.c_str(), id.raw(), strExt.c_str());
3322 }
3323 }
3324
3325 /* append the default folder if no path is given */
3326 if (!RTPathHavePath(location.c_str()))
3327 {
3328 Utf8Str tmp;
3329 m->pVirtualBox->getDefaultHardDiskFolder(tmp);
3330 tmp.append(RTPATH_DELIMITER);
3331 tmp.append(location);
3332 location = tmp;
3333 }
3334
3335 /* get the full file name */
3336 Utf8Str locationFull;
3337 int vrc = m->pVirtualBox->calculateFullPath(location, locationFull);
3338 if (RT_FAILURE(vrc))
3339 return setError(VBOX_E_FILE_ERROR,
3340 tr("Invalid medium storage file location '%s' (%Rrc)"),
3341 location.c_str(), vrc);
3342
3343 /* detect the backend from the storage unit if importing */
3344 if (isImport)
3345 {
3346 char *backendName = NULL;
3347
3348 /* is it a file? */
3349 {
3350 RTFILE file;
3351 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
3352 if (RT_SUCCESS(vrc))
3353 RTFileClose(file);
3354 }
3355 if (RT_SUCCESS(vrc))
3356 {
3357 vrc = VDGetFormat(NULL, locationFull.c_str(), &backendName);
3358 }
3359 else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
3360 {
3361 /* assume it's not a file, restore the original location */
3362 location = locationFull = aLocation;
3363 vrc = VDGetFormat(NULL, locationFull.c_str(), &backendName);
3364 }
3365
3366 if (RT_FAILURE(vrc))
3367 {
3368 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
3369 return setError(VBOX_E_FILE_ERROR,
3370 tr("Could not find file for the medium '%s' (%Rrc)"),
3371 locationFull.c_str(), vrc);
3372 else if (aFormat.isEmpty())
3373 return setError(VBOX_E_IPRT_ERROR,
3374 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
3375 locationFull.c_str(), vrc);
3376 else
3377 {
3378 HRESULT rc = setFormat(aFormat);
3379 /* setFormat() must not fail since we've just used the backend so
3380 * the format object must be there */
3381 AssertComRCReturnRC(rc);
3382 }
3383 }
3384 else
3385 {
3386 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
3387
3388 HRESULT rc = setFormat(backendName);
3389 RTStrFree(backendName);
3390
3391 /* setFormat() must not fail since we've just used the backend so
3392 * the format object must be there */
3393 AssertComRCReturnRC(rc);
3394 }
3395 }
3396
3397 /* is it still a file? */
3398 if (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
3399 {
3400 m->strLocation = location;
3401 m->strLocationFull = locationFull;
3402
3403 if (m->state == MediumState_NotCreated)
3404 {
3405 /* assign a new UUID (this UUID will be used when calling
3406 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
3407 * also do that if we didn't generate it to make sure it is
3408 * either generated by us or reset to null */
3409 unconst(m->id) = id;
3410 }
3411 }
3412 else
3413 {
3414 m->strLocation = locationFull;
3415 m->strLocationFull = locationFull;
3416 }
3417 }
3418 else
3419 {
3420 m->strLocation = aLocation;
3421 m->strLocationFull = aLocation;
3422 }
3423
3424 return S_OK;
3425}
3426
3427/**
3428 * Queries information from the medium.
3429 *
3430 * As a result of this call, the accessibility state and data members such as
3431 * size and description will be updated with the current information.
3432 *
3433 * @note This method may block during a system I/O call that checks storage
3434 * accessibility.
3435 *
3436 * @note Locks medium tree for reading and writing (for new diff media checked
3437 * for the first time). Locks mParent for reading. Locks this object for
3438 * writing.
3439 */
3440HRESULT Medium::queryInfo()
3441{
3442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3443
3444 if ( m->state != MediumState_Created
3445 && m->state != MediumState_Inaccessible
3446 && m->state != MediumState_LockedRead)
3447 return E_FAIL;
3448
3449 HRESULT rc = S_OK;
3450
3451 int vrc = VINF_SUCCESS;
3452
3453 /* check if a blocking queryInfo() call is in progress on some other thread,
3454 * and wait for it to finish if so instead of querying data ourselves */
3455 if (m->queryInfoRunning)
3456 {
3457 Assert( m->state == MediumState_LockedRead
3458 || m->state == MediumState_LockedWrite);
3459
3460 alock.leave();
3461 vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
3462 alock.enter();
3463
3464 AssertRC(vrc);
3465
3466 return S_OK;
3467 }
3468
3469 bool success = false;
3470 Utf8Str lastAccessError;
3471
3472 /* are we dealing with a new medium constructed using the existing
3473 * location? */
3474 bool isImport = m->id.isEmpty();
3475 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
3476
3477 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
3478 * media because that would prevent necessary modifications
3479 * when opening media of some third-party formats for the first
3480 * time in VirtualBox (such as VMDK for which VDOpen() needs to
3481 * generate an UUID if it is missing) */
3482 if ( (m->hddOpenMode == OpenReadOnly)
3483 || !isImport
3484 )
3485 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
3486
3487 /* Open shareable medium with the appropriate flags */
3488 if (m->type == MediumType_Shareable)
3489 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
3490
3491 /* Lock the medium, which makes the behavior much more consistent */
3492 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY || VD_OPEN_FLAGS_SHAREABLE))
3493 rc = LockRead(NULL);
3494 else
3495 rc = LockWrite(NULL);
3496 if (FAILED(rc)) return rc;
3497
3498 /* Copies of the input state fields which are not read-only,
3499 * as we're dropping the lock. CAUTION: be extremely careful what
3500 * you do with the contents of this medium object, as you will
3501 * create races if there are concurrent changes. */
3502 Utf8Str format(m->strFormat);
3503 Utf8Str location(m->strLocationFull);
3504 ComObjPtr<MediumFormat> formatObj = m->formatObj;
3505
3506 /* "Output" values which can't be set because the lock isn't held
3507 * at the time the values are determined. */
3508 Guid mediumId = m->id;
3509 uint64_t mediumSize = 0;
3510 uint64_t mediumLogicalSize = 0;
3511
3512 /* leave the lock before a lengthy operation */
3513 vrc = RTSemEventMultiReset(m->queryInfoSem);
3514 AssertRCReturn(vrc, E_FAIL);
3515 m->queryInfoRunning = true;
3516 alock.leave();
3517
3518 try
3519 {
3520 /* skip accessibility checks for host drives */
3521 if (m->hostDrive)
3522 {
3523 success = true;
3524 throw S_OK;
3525 }
3526
3527 PVBOXHDD hdd;
3528 vrc = VDCreate(m->vdDiskIfaces, &hdd);
3529 ComAssertRCThrow(vrc, E_FAIL);
3530
3531 try
3532 {
3533 /** @todo This kind of opening of media is assuming that diff
3534 * media can be opened as base media. Should be documented that
3535 * it must work for all medium format backends. */
3536 vrc = VDOpen(hdd,
3537 format.c_str(),
3538 location.c_str(),
3539 uOpenFlags,
3540 m->vdDiskIfaces);
3541 if (RT_FAILURE(vrc))
3542 {
3543 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
3544 location.c_str(), vdError(vrc).c_str());
3545 throw S_OK;
3546 }
3547
3548 if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
3549 {
3550 /* Modify the UUIDs if necessary. The associated fields are
3551 * not modified by other code, so no need to copy. */
3552 if (m->setImageId)
3553 {
3554 vrc = VDSetUuid(hdd, 0, m->imageId);
3555 ComAssertRCThrow(vrc, E_FAIL);
3556 }
3557 if (m->setParentId)
3558 {
3559 vrc = VDSetParentUuid(hdd, 0, m->parentId);
3560 ComAssertRCThrow(vrc, E_FAIL);
3561 }
3562 /* zap the information, these are no long-term members */
3563 m->setImageId = false;
3564 unconst(m->imageId).clear();
3565 m->setParentId = false;
3566 unconst(m->parentId).clear();
3567
3568 /* check the UUID */
3569 RTUUID uuid;
3570 vrc = VDGetUuid(hdd, 0, &uuid);
3571 ComAssertRCThrow(vrc, E_FAIL);
3572
3573 if (isImport)
3574 {
3575 mediumId = uuid;
3576
3577 if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
3578 // only when importing a VDMK that has no UUID, create one in memory
3579 mediumId.create();
3580 }
3581 else
3582 {
3583 Assert(!mediumId.isEmpty());
3584
3585 if (mediumId != uuid)
3586 {
3587 lastAccessError = Utf8StrFmt(
3588 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
3589 &uuid,
3590 location.c_str(),
3591 mediumId.raw(),
3592 m->pVirtualBox->settingsFilePath().c_str());
3593 throw S_OK;
3594 }
3595 }
3596 }
3597 else
3598 {
3599 /* the backend does not support storing UUIDs within the
3600 * underlying storage so use what we store in XML */
3601
3602 /* generate an UUID for an imported UUID-less medium */
3603 if (isImport)
3604 {
3605 if (m->setImageId)
3606 mediumId = m->imageId;
3607 else
3608 mediumId.create();
3609 }
3610 }
3611
3612 /* get the medium variant */
3613 unsigned uImageFlags;
3614 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
3615 ComAssertRCThrow(vrc, E_FAIL);
3616 m->variant = (MediumVariant_T)uImageFlags;
3617
3618 /* check/get the parent uuid and update corresponding state */
3619 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
3620 {
3621 RTUUID parentId;
3622 vrc = VDGetParentUuid(hdd, 0, &parentId);
3623 ComAssertRCThrow(vrc, E_FAIL);
3624
3625 if (isImport)
3626 {
3627 /* the parent must be known to us. Note that we freely
3628 * call locking methods of mVirtualBox and parent, as all
3629 * relevant locks must be already held. There may be no
3630 * concurrent access to the just opened medium on other
3631 * threads yet (and init() will fail if this method reports
3632 * MediumState_Inaccessible) */
3633
3634 Guid id = parentId;
3635 ComObjPtr<Medium> pParent;
3636 rc = m->pVirtualBox->findHardDisk(&id, Utf8Str::Empty, false /* aSetError */, &pParent);
3637 if (FAILED(rc))
3638 {
3639 lastAccessError = Utf8StrFmt(
3640 tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
3641 &parentId, location.c_str(),
3642 m->pVirtualBox->settingsFilePath().c_str());
3643 throw S_OK;
3644 }
3645
3646 /* we set mParent & children() */
3647 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3648
3649 Assert(m->pParent.isNull());
3650 m->pParent = pParent;
3651 m->pParent->m->llChildren.push_back(this);
3652 }
3653 else
3654 {
3655 /* we access mParent */
3656 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3657
3658 /* check that parent UUIDs match. Note that there's no need
3659 * for the parent's AutoCaller (our lifetime is bound to
3660 * it) */
3661
3662 if (m->pParent.isNull())
3663 {
3664 lastAccessError = Utf8StrFmt(
3665 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
3666 location.c_str(),
3667 m->pVirtualBox->settingsFilePath().c_str());
3668 throw S_OK;
3669 }
3670
3671 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
3672 if ( m->pParent->getState() != MediumState_Inaccessible
3673 && m->pParent->getId() != parentId)
3674 {
3675 lastAccessError = Utf8StrFmt(
3676 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
3677 &parentId, location.c_str(),
3678 m->pParent->getId().raw(),
3679 m->pVirtualBox->settingsFilePath().c_str());
3680 throw S_OK;
3681 }
3682
3683 /// @todo NEWMEDIA what to do if the parent is not
3684 /// accessible while the diff is? Probably nothing. The
3685 /// real code will detect the mismatch anyway.
3686 }
3687 }
3688
3689 mediumSize = VDGetFileSize(hdd, 0);
3690 mediumLogicalSize = VDGetSize(hdd, 0) / _1M;
3691
3692 success = true;
3693 }
3694 catch (HRESULT aRC)
3695 {
3696 rc = aRC;
3697 }
3698
3699 VDDestroy(hdd);
3700
3701 }
3702 catch (HRESULT aRC)
3703 {
3704 rc = aRC;
3705 }
3706
3707 alock.enter();
3708
3709 if (isImport)
3710 unconst(m->id) = mediumId;
3711
3712 if (success)
3713 {
3714 m->size = mediumSize;
3715 m->logicalSize = mediumLogicalSize;
3716 m->strLastAccessError.setNull();
3717 }
3718 else
3719 {
3720 m->strLastAccessError = lastAccessError;
3721 LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
3722 location.c_str(), m->strLastAccessError.c_str(),
3723 rc, vrc));
3724 }
3725
3726 /* inform other callers if there are any */
3727 RTSemEventMultiSignal(m->queryInfoSem);
3728 m->queryInfoRunning = false;
3729
3730 /* Set the proper state according to the result of the check */
3731 if (success)
3732 m->preLockState = MediumState_Created;
3733 else
3734 m->preLockState = MediumState_Inaccessible;
3735
3736 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY || VD_OPEN_FLAGS_SHAREABLE))
3737 rc = UnlockRead(NULL);
3738 else
3739 rc = UnlockWrite(NULL);
3740 if (FAILED(rc)) return rc;
3741
3742 return rc;
3743}
3744
3745/**
3746 * Sets the extended error info according to the current media state.
3747 *
3748 * @note Must be called from under this object's write or read lock.
3749 */
3750HRESULT Medium::setStateError()
3751{
3752 HRESULT rc = E_FAIL;
3753
3754 switch (m->state)
3755 {
3756 case MediumState_NotCreated:
3757 {
3758 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3759 tr("Storage for the medium '%s' is not created"),
3760 m->strLocationFull.c_str());
3761 break;
3762 }
3763 case MediumState_Created:
3764 {
3765 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3766 tr("Storage for the medium '%s' is already created"),
3767 m->strLocationFull.c_str());
3768 break;
3769 }
3770 case MediumState_LockedRead:
3771 {
3772 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3773 tr("Medium '%s' is locked for reading by another task"),
3774 m->strLocationFull.c_str());
3775 break;
3776 }
3777 case MediumState_LockedWrite:
3778 {
3779 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3780 tr("Medium '%s' is locked for writing by another task"),
3781 m->strLocationFull.c_str());
3782 break;
3783 }
3784 case MediumState_Inaccessible:
3785 {
3786 /* be in sync with Console::powerUpThread() */
3787 if (!m->strLastAccessError.isEmpty())
3788 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3789 tr("Medium '%s' is not accessible. %s"),
3790 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
3791 else
3792 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3793 tr("Medium '%s' is not accessible"),
3794 m->strLocationFull.c_str());
3795 break;
3796 }
3797 case MediumState_Creating:
3798 {
3799 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3800 tr("Storage for the medium '%s' is being created"),
3801 m->strLocationFull.c_str());
3802 break;
3803 }
3804 case MediumState_Deleting:
3805 {
3806 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3807 tr("Storage for the medium '%s' is being deleted"),
3808 m->strLocationFull.c_str());
3809 break;
3810 }
3811 default:
3812 {
3813 AssertFailed();
3814 break;
3815 }
3816 }
3817
3818 return rc;
3819}
3820
3821/**
3822 * Implementation for the public Medium::Close() with the exception of calling
3823 * VirtualBox::saveSettings(), in case someone wants to call this for several
3824 * media.
3825 *
3826 * After this returns with success, uninit() has been called on the medium, and
3827 * the object is no longer usable ("not ready" state).
3828 *
3829 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3830 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3831 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
3832 * and this parameter is ignored.
3833 * @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here
3834 * upon which the Medium instance gets uninitialized.
3835 * @return
3836 */
3837HRESULT Medium::close(bool *pfNeedsSaveSettings, AutoCaller &autoCaller)
3838{
3839 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
3840 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
3841 this->lockHandle()
3842 COMMA_LOCKVAL_SRC_POS);
3843
3844 LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str()));
3845
3846 bool wasCreated = true;
3847
3848 switch (m->state)
3849 {
3850 case MediumState_NotCreated:
3851 wasCreated = false;
3852 break;
3853 case MediumState_Created:
3854 case MediumState_Inaccessible:
3855 break;
3856 default:
3857 return setStateError();
3858 }
3859
3860 if (m->backRefs.size() != 0)
3861 return setError(VBOX_E_OBJECT_IN_USE,
3862 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
3863 m->strLocationFull.c_str(), m->backRefs.size());
3864
3865 // perform extra media-dependent close checks
3866 HRESULT rc = canClose();
3867 if (FAILED(rc)) return rc;
3868
3869 if (wasCreated)
3870 {
3871 // remove from the list of known media before performing actual
3872 // uninitialization (to keep the media registry consistent on
3873 // failure to do so)
3874 rc = unregisterWithVirtualBox(pfNeedsSaveSettings);
3875 if (FAILED(rc)) return rc;
3876 }
3877
3878 // leave the AutoCaller, as otherwise uninit() will simply hang
3879 autoCaller.release();
3880
3881 // Keep the locks held until after uninit, as otherwise the consistency
3882 // of the medium tree cannot be guaranteed.
3883 uninit();
3884
3885 LogFlowFuncLeave();
3886
3887 return rc;
3888}
3889
3890/**
3891 * Deletes the medium storage unit.
3892 *
3893 * If @a aProgress is not NULL but the object it points to is @c null then a new
3894 * progress object will be created and assigned to @a *aProgress on success,
3895 * otherwise the existing progress object is used. If Progress is NULL, then no
3896 * progress object is created/used at all.
3897 *
3898 * When @a aWait is @c false, this method will create a thread to perform the
3899 * delete operation asynchronously and will return immediately. Otherwise, it
3900 * will perform the operation on the calling thread and will not return to the
3901 * caller until the operation is completed. Note that @a aProgress cannot be
3902 * NULL when @a aWait is @c false (this method will assert in this case).
3903 *
3904 * @param aProgress Where to find/store a Progress object to track operation
3905 * completion.
3906 * @param aWait @c true if this method should block instead of creating
3907 * an asynchronous thread.
3908 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3909 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3910 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
3911 * and this parameter is ignored.
3912 *
3913 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
3914 * writing.
3915 */
3916HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
3917 bool aWait,
3918 bool *pfNeedsSaveSettings)
3919{
3920 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
3921
3922 AutoCaller autoCaller(this);
3923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3924
3925 HRESULT rc = S_OK;
3926 ComObjPtr<Progress> pProgress;
3927 Medium::Task *pTask = NULL;
3928
3929 try
3930 {
3931 /* we're accessing the media tree, and canClose() needs it too */
3932 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
3933 this->lockHandle()
3934 COMMA_LOCKVAL_SRC_POS);
3935 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
3936
3937 if ( !(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
3938 | MediumFormatCapabilities_CreateFixed)))
3939 throw setError(VBOX_E_NOT_SUPPORTED,
3940 tr("Medium format '%s' does not support storage deletion"),
3941 m->strFormat.c_str());
3942
3943 /* Note that we are fine with Inaccessible state too: a) for symmetry
3944 * with create calls and b) because it doesn't really harm to try, if
3945 * it is really inaccessible, the delete operation will fail anyway.
3946 * Accepting Inaccessible state is especially important because all
3947 * registered media are initially Inaccessible upon VBoxSVC startup
3948 * until COMGETTER(RefreshState) is called. Accept Deleting state
3949 * because some callers need to put the medium in this state early
3950 * to prevent races. */
3951 switch (m->state)
3952 {
3953 case MediumState_Created:
3954 case MediumState_Deleting:
3955 case MediumState_Inaccessible:
3956 break;
3957 default:
3958 throw setStateError();
3959 }
3960
3961 if (m->backRefs.size() != 0)
3962 {
3963 Utf8Str strMachines;
3964 for (BackRefList::const_iterator it = m->backRefs.begin();
3965 it != m->backRefs.end();
3966 ++it)
3967 {
3968 const BackRef &b = *it;
3969 if (strMachines.length())
3970 strMachines.append(", ");
3971 strMachines.append(b.machineId.toString().c_str());
3972 }
3973#ifdef DEBUG
3974 dumpBackRefs();
3975#endif
3976 throw setError(VBOX_E_OBJECT_IN_USE,
3977 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
3978 m->strLocationFull.c_str(),
3979 m->backRefs.size(),
3980 strMachines.c_str());
3981 }
3982
3983 rc = canClose();
3984 if (FAILED(rc))
3985 throw rc;
3986
3987 /* go to Deleting state, so that the medium is not actually locked */
3988 if (m->state != MediumState_Deleting)
3989 {
3990 rc = markForDeletion();
3991 if (FAILED(rc))
3992 throw rc;
3993 }
3994
3995 /* Build the medium lock list. */
3996 MediumLockList *pMediumLockList(new MediumLockList());
3997 rc = createMediumLockList(true /* fFailIfInaccessible */,
3998 true /* fMediumLockWrite */,
3999 NULL,
4000 *pMediumLockList);
4001 if (FAILED(rc))
4002 {
4003 delete pMediumLockList;
4004 throw rc;
4005 }
4006
4007 rc = pMediumLockList->Lock();
4008 if (FAILED(rc))
4009 {
4010 delete pMediumLockList;
4011 throw setError(rc,
4012 tr("Failed to lock media when deleting '%s'"),
4013 getLocationFull().c_str());
4014 }
4015
4016 /* try to remove from the list of known media before performing
4017 * actual deletion (we favor the consistency of the media registry
4018 * which would have been broken if unregisterWithVirtualBox() failed
4019 * after we successfully deleted the storage) */
4020 rc = unregisterWithVirtualBox(pfNeedsSaveSettings);
4021 if (FAILED(rc))
4022 throw rc;
4023 // no longer need lock
4024 multilock.release();
4025
4026 if (aProgress != NULL)
4027 {
4028 /* use the existing progress object... */
4029 pProgress = *aProgress;
4030
4031 /* ...but create a new one if it is null */
4032 if (pProgress.isNull())
4033 {
4034 pProgress.createObject();
4035 rc = pProgress->init(m->pVirtualBox,
4036 static_cast<IMedium*>(this),
4037 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()),
4038 FALSE /* aCancelable */);
4039 if (FAILED(rc))
4040 throw rc;
4041 }
4042 }
4043
4044 /* setup task object to carry out the operation sync/async */
4045 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
4046 rc = pTask->rc();
4047 AssertComRC(rc);
4048 if (FAILED(rc))
4049 throw rc;
4050 }
4051 catch (HRESULT aRC) { rc = aRC; }
4052
4053 if (SUCCEEDED(rc))
4054 {
4055 if (aWait)
4056 rc = runNow(pTask, NULL /* pfNeedsSaveSettings*/);
4057 else
4058 rc = startThread(pTask);
4059
4060 if (SUCCEEDED(rc) && aProgress != NULL)
4061 *aProgress = pProgress;
4062
4063 }
4064 else
4065 {
4066 if (pTask)
4067 delete pTask;
4068
4069 /* Undo deleting state if necessary. */
4070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4071 unmarkForDeletion();
4072 }
4073
4074 return rc;
4075}
4076
4077/**
4078 * Mark a medium for deletion.
4079 *
4080 * @note Caller must hold the write lock on this medium!
4081 */
4082HRESULT Medium::markForDeletion()
4083{
4084 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4085 switch (m->state)
4086 {
4087 case MediumState_Created:
4088 case MediumState_Inaccessible:
4089 m->preLockState = m->state;
4090 m->state = MediumState_Deleting;
4091 return S_OK;
4092 default:
4093 return setStateError();
4094 }
4095}
4096
4097/**
4098 * Removes the "mark for deletion".
4099 *
4100 * @note Caller must hold the write lock on this medium!
4101 */
4102HRESULT Medium::unmarkForDeletion()
4103{
4104 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4105 switch (m->state)
4106 {
4107 case MediumState_Deleting:
4108 m->state = m->preLockState;
4109 return S_OK;
4110 default:
4111 return setStateError();
4112 }
4113}
4114
4115/**
4116 * Mark a medium for deletion which is in locked state.
4117 *
4118 * @note Caller must hold the write lock on this medium!
4119 */
4120HRESULT Medium::markLockedForDeletion()
4121{
4122 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4123 if ( ( m->state == MediumState_LockedRead
4124 || m->state == MediumState_LockedWrite)
4125 && m->preLockState == MediumState_Created)
4126 {
4127 m->preLockState = MediumState_Deleting;
4128 return S_OK;
4129 }
4130 else
4131 return setStateError();
4132}
4133
4134/**
4135 * Removes the "mark for deletion" for a medium in locked state.
4136 *
4137 * @note Caller must hold the write lock on this medium!
4138 */
4139HRESULT Medium::unmarkLockedForDeletion()
4140{
4141 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4142 if ( ( m->state == MediumState_LockedRead
4143 || m->state == MediumState_LockedWrite)
4144 && m->preLockState == MediumState_Deleting)
4145 {
4146 m->preLockState = MediumState_Created;
4147 return S_OK;
4148 }
4149 else
4150 return setStateError();
4151}
4152
4153/**
4154 * Creates a new differencing storage unit using the format of the given target
4155 * medium and the location. Note that @c aTarget must be NotCreated.
4156 *
4157 * The @a aMediumLockList parameter contains the associated medium lock list,
4158 * which must be in locked state. If @a aWait is @c true then the caller is
4159 * responsible for unlocking.
4160 *
4161 * If @a aProgress is not NULL but the object it points to is @c null then a
4162 * new progress object will be created and assigned to @a *aProgress on
4163 * success, otherwise the existing progress object is used. If @a aProgress is
4164 * NULL, then no progress object is created/used at all.
4165 *
4166 * When @a aWait is @c false, this method will create a thread to perform the
4167 * create operation asynchronously and will return immediately. Otherwise, it
4168 * will perform the operation on the calling thread and will not return to the
4169 * caller until the operation is completed. Note that @a aProgress cannot be
4170 * NULL when @a aWait is @c false (this method will assert in this case).
4171 *
4172 * @param aTarget Target medium.
4173 * @param aVariant Precise medium variant to create.
4174 * @param aMediumLockList List of media which should be locked.
4175 * @param aProgress Where to find/store a Progress object to track
4176 * operation completion.
4177 * @param aWait @c true if this method should block instead of
4178 * creating an asynchronous thread.
4179 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been
4180 * initialized to false and that will be set to true
4181 * by this function if the caller should invoke
4182 * VirtualBox::saveSettings() because the global
4183 * settings have changed. This only works in "wait"
4184 * mode; otherwise saveSettings is called
4185 * automatically by the thread that was created,
4186 * and this parameter is ignored.
4187 *
4188 * @note Locks this object and @a aTarget for writing.
4189 */
4190HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
4191 MediumVariant_T aVariant,
4192 MediumLockList *aMediumLockList,
4193 ComObjPtr<Progress> *aProgress,
4194 bool aWait,
4195 bool *pfNeedsSaveSettings)
4196{
4197 AssertReturn(!aTarget.isNull(), E_FAIL);
4198 AssertReturn(aMediumLockList, E_FAIL);
4199 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4200
4201 AutoCaller autoCaller(this);
4202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4203
4204 AutoCaller targetCaller(aTarget);
4205 if (FAILED(targetCaller.rc())) return targetCaller.rc();
4206
4207 HRESULT rc = S_OK;
4208 ComObjPtr<Progress> pProgress;
4209 Medium::Task *pTask = NULL;
4210
4211 try
4212 {
4213 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
4214
4215 ComAssertThrow( m->type != MediumType_Writethrough
4216 && m->type != MediumType_Shareable, E_FAIL);
4217 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
4218
4219 if (aTarget->m->state != MediumState_NotCreated)
4220 throw aTarget->setStateError();
4221
4222 /* Check that the medium is not attached to the current state of
4223 * any VM referring to it. */
4224 for (BackRefList::const_iterator it = m->backRefs.begin();
4225 it != m->backRefs.end();
4226 ++it)
4227 {
4228 if (it->fInCurState)
4229 {
4230 /* Note: when a VM snapshot is being taken, all normal media
4231 * attached to the VM in the current state will be, as an
4232 * exception, also associated with the snapshot which is about
4233 * to create (see SnapshotMachine::init()) before deassociating
4234 * them from the current state (which takes place only on
4235 * success in Machine::fixupHardDisks()), so that the size of
4236 * snapshotIds will be 1 in this case. The extra condition is
4237 * used to filter out this legal situation. */
4238 if (it->llSnapshotIds.size() == 0)
4239 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4240 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"),
4241 m->strLocationFull.c_str(), it->machineId.raw());
4242
4243 Assert(it->llSnapshotIds.size() == 1);
4244 }
4245 }
4246
4247 if (aProgress != NULL)
4248 {
4249 /* use the existing progress object... */
4250 pProgress = *aProgress;
4251
4252 /* ...but create a new one if it is null */
4253 if (pProgress.isNull())
4254 {
4255 pProgress.createObject();
4256 rc = pProgress->init(m->pVirtualBox,
4257 static_cast<IMedium*>(this),
4258 BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()),
4259 TRUE /* aCancelable */);
4260 if (FAILED(rc))
4261 throw rc;
4262 }
4263 }
4264
4265 /* setup task object to carry out the operation sync/async */
4266 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
4267 aMediumLockList,
4268 aWait /* fKeepMediumLockList */);
4269 rc = pTask->rc();
4270 AssertComRC(rc);
4271 if (FAILED(rc))
4272 throw rc;
4273
4274 /* register a task (it will deregister itself when done) */
4275 ++m->numCreateDiffTasks;
4276 Assert(m->numCreateDiffTasks != 0); /* overflow? */
4277
4278 aTarget->m->state = MediumState_Creating;
4279 }
4280 catch (HRESULT aRC) { rc = aRC; }
4281
4282 if (SUCCEEDED(rc))
4283 {
4284 if (aWait)
4285 rc = runNow(pTask, pfNeedsSaveSettings);
4286 else
4287 rc = startThread(pTask);
4288
4289 if (SUCCEEDED(rc) && aProgress != NULL)
4290 *aProgress = pProgress;
4291 }
4292 else if (pTask != NULL)
4293 delete pTask;
4294
4295 return rc;
4296}
4297
4298/**
4299 * Prepares this (source) medium, target medium and all intermediate media
4300 * for the merge operation.
4301 *
4302 * This method is to be called prior to calling the #mergeTo() to perform
4303 * necessary consistency checks and place involved media to appropriate
4304 * states. If #mergeTo() is not called or fails, the state modifications
4305 * performed by this method must be undone by #cancelMergeTo().
4306 *
4307 * See #mergeTo() for more information about merging.
4308 *
4309 * @param pTarget Target medium.
4310 * @param aMachineId Allowed machine attachment. NULL means do not check.
4311 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
4312 * do not check.
4313 * @param fLockMedia Flag whether to lock the medium lock list or not.
4314 * If set to false and the medium lock list locking fails
4315 * later you must call #cancelMergeTo().
4316 * @param fMergeForward Resulting merge direction (out).
4317 * @param pParentForTarget New parent for target medium after merge (out).
4318 * @param aChildrenToReparent List of children of the source which will have
4319 * to be reparented to the target after merge (out).
4320 * @param aMediumLockList Medium locking information (out).
4321 *
4322 * @note Locks medium tree for reading. Locks this object, aTarget and all
4323 * intermediate media for writing.
4324 */
4325HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
4326 const Guid *aMachineId,
4327 const Guid *aSnapshotId,
4328 bool fLockMedia,
4329 bool &fMergeForward,
4330 ComObjPtr<Medium> &pParentForTarget,
4331 MediaList &aChildrenToReparent,
4332 MediumLockList * &aMediumLockList)
4333{
4334 AssertReturn(pTarget != NULL, E_FAIL);
4335 AssertReturn(pTarget != this, E_FAIL);
4336
4337 AutoCaller autoCaller(this);
4338 AssertComRCReturnRC(autoCaller.rc());
4339
4340 AutoCaller targetCaller(pTarget);
4341 AssertComRCReturnRC(targetCaller.rc());
4342
4343 HRESULT rc = S_OK;
4344 fMergeForward = false;
4345 pParentForTarget.setNull();
4346 aChildrenToReparent.clear();
4347 Assert(aMediumLockList == NULL);
4348 aMediumLockList = NULL;
4349
4350 try
4351 {
4352 // locking: we need the tree lock first because we access parent pointers
4353 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4354
4355 /* more sanity checking and figuring out the merge direction */
4356 ComObjPtr<Medium> pMedium = getParent();
4357 while (!pMedium.isNull() && pMedium != pTarget)
4358 pMedium = pMedium->getParent();
4359 if (pMedium == pTarget)
4360 fMergeForward = false;
4361 else
4362 {
4363 pMedium = pTarget->getParent();
4364 while (!pMedium.isNull() && pMedium != this)
4365 pMedium = pMedium->getParent();
4366 if (pMedium == this)
4367 fMergeForward = true;
4368 else
4369 {
4370 Utf8Str tgtLoc;
4371 {
4372 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4373 tgtLoc = pTarget->getLocationFull();
4374 }
4375
4376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4377 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4378 tr("Media '%s' and '%s' are unrelated"),
4379 m->strLocationFull.c_str(), tgtLoc.c_str());
4380 }
4381 }
4382
4383 /* Build the lock list. */
4384 aMediumLockList = new MediumLockList();
4385 if (fMergeForward)
4386 rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
4387 true /* fMediumLockWrite */,
4388 NULL,
4389 *aMediumLockList);
4390 else
4391 rc = createMediumLockList(true /* fFailIfInaccessible */,
4392 false /* fMediumLockWrite */,
4393 NULL,
4394 *aMediumLockList);
4395 if (FAILED(rc))
4396 throw rc;
4397
4398 /* Sanity checking, must be after lock list creation as it depends on
4399 * valid medium states. The medium objects must be accessible. Only
4400 * do this if immediate locking is requested, otherwise it fails when
4401 * we construct a medium lock list for an already running VM. Snapshot
4402 * deletion uses this to simplify its life. */
4403 if (fLockMedia)
4404 {
4405 {
4406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4407 if (m->state != MediumState_Created)
4408 throw setStateError();
4409 }
4410 {
4411 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4412 if (pTarget->m->state != MediumState_Created)
4413 throw pTarget->setStateError();
4414 }
4415 }
4416
4417 /* check medium attachment and other sanity conditions */
4418 if (fMergeForward)
4419 {
4420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4421 if (getChildren().size() > 1)
4422 {
4423 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4424 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4425 m->strLocationFull.c_str(), getChildren().size());
4426 }
4427 /* One backreference is only allowed if the machine ID is not empty
4428 * and it matches the machine the medium is attached to (including
4429 * the snapshot ID if not empty). */
4430 if ( m->backRefs.size() != 0
4431 && ( !aMachineId
4432 || m->backRefs.size() != 1
4433 || aMachineId->isEmpty()
4434 || *getFirstMachineBackrefId() != *aMachineId
4435 || ( (!aSnapshotId || !aSnapshotId->isEmpty())
4436 && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
4437 throw setError(VBOX_E_OBJECT_IN_USE,
4438 tr("Medium '%s' is attached to %d virtual machines"),
4439 m->strLocationFull.c_str(), m->backRefs.size());
4440 if (m->type == MediumType_Immutable)
4441 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4442 tr("Medium '%s' is immutable"),
4443 m->strLocationFull.c_str());
4444 }
4445 else
4446 {
4447 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4448 if (pTarget->getChildren().size() > 1)
4449 {
4450 throw setError(VBOX_E_OBJECT_IN_USE,
4451 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4452 pTarget->m->strLocationFull.c_str(),
4453 pTarget->getChildren().size());
4454 }
4455 if (pTarget->m->type == MediumType_Immutable)
4456 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4457 tr("Medium '%s' is immutable"),
4458 pTarget->m->strLocationFull.c_str());
4459 }
4460 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
4461 ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
4462 for (pLast = pLastIntermediate;
4463 !pLast.isNull() && pLast != pTarget && pLast != this;
4464 pLast = pLast->getParent())
4465 {
4466 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4467 if (pLast->getChildren().size() > 1)
4468 {
4469 throw setError(VBOX_E_OBJECT_IN_USE,
4470 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4471 pLast->m->strLocationFull.c_str(),
4472 pLast->getChildren().size());
4473 }
4474 if (pLast->m->backRefs.size() != 0)
4475 throw setError(VBOX_E_OBJECT_IN_USE,
4476 tr("Medium '%s' is attached to %d virtual machines"),
4477 pLast->m->strLocationFull.c_str(),
4478 pLast->m->backRefs.size());
4479
4480 }
4481
4482 /* Update medium states appropriately */
4483 if (m->state == MediumState_Created)
4484 {
4485 rc = markForDeletion();
4486 if (FAILED(rc))
4487 throw rc;
4488 }
4489 else
4490 {
4491 if (fLockMedia)
4492 throw setStateError();
4493 else if ( m->state == MediumState_LockedWrite
4494 || m->state == MediumState_LockedRead)
4495 {
4496 /* Either mark it for deletiion in locked state or allow
4497 * others to have done so. */
4498 if (m->preLockState == MediumState_Created)
4499 markLockedForDeletion();
4500 else if (m->preLockState != MediumState_Deleting)
4501 throw setStateError();
4502 }
4503 else
4504 throw setStateError();
4505 }
4506
4507 if (fMergeForward)
4508 {
4509 /* we will need parent to reparent target */
4510 pParentForTarget = m->pParent;
4511 }
4512 else
4513 {
4514 /* we will need to reparent children of the source */
4515 for (MediaList::const_iterator it = getChildren().begin();
4516 it != getChildren().end();
4517 ++it)
4518 {
4519 pMedium = *it;
4520 if (fLockMedia)
4521 {
4522 rc = pMedium->LockWrite(NULL);
4523 if (FAILED(rc))
4524 throw rc;
4525 }
4526
4527 aChildrenToReparent.push_back(pMedium);
4528 }
4529 }
4530 for (pLast = pLastIntermediate;
4531 !pLast.isNull() && pLast != pTarget && pLast != this;
4532 pLast = pLast->getParent())
4533 {
4534 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4535 if (pLast->m->state == MediumState_Created)
4536 {
4537 rc = pLast->markForDeletion();
4538 if (FAILED(rc))
4539 throw rc;
4540 }
4541 else
4542 throw pLast->setStateError();
4543 }
4544
4545 /* Tweak the lock list in the backward merge case, as the target
4546 * isn't marked to be locked for writing yet. */
4547 if (!fMergeForward)
4548 {
4549 MediumLockList::Base::iterator lockListBegin =
4550 aMediumLockList->GetBegin();
4551 MediumLockList::Base::iterator lockListEnd =
4552 aMediumLockList->GetEnd();
4553 lockListEnd--;
4554 for (MediumLockList::Base::iterator it = lockListBegin;
4555 it != lockListEnd;
4556 ++it)
4557 {
4558 MediumLock &mediumLock = *it;
4559 if (mediumLock.GetMedium() == pTarget)
4560 {
4561 HRESULT rc2 = mediumLock.UpdateLock(true);
4562 AssertComRC(rc2);
4563 break;
4564 }
4565 }
4566 }
4567
4568 if (fLockMedia)
4569 {
4570 rc = aMediumLockList->Lock();
4571 if (FAILED(rc))
4572 {
4573 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4574 throw setError(rc,
4575 tr("Failed to lock media when merging to '%s'"),
4576 pTarget->getLocationFull().c_str());
4577 }
4578 }
4579 }
4580 catch (HRESULT aRC) { rc = aRC; }
4581
4582 if (FAILED(rc))
4583 {
4584 delete aMediumLockList;
4585 aMediumLockList = NULL;
4586 }
4587
4588 return rc;
4589}
4590
4591/**
4592 * Merges this medium to the specified medium which must be either its
4593 * direct ancestor or descendant.
4594 *
4595 * Given this medium is SOURCE and the specified medium is TARGET, we will
4596 * get two variants of the merge operation:
4597 *
4598 * forward merge
4599 * ------------------------->
4600 * [Extra] <- SOURCE <- Intermediate <- TARGET
4601 * Any Del Del LockWr
4602 *
4603 *
4604 * backward merge
4605 * <-------------------------
4606 * TARGET <- Intermediate <- SOURCE <- [Extra]
4607 * LockWr Del Del LockWr
4608 *
4609 * Each diagram shows the involved media on the media chain where
4610 * SOURCE and TARGET belong. Under each medium there is a state value which
4611 * the medium must have at a time of the mergeTo() call.
4612 *
4613 * The media in the square braces may be absent (e.g. when the forward
4614 * operation takes place and SOURCE is the base medium, or when the backward
4615 * merge operation takes place and TARGET is the last child in the chain) but if
4616 * they present they are involved too as shown.
4617 *
4618 * Neither the source medium nor intermediate media may be attached to
4619 * any VM directly or in the snapshot, otherwise this method will assert.
4620 *
4621 * The #prepareMergeTo() method must be called prior to this method to place all
4622 * involved to necessary states and perform other consistency checks.
4623 *
4624 * If @a aWait is @c true then this method will perform the operation on the
4625 * calling thread and will not return to the caller until the operation is
4626 * completed. When this method succeeds, all intermediate medium objects in
4627 * the chain will be uninitialized, the state of the target medium (and all
4628 * involved extra media) will be restored. @a aMediumLockList will not be
4629 * deleted, whether the operation is successful or not. The caller has to do
4630 * this if appropriate. Note that this (source) medium is not uninitialized
4631 * because of possible AutoCaller instances held by the caller of this method
4632 * on the current thread. It's therefore the responsibility of the caller to
4633 * call Medium::uninit() after releasing all callers.
4634 *
4635 * If @a aWait is @c false then this method will create a thread to perform the
4636 * operation asynchronously and will return immediately. If the operation
4637 * succeeds, the thread will uninitialize the source medium object and all
4638 * intermediate medium objects in the chain, reset the state of the target
4639 * medium (and all involved extra media) and delete @a aMediumLockList.
4640 * If the operation fails, the thread will only reset the states of all
4641 * involved media and delete @a aMediumLockList.
4642 *
4643 * When this method fails (regardless of the @a aWait mode), it is a caller's
4644 * responsiblity to undo state changes and delete @a aMediumLockList using
4645 * #cancelMergeTo().
4646 *
4647 * If @a aProgress is not NULL but the object it points to is @c null then a new
4648 * progress object will be created and assigned to @a *aProgress on success,
4649 * otherwise the existing progress object is used. If Progress is NULL, then no
4650 * progress object is created/used at all. Note that @a aProgress cannot be
4651 * NULL when @a aWait is @c false (this method will assert in this case).
4652 *
4653 * @param pTarget Target medium.
4654 * @param fMergeForward Merge direction.
4655 * @param pParentForTarget New parent for target medium after merge.
4656 * @param aChildrenToReparent List of children of the source which will have
4657 * to be reparented to the target after merge.
4658 * @param aMediumLockList Medium locking information.
4659 * @param aProgress Where to find/store a Progress object to track operation
4660 * completion.
4661 * @param aWait @c true if this method should block instead of creating
4662 * an asynchronous thread.
4663 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
4664 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
4665 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
4666 * and this parameter is ignored.
4667 *
4668 * @note Locks the tree lock for writing. Locks the media from the chain
4669 * for writing.
4670 */
4671HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
4672 bool fMergeForward,
4673 const ComObjPtr<Medium> &pParentForTarget,
4674 const MediaList &aChildrenToReparent,
4675 MediumLockList *aMediumLockList,
4676 ComObjPtr <Progress> *aProgress,
4677 bool aWait,
4678 bool *pfNeedsSaveSettings)
4679{
4680 AssertReturn(pTarget != NULL, E_FAIL);
4681 AssertReturn(pTarget != this, E_FAIL);
4682 AssertReturn(aMediumLockList != NULL, E_FAIL);
4683 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4684
4685 AutoCaller autoCaller(this);
4686 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4687
4688 AutoCaller targetCaller(pTarget);
4689 AssertComRCReturnRC(targetCaller.rc());
4690
4691 HRESULT rc = S_OK;
4692 ComObjPtr <Progress> pProgress;
4693 Medium::Task *pTask = NULL;
4694
4695 try
4696 {
4697 if (aProgress != NULL)
4698 {
4699 /* use the existing progress object... */
4700 pProgress = *aProgress;
4701
4702 /* ...but create a new one if it is null */
4703 if (pProgress.isNull())
4704 {
4705 Utf8Str tgtName;
4706 {
4707 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4708 tgtName = pTarget->getName();
4709 }
4710
4711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4712
4713 pProgress.createObject();
4714 rc = pProgress->init(m->pVirtualBox,
4715 static_cast<IMedium*>(this),
4716 BstrFmt(tr("Merging medium '%s' to '%s'"),
4717 getName().c_str(),
4718 tgtName.c_str()),
4719 TRUE /* aCancelable */);
4720 if (FAILED(rc))
4721 throw rc;
4722 }
4723 }
4724
4725 /* setup task object to carry out the operation sync/async */
4726 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
4727 pParentForTarget, aChildrenToReparent,
4728 pProgress, aMediumLockList,
4729 aWait /* fKeepMediumLockList */);
4730 rc = pTask->rc();
4731 AssertComRC(rc);
4732 if (FAILED(rc))
4733 throw rc;
4734 }
4735 catch (HRESULT aRC) { rc = aRC; }
4736
4737 if (SUCCEEDED(rc))
4738 {
4739 if (aWait)
4740 rc = runNow(pTask, pfNeedsSaveSettings);
4741 else
4742 rc = startThread(pTask);
4743
4744 if (SUCCEEDED(rc) && aProgress != NULL)
4745 *aProgress = pProgress;
4746 }
4747 else if (pTask != NULL)
4748 delete pTask;
4749
4750 return rc;
4751}
4752
4753/**
4754 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
4755 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
4756 * the medium objects in @a aChildrenToReparent.
4757 *
4758 * @param aChildrenToReparent List of children of the source which will have
4759 * to be reparented to the target after merge.
4760 * @param aMediumLockList Medium locking information.
4761 *
4762 * @note Locks the media from the chain for writing.
4763 */
4764void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
4765 MediumLockList *aMediumLockList)
4766{
4767 AutoCaller autoCaller(this);
4768 AssertComRCReturnVoid(autoCaller.rc());
4769
4770 AssertReturnVoid(aMediumLockList != NULL);
4771
4772 /* Revert media marked for deletion to previous state. */
4773 HRESULT rc;
4774 MediumLockList::Base::const_iterator mediumListBegin =
4775 aMediumLockList->GetBegin();
4776 MediumLockList::Base::const_iterator mediumListEnd =
4777 aMediumLockList->GetEnd();
4778 for (MediumLockList::Base::const_iterator it = mediumListBegin;
4779 it != mediumListEnd;
4780 ++it)
4781 {
4782 const MediumLock &mediumLock = *it;
4783 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4784 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4785
4786 if (pMedium->m->state == MediumState_Deleting)
4787 {
4788 rc = pMedium->unmarkForDeletion();
4789 AssertComRC(rc);
4790 }
4791 }
4792
4793 /* the destructor will do the work */
4794 delete aMediumLockList;
4795
4796 /* unlock the children which had to be reparented */
4797 for (MediaList::const_iterator it = aChildrenToReparent.begin();
4798 it != aChildrenToReparent.end();
4799 ++it)
4800 {
4801 const ComObjPtr<Medium> &pMedium = *it;
4802
4803 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4804 pMedium->UnlockWrite(NULL);
4805 }
4806}
4807
4808////////////////////////////////////////////////////////////////////////////////
4809//
4810// Private methods
4811//
4812////////////////////////////////////////////////////////////////////////////////
4813
4814/**
4815 * Performs extra checks if the medium can be closed and returns S_OK in
4816 * this case. Otherwise, returns a respective error message. Called by
4817 * Close() under the medium tree lock and the medium lock.
4818 *
4819 * @note Also reused by Medium::Reset().
4820 *
4821 * @note Caller must hold the media tree write lock!
4822 */
4823HRESULT Medium::canClose()
4824{
4825 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4826
4827 if (getChildren().size() != 0)
4828 return setError(VBOX_E_OBJECT_IN_USE,
4829 tr("Cannot close medium '%s' because it has %d child media"),
4830 m->strLocationFull.c_str(), getChildren().size());
4831
4832 return S_OK;
4833}
4834
4835/**
4836 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
4837 *
4838 * This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending
4839 * on the device type of this medium.
4840 *
4841 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
4842 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
4843 *
4844 * @note Caller must have locked the media tree lock for writing!
4845 */
4846HRESULT Medium::unregisterWithVirtualBox(bool *pfNeedsSaveSettings)
4847{
4848 /* Note that we need to de-associate ourselves from the parent to let
4849 * unregisterHardDisk() properly save the registry */
4850
4851 /* we modify mParent and access children */
4852 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4853
4854 Medium *pParentBackup = m->pParent;
4855 AssertReturn(getChildren().size() == 0, E_FAIL);
4856 if (m->pParent)
4857 deparent();
4858
4859 HRESULT rc = E_FAIL;
4860 switch (m->devType)
4861 {
4862 case DeviceType_DVD:
4863 rc = m->pVirtualBox->unregisterImage(this, DeviceType_DVD, pfNeedsSaveSettings);
4864 break;
4865
4866 case DeviceType_Floppy:
4867 rc = m->pVirtualBox->unregisterImage(this, DeviceType_Floppy, pfNeedsSaveSettings);
4868 break;
4869
4870 case DeviceType_HardDisk:
4871 rc = m->pVirtualBox->unregisterHardDisk(this, pfNeedsSaveSettings);
4872 break;
4873
4874 default:
4875 break;
4876 }
4877
4878 if (FAILED(rc))
4879 {
4880 if (pParentBackup)
4881 {
4882 // re-associate with the parent as we are still relatives in the registry
4883 m->pParent = pParentBackup;
4884 m->pParent->m->llChildren.push_back(this);
4885 }
4886 }
4887
4888 return rc;
4889}
4890
4891/**
4892 * Checks that the format ID is valid and sets it on success.
4893 *
4894 * Note that this method will caller-reference the format object on success!
4895 * This reference must be released somewhere to let the MediumFormat object be
4896 * uninitialized.
4897 *
4898 * @note Must be called from under this object's write lock.
4899 */
4900HRESULT Medium::setFormat(const Utf8Str &aFormat)
4901{
4902 /* get the format object first */
4903 {
4904 SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
4905 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
4906
4907 unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
4908 if (m->formatObj.isNull())
4909 return setError(E_INVALIDARG,
4910 tr("Invalid medium storage format '%s'"),
4911 aFormat.c_str());
4912
4913 /* reference the format permanently to prevent its unexpected
4914 * uninitialization */
4915 HRESULT rc = m->formatObj->addCaller();
4916 AssertComRCReturnRC(rc);
4917
4918 /* get properties (preinsert them as keys in the map). Note that the
4919 * map doesn't grow over the object life time since the set of
4920 * properties is meant to be constant. */
4921
4922 Assert(m->mapProperties.empty());
4923
4924 for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
4925 it != m->formatObj->getProperties().end();
4926 ++it)
4927 {
4928 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
4929 }
4930 }
4931
4932 unconst(m->strFormat) = aFormat;
4933
4934 return S_OK;
4935}
4936
4937/**
4938 * Returns the last error message collected by the vdErrorCall callback and
4939 * resets it.
4940 *
4941 * The error message is returned prepended with a dot and a space, like this:
4942 * <code>
4943 * ". <error_text> (%Rrc)"
4944 * </code>
4945 * to make it easily appendable to a more general error message. The @c %Rrc
4946 * format string is given @a aVRC as an argument.
4947 *
4948 * If there is no last error message collected by vdErrorCall or if it is a
4949 * null or empty string, then this function returns the following text:
4950 * <code>
4951 * " (%Rrc)"
4952 * </code>
4953 *
4954 * @note Doesn't do any object locking; it is assumed that the caller makes sure
4955 * the callback isn't called by more than one thread at a time.
4956 *
4957 * @param aVRC VBox error code to use when no error message is provided.
4958 */
4959Utf8Str Medium::vdError(int aVRC)
4960{
4961 Utf8Str error;
4962
4963 if (m->vdError.isEmpty())
4964 error = Utf8StrFmt(" (%Rrc)", aVRC);
4965 else
4966 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
4967
4968 m->vdError.setNull();
4969
4970 return error;
4971}
4972
4973/**
4974 * Error message callback.
4975 *
4976 * Puts the reported error message to the m->vdError field.
4977 *
4978 * @note Doesn't do any object locking; it is assumed that the caller makes sure
4979 * the callback isn't called by more than one thread at a time.
4980 *
4981 * @param pvUser The opaque data passed on container creation.
4982 * @param rc The VBox error code.
4983 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
4984 * @param pszFormat Error message format string.
4985 * @param va Error message arguments.
4986 */
4987/*static*/
4988DECLCALLBACK(void) Medium::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
4989 const char *pszFormat, va_list va)
4990{
4991 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
4992
4993 Medium *that = static_cast<Medium*>(pvUser);
4994 AssertReturnVoid(that != NULL);
4995
4996 if (that->m->vdError.isEmpty())
4997 that->m->vdError =
4998 Utf8StrFmt("%s (%Rrc)", Utf8StrFmtVA(pszFormat, va).c_str(), rc);
4999 else
5000 that->m->vdError =
5001 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
5002 Utf8StrFmtVA(pszFormat, va).c_str(), rc);
5003}
5004
5005/* static */
5006DECLCALLBACK(bool) Medium::vdConfigAreKeysValid(void *pvUser,
5007 const char * /* pszzValid */)
5008{
5009 Medium *that = static_cast<Medium*>(pvUser);
5010 AssertReturn(that != NULL, false);
5011
5012 /* we always return true since the only keys we have are those found in
5013 * VDBACKENDINFO */
5014 return true;
5015}
5016
5017/* static */
5018DECLCALLBACK(int) Medium::vdConfigQuerySize(void *pvUser,
5019 const char *pszName,
5020 size_t *pcbValue)
5021{
5022 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
5023
5024 Medium *that = static_cast<Medium*>(pvUser);
5025 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
5026
5027 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
5028 if (it == that->m->mapProperties.end())
5029 return VERR_CFGM_VALUE_NOT_FOUND;
5030
5031 /* we interpret null values as "no value" in Medium */
5032 if (it->second.isEmpty())
5033 return VERR_CFGM_VALUE_NOT_FOUND;
5034
5035 *pcbValue = it->second.length() + 1 /* include terminator */;
5036
5037 return VINF_SUCCESS;
5038}
5039
5040/* static */
5041DECLCALLBACK(int) Medium::vdConfigQuery(void *pvUser,
5042 const char *pszName,
5043 char *pszValue,
5044 size_t cchValue)
5045{
5046 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
5047
5048 Medium *that = static_cast<Medium*>(pvUser);
5049 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
5050
5051 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
5052 if (it == that->m->mapProperties.end())
5053 return VERR_CFGM_VALUE_NOT_FOUND;
5054
5055 /* we interpret null values as "no value" in Medium */
5056 if (it->second.isEmpty())
5057 return VERR_CFGM_VALUE_NOT_FOUND;
5058
5059 const Utf8Str &value = it->second;
5060 if (value.length() >= cchValue)
5061 return VERR_CFGM_NOT_ENOUGH_SPACE;
5062
5063 memcpy(pszValue, value.c_str(), value.length() + 1);
5064
5065 return VINF_SUCCESS;
5066}
5067
5068DECLCALLBACK(int) Medium::vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
5069{
5070 PVDSOCKETINT pSocketInt = NULL;
5071
5072 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
5073 return VERR_NOT_SUPPORTED;
5074
5075 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
5076 if (!pSocketInt)
5077 return VERR_NO_MEMORY;
5078
5079 pSocketInt->hSocket = NIL_RTSOCKET;
5080 *pSock = pSocketInt;
5081 return VINF_SUCCESS;
5082}
5083
5084DECLCALLBACK(int) Medium::vdTcpSocketDestroy(VDSOCKET Sock)
5085{
5086 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5087
5088 if (pSocketInt->hSocket != NIL_RTSOCKET)
5089 RTTcpClientClose(pSocketInt->hSocket);
5090
5091 RTMemFree(pSocketInt);
5092
5093 return VINF_SUCCESS;
5094}
5095
5096DECLCALLBACK(int) Medium::vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
5097{
5098 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5099
5100 return RTTcpClientConnect(pszAddress, uPort, &pSocketInt->hSocket);
5101}
5102
5103DECLCALLBACK(int) Medium::vdTcpClientClose(VDSOCKET Sock)
5104{
5105 int rc = VINF_SUCCESS;
5106 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5107
5108 rc = RTTcpClientClose(pSocketInt->hSocket);
5109 pSocketInt->hSocket = NIL_RTSOCKET;
5110 return rc;
5111}
5112
5113DECLCALLBACK(bool) Medium::vdTcpIsClientConnected(VDSOCKET Sock)
5114{
5115 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5116 return pSocketInt->hSocket != NIL_RTSOCKET;
5117}
5118
5119DECLCALLBACK(int) Medium::vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
5120{
5121 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5122 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
5123}
5124
5125DECLCALLBACK(int) Medium::vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
5126{
5127 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5128 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
5129}
5130
5131DECLCALLBACK(int) Medium::vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
5132{
5133 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5134 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
5135}
5136
5137DECLCALLBACK(int) Medium::vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
5138{
5139 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5140 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
5141}
5142
5143DECLCALLBACK(int) Medium::vdTcpFlush(VDSOCKET Sock)
5144{
5145 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5146 return RTTcpFlush(pSocketInt->hSocket);
5147}
5148
5149DECLCALLBACK(int) Medium::vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
5150{
5151 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5152 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
5153}
5154
5155DECLCALLBACK(int) Medium::vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
5156{
5157 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5158 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
5159}
5160
5161DECLCALLBACK(int) Medium::vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
5162{
5163 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5164 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
5165}
5166
5167
5168/**
5169 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
5170 *
5171 * @note When the task is executed by this method, IProgress::notifyComplete()
5172 * is automatically called for the progress object associated with this
5173 * task when the task is finished to signal the operation completion for
5174 * other threads asynchronously waiting for it.
5175 */
5176HRESULT Medium::startThread(Medium::Task *pTask)
5177{
5178#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
5179 /* Extreme paranoia: The calling thread should not hold the medium
5180 * tree lock or any medium lock. Since there is no separate lock class
5181 * for medium objects be even more strict: no other object locks. */
5182 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
5183 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
5184#endif
5185
5186 /// @todo use a more descriptive task name
5187 int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
5188 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
5189 "Medium::Task");
5190 if (RT_FAILURE(vrc))
5191 {
5192 delete pTask;
5193 return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n", vrc);
5194 }
5195
5196 return S_OK;
5197}
5198
5199/**
5200 * Fix the parent UUID of all children to point to this medium as their
5201 * parent.
5202 */
5203HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
5204{
5205 MediumLockList mediumLockList;
5206 HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
5207 false /* fMediumLockWrite */,
5208 this,
5209 mediumLockList);
5210 AssertComRCReturnRC(rc);
5211
5212 try
5213 {
5214 PVBOXHDD hdd;
5215 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5216 ComAssertRCThrow(vrc, E_FAIL);
5217
5218 try
5219 {
5220 MediumLockList::Base::iterator lockListBegin =
5221 mediumLockList.GetBegin();
5222 MediumLockList::Base::iterator lockListEnd =
5223 mediumLockList.GetEnd();
5224 for (MediumLockList::Base::iterator it = lockListBegin;
5225 it != lockListEnd;
5226 ++it)
5227 {
5228 MediumLock &mediumLock = *it;
5229 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5230 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5231
5232 // open the medium
5233 vrc = VDOpen(hdd,
5234 pMedium->m->strFormat.c_str(),
5235 pMedium->m->strLocationFull.c_str(),
5236 VD_OPEN_FLAGS_READONLY,
5237 pMedium->m->vdDiskIfaces);
5238 if (RT_FAILURE(vrc))
5239 throw vrc;
5240 }
5241
5242 for (MediaList::const_iterator it = childrenToReparent.begin();
5243 it != childrenToReparent.end();
5244 ++it)
5245 {
5246 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
5247 vrc = VDOpen(hdd,
5248 (*it)->m->strFormat.c_str(),
5249 (*it)->m->strLocationFull.c_str(),
5250 VD_OPEN_FLAGS_INFO,
5251 (*it)->m->vdDiskIfaces);
5252 if (RT_FAILURE(vrc))
5253 throw vrc;
5254
5255 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id);
5256 if (RT_FAILURE(vrc))
5257 throw vrc;
5258
5259 vrc = VDClose(hdd, false /* fDelete */);
5260 if (RT_FAILURE(vrc))
5261 throw vrc;
5262
5263 (*it)->UnlockWrite(NULL);
5264 }
5265 }
5266 catch (HRESULT aRC) { rc = aRC; }
5267 catch (int aVRC)
5268 {
5269 throw setError(E_FAIL,
5270 tr("Could not update medium UUID references to parent '%s' (%s)"),
5271 m->strLocationFull.c_str(),
5272 vdError(aVRC).c_str());
5273 }
5274
5275 VDDestroy(hdd);
5276 }
5277 catch (HRESULT aRC) { rc = aRC; }
5278
5279 return rc;
5280}
5281
5282/**
5283 * Runs Medium::Task::handler() on the current thread instead of creating
5284 * a new one.
5285 *
5286 * This call implies that it is made on another temporary thread created for
5287 * some asynchronous task. Avoid calling it from a normal thread since the task
5288 * operations are potentially lengthy and will block the calling thread in this
5289 * case.
5290 *
5291 * @note When the task is executed by this method, IProgress::notifyComplete()
5292 * is not called for the progress object associated with this task when
5293 * the task is finished. Instead, the result of the operation is returned
5294 * by this method directly and it's the caller's responsibility to
5295 * complete the progress object in this case.
5296 */
5297HRESULT Medium::runNow(Medium::Task *pTask,
5298 bool *pfNeedsSaveSettings)
5299{
5300#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
5301 /* Extreme paranoia: The calling thread should not hold the medium
5302 * tree lock or any medium lock. Since there is no separate lock class
5303 * for medium objects be even more strict: no other object locks. */
5304 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
5305 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
5306#endif
5307
5308 pTask->m_pfNeedsSaveSettings = pfNeedsSaveSettings;
5309
5310 /* NIL_RTTHREAD indicates synchronous call. */
5311 return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
5312}
5313
5314/**
5315 * Implementation code for the "create base" task.
5316 *
5317 * This only gets started from Medium::CreateBaseStorage() and always runs
5318 * asynchronously. As a result, we always save the VirtualBox.xml file when
5319 * we're done here.
5320 *
5321 * @param task
5322 * @return
5323 */
5324HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
5325{
5326 HRESULT rc = S_OK;
5327
5328 /* these parameters we need after creation */
5329 uint64_t size = 0, logicalSize = 0;
5330 MediumVariant_T variant = MediumVariant_Standard;
5331 bool fGenerateUuid = false;
5332
5333 try
5334 {
5335 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
5336
5337 /* The object may request a specific UUID (through a special form of
5338 * the setLocation() argument). Otherwise we have to generate it */
5339 Guid id = m->id;
5340 fGenerateUuid = id.isEmpty();
5341 if (fGenerateUuid)
5342 {
5343 id.create();
5344 /* VirtualBox::registerHardDisk() will need UUID */
5345 unconst(m->id) = id;
5346 }
5347
5348 Utf8Str format(m->strFormat);
5349 Utf8Str location(m->strLocationFull);
5350 uint64_t capabilities = m->formatObj->getCapabilities();
5351 ComAssertThrow(capabilities & ( VD_CAP_CREATE_FIXED
5352 | VD_CAP_CREATE_DYNAMIC), E_FAIL);
5353 Assert(m->state == MediumState_Creating);
5354
5355 PVBOXHDD hdd;
5356 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5357 ComAssertRCThrow(vrc, E_FAIL);
5358
5359 /* unlock before the potentially lengthy operation */
5360 thisLock.release();
5361
5362 try
5363 {
5364 /* ensure the directory exists */
5365 rc = VirtualBox::ensureFilePathExists(location);
5366 if (FAILED(rc))
5367 throw rc;
5368
5369 PDMMEDIAGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
5370
5371 vrc = VDCreateBase(hdd,
5372 format.c_str(),
5373 location.c_str(),
5374 task.mSize * _1M,
5375 task.mVariant,
5376 NULL,
5377 &geo,
5378 &geo,
5379 id.raw(),
5380 VD_OPEN_FLAGS_NORMAL,
5381 NULL,
5382 task.mVDOperationIfaces);
5383 if (RT_FAILURE(vrc))
5384 throw setError(VBOX_E_FILE_ERROR,
5385 tr("Could not create the medium storage unit '%s'%s"),
5386 location.c_str(), vdError(vrc).c_str());
5387
5388 size = VDGetFileSize(hdd, 0);
5389 logicalSize = VDGetSize(hdd, 0) / _1M;
5390 unsigned uImageFlags;
5391 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5392 if (RT_SUCCESS(vrc))
5393 variant = (MediumVariant_T)uImageFlags;
5394 }
5395 catch (HRESULT aRC) { rc = aRC; }
5396
5397 VDDestroy(hdd);
5398 }
5399 catch (HRESULT aRC) { rc = aRC; }
5400
5401 if (SUCCEEDED(rc))
5402 {
5403 /* register with mVirtualBox as the last step and move to
5404 * Created state only on success (leaving an orphan file is
5405 * better than breaking media registry consistency) */
5406 bool fNeedsSaveSettings = false;
5407 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5408 rc = m->pVirtualBox->registerHardDisk(this, &fNeedsSaveSettings);
5409 treeLock.release();
5410
5411 if (fNeedsSaveSettings)
5412 {
5413 AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
5414 m->pVirtualBox->saveSettings();
5415 }
5416 }
5417
5418 // reenter the lock before changing state
5419 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
5420
5421 if (SUCCEEDED(rc))
5422 {
5423 m->state = MediumState_Created;
5424
5425 m->size = size;
5426 m->logicalSize = logicalSize;
5427 m->variant = variant;
5428 }
5429 else
5430 {
5431 /* back to NotCreated on failure */
5432 m->state = MediumState_NotCreated;
5433
5434 /* reset UUID to prevent it from being reused next time */
5435 if (fGenerateUuid)
5436 unconst(m->id).clear();
5437 }
5438
5439 return rc;
5440}
5441
5442/**
5443 * Implementation code for the "create diff" task.
5444 *
5445 * This task always gets started from Medium::createDiffStorage() and can run
5446 * synchronously or asynchronously depending on the "wait" parameter passed to
5447 * that function. If we run synchronously, the caller expects the bool
5448 * *pfNeedsSaveSettings to be set before returning; otherwise (in asynchronous
5449 * mode), we save the settings ourselves.
5450 *
5451 * @param task
5452 * @return
5453 */
5454HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
5455{
5456 HRESULT rc = S_OK;
5457
5458 bool fNeedsSaveSettings = false;
5459
5460 const ComObjPtr<Medium> &pTarget = task.mTarget;
5461
5462 uint64_t size = 0, logicalSize = 0;
5463 MediumVariant_T variant = MediumVariant_Standard;
5464 bool fGenerateUuid = false;
5465
5466 try
5467 {
5468 /* Lock both in {parent,child} order. */
5469 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
5470
5471 /* The object may request a specific UUID (through a special form of
5472 * the setLocation() argument). Otherwise we have to generate it */
5473 Guid targetId = pTarget->m->id;
5474 fGenerateUuid = targetId.isEmpty();
5475 if (fGenerateUuid)
5476 {
5477 targetId.create();
5478 /* VirtualBox::registerHardDisk() will need UUID */
5479 unconst(pTarget->m->id) = targetId;
5480 }
5481
5482 Guid id = m->id;
5483
5484 Utf8Str targetFormat(pTarget->m->strFormat);
5485 Utf8Str targetLocation(pTarget->m->strLocationFull);
5486 uint64_t capabilities = m->formatObj->getCapabilities();
5487 ComAssertThrow(capabilities & VD_CAP_CREATE_DYNAMIC, E_FAIL);
5488
5489 Assert(pTarget->m->state == MediumState_Creating);
5490 Assert(m->state == MediumState_LockedRead);
5491
5492 PVBOXHDD hdd;
5493 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5494 ComAssertRCThrow(vrc, E_FAIL);
5495
5496 /* the two media are now protected by their non-default states;
5497 * unlock the media before the potentially lengthy operation */
5498 mediaLock.release();
5499
5500 try
5501 {
5502 /* Open all media in the target chain but the last. */
5503 MediumLockList::Base::const_iterator targetListBegin =
5504 task.mpMediumLockList->GetBegin();
5505 MediumLockList::Base::const_iterator targetListEnd =
5506 task.mpMediumLockList->GetEnd();
5507 for (MediumLockList::Base::const_iterator it = targetListBegin;
5508 it != targetListEnd;
5509 ++it)
5510 {
5511 const MediumLock &mediumLock = *it;
5512 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5513
5514 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5515
5516 /* Skip over the target diff medium */
5517 if (pMedium->m->state == MediumState_Creating)
5518 continue;
5519
5520 /* sanity check */
5521 Assert(pMedium->m->state == MediumState_LockedRead);
5522
5523 /* Open all media in appropriate mode. */
5524 vrc = VDOpen(hdd,
5525 pMedium->m->strFormat.c_str(),
5526 pMedium->m->strLocationFull.c_str(),
5527 VD_OPEN_FLAGS_READONLY,
5528 pMedium->m->vdDiskIfaces);
5529 if (RT_FAILURE(vrc))
5530 throw setError(VBOX_E_FILE_ERROR,
5531 tr("Could not open the medium storage unit '%s'%s"),
5532 pMedium->m->strLocationFull.c_str(),
5533 vdError(vrc).c_str());
5534 }
5535
5536 /* ensure the target directory exists */
5537 rc = VirtualBox::ensureFilePathExists(targetLocation);
5538 if (FAILED(rc))
5539 throw rc;
5540
5541 vrc = VDCreateDiff(hdd,
5542 targetFormat.c_str(),
5543 targetLocation.c_str(),
5544 task.mVariant | VD_IMAGE_FLAGS_DIFF,
5545 NULL,
5546 targetId.raw(),
5547 id.raw(),
5548 VD_OPEN_FLAGS_NORMAL,
5549 pTarget->m->vdDiskIfaces,
5550 task.mVDOperationIfaces);
5551 if (RT_FAILURE(vrc))
5552 throw setError(VBOX_E_FILE_ERROR,
5553 tr("Could not create the differencing medium storage unit '%s'%s"),
5554 targetLocation.c_str(), vdError(vrc).c_str());
5555
5556 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
5557 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE) / _1M;
5558 unsigned uImageFlags;
5559 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5560 if (RT_SUCCESS(vrc))
5561 variant = (MediumVariant_T)uImageFlags;
5562 }
5563 catch (HRESULT aRC) { rc = aRC; }
5564
5565 VDDestroy(hdd);
5566 }
5567 catch (HRESULT aRC) { rc = aRC; }
5568
5569 if (SUCCEEDED(rc))
5570 {
5571 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5572
5573 Assert(pTarget->m->pParent.isNull());
5574
5575 /* associate the child with the parent */
5576 pTarget->m->pParent = this;
5577 m->llChildren.push_back(pTarget);
5578
5579 /** @todo r=klaus neither target nor base() are locked,
5580 * potential race! */
5581 /* diffs for immutable media are auto-reset by default */
5582 pTarget->m->autoReset = (getBase()->m->type == MediumType_Immutable);
5583
5584 /* register with mVirtualBox as the last step and move to
5585 * Created state only on success (leaving an orphan file is
5586 * better than breaking media registry consistency) */
5587 rc = m->pVirtualBox->registerHardDisk(pTarget, &fNeedsSaveSettings);
5588
5589 if (FAILED(rc))
5590 /* break the parent association on failure to register */
5591 deparent();
5592 }
5593
5594 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
5595
5596 if (SUCCEEDED(rc))
5597 {
5598 pTarget->m->state = MediumState_Created;
5599
5600 pTarget->m->size = size;
5601 pTarget->m->logicalSize = logicalSize;
5602 pTarget->m->variant = variant;
5603 }
5604 else
5605 {
5606 /* back to NotCreated on failure */
5607 pTarget->m->state = MediumState_NotCreated;
5608
5609 pTarget->m->autoReset = false;
5610
5611 /* reset UUID to prevent it from being reused next time */
5612 if (fGenerateUuid)
5613 unconst(pTarget->m->id).clear();
5614 }
5615
5616 // deregister the task registered in createDiffStorage()
5617 Assert(m->numCreateDiffTasks != 0);
5618 --m->numCreateDiffTasks;
5619
5620 if (task.isAsync())
5621 {
5622 if (fNeedsSaveSettings)
5623 {
5624 // save the global settings; for that we should hold only the VirtualBox lock
5625 mediaLock.release();
5626 AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
5627 m->pVirtualBox->saveSettings();
5628 }
5629 }
5630 else
5631 // synchronous mode: report save settings result to caller
5632 if (task.m_pfNeedsSaveSettings)
5633 *task.m_pfNeedsSaveSettings = fNeedsSaveSettings;
5634
5635 /* Note that in sync mode, it's the caller's responsibility to
5636 * unlock the medium. */
5637
5638 return rc;
5639}
5640
5641/**
5642 * Implementation code for the "merge" task.
5643 *
5644 * This task always gets started from Medium::mergeTo() and can run
5645 * synchronously or asynchrously depending on the "wait" parameter passed to
5646 * that function. If we run synchronously, the caller expects the bool
5647 * *pfNeedsSaveSettings to be set before returning; otherwise (in asynchronous
5648 * mode), we save the settings ourselves.
5649 *
5650 * @param task
5651 * @return
5652 */
5653HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
5654{
5655 HRESULT rc = S_OK;
5656
5657 const ComObjPtr<Medium> &pTarget = task.mTarget;
5658
5659 try
5660 {
5661 PVBOXHDD hdd;
5662 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5663 ComAssertRCThrow(vrc, E_FAIL);
5664
5665 try
5666 {
5667 // Similar code appears in SessionMachine::onlineMergeMedium, so
5668 // if you make any changes below check whether they are applicable
5669 // in that context as well.
5670
5671 unsigned uTargetIdx = VD_LAST_IMAGE;
5672 unsigned uSourceIdx = VD_LAST_IMAGE;
5673 /* Open all media in the chain. */
5674 MediumLockList::Base::iterator lockListBegin =
5675 task.mpMediumLockList->GetBegin();
5676 MediumLockList::Base::iterator lockListEnd =
5677 task.mpMediumLockList->GetEnd();
5678 unsigned i = 0;
5679 for (MediumLockList::Base::iterator it = lockListBegin;
5680 it != lockListEnd;
5681 ++it)
5682 {
5683 MediumLock &mediumLock = *it;
5684 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5685
5686 if (pMedium == this)
5687 uSourceIdx = i;
5688 else if (pMedium == pTarget)
5689 uTargetIdx = i;
5690
5691 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5692
5693 /*
5694 * complex sanity (sane complexity)
5695 *
5696 * The current medium must be in the Deleting (medium is merged)
5697 * or LockedRead (parent medium) state if it is not the target.
5698 * If it is the target it must be in the LockedWrite state.
5699 */
5700 Assert( ( pMedium != pTarget
5701 && ( pMedium->m->state == MediumState_Deleting
5702 || pMedium->m->state == MediumState_LockedRead))
5703 || ( pMedium == pTarget
5704 && pMedium->m->state == MediumState_LockedWrite));
5705
5706 /*
5707 * Medium must be the target, in the LockedRead state
5708 * or Deleting state where it is not allowed to be attached
5709 * to a virtual machine.
5710 */
5711 Assert( pMedium == pTarget
5712 || pMedium->m->state == MediumState_LockedRead
5713 || ( pMedium->m->backRefs.size() == 0
5714 && pMedium->m->state == MediumState_Deleting));
5715 /* The source medium must be in Deleting state. */
5716 Assert( pMedium != this
5717 || pMedium->m->state == MediumState_Deleting);
5718
5719 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
5720
5721 if ( pMedium->m->state == MediumState_LockedRead
5722 || pMedium->m->state == MediumState_Deleting)
5723 uOpenFlags = VD_OPEN_FLAGS_READONLY;
5724 if (pMedium->m->type == MediumType_Shareable)
5725 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
5726
5727 /* Open the medium */
5728 vrc = VDOpen(hdd,
5729 pMedium->m->strFormat.c_str(),
5730 pMedium->m->strLocationFull.c_str(),
5731 uOpenFlags,
5732 pMedium->m->vdDiskIfaces);
5733 if (RT_FAILURE(vrc))
5734 throw vrc;
5735
5736 i++;
5737 }
5738
5739 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
5740 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
5741
5742 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
5743 task.mVDOperationIfaces);
5744 if (RT_FAILURE(vrc))
5745 throw vrc;
5746
5747 /* update parent UUIDs */
5748 if (!task.mfMergeForward)
5749 {
5750 /* we need to update UUIDs of all source's children
5751 * which cannot be part of the container at once so
5752 * add each one in there individually */
5753 if (task.mChildrenToReparent.size() > 0)
5754 {
5755 for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
5756 it != task.mChildrenToReparent.end();
5757 ++it)
5758 {
5759 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
5760 vrc = VDOpen(hdd,
5761 (*it)->m->strFormat.c_str(),
5762 (*it)->m->strLocationFull.c_str(),
5763 VD_OPEN_FLAGS_INFO,
5764 (*it)->m->vdDiskIfaces);
5765 if (RT_FAILURE(vrc))
5766 throw vrc;
5767
5768 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
5769 pTarget->m->id);
5770 if (RT_FAILURE(vrc))
5771 throw vrc;
5772
5773 vrc = VDClose(hdd, false /* fDelete */);
5774 if (RT_FAILURE(vrc))
5775 throw vrc;
5776
5777 (*it)->UnlockWrite(NULL);
5778 }
5779 }
5780 }
5781 }
5782 catch (HRESULT aRC) { rc = aRC; }
5783 catch (int aVRC)
5784 {
5785 throw setError(VBOX_E_FILE_ERROR,
5786 tr("Could not merge the medium '%s' to '%s'%s"),
5787 m->strLocationFull.c_str(),
5788 pTarget->m->strLocationFull.c_str(),
5789 vdError(aVRC).c_str());
5790 }
5791
5792 VDDestroy(hdd);
5793 }
5794 catch (HRESULT aRC) { rc = aRC; }
5795
5796 HRESULT rc2;
5797
5798 if (SUCCEEDED(rc))
5799 {
5800 /* all media but the target were successfully deleted by
5801 * VDMerge; reparent the last one and uninitialize deleted media. */
5802
5803 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5804
5805 if (task.mfMergeForward)
5806 {
5807 /* first, unregister the target since it may become a base
5808 * medium which needs re-registration */
5809 rc2 = m->pVirtualBox->unregisterHardDisk(pTarget, NULL /*&fNeedsSaveSettings*/);
5810 AssertComRC(rc2);
5811
5812 /* then, reparent it and disconnect the deleted branch at
5813 * both ends (chain->parent() is source's parent) */
5814 pTarget->deparent();
5815 pTarget->m->pParent = task.mParentForTarget;
5816 if (pTarget->m->pParent)
5817 {
5818 pTarget->m->pParent->m->llChildren.push_back(pTarget);
5819 deparent();
5820 }
5821
5822 /* then, register again */
5823 rc2 = m->pVirtualBox->registerHardDisk(pTarget, NULL /*&fNeedsSaveSettings*/);
5824 AssertComRC(rc2);
5825 }
5826 else
5827 {
5828 Assert(pTarget->getChildren().size() == 1);
5829 Medium *targetChild = pTarget->getChildren().front();
5830
5831 /* disconnect the deleted branch at the elder end */
5832 targetChild->deparent();
5833
5834 /* reparent source's children and disconnect the deleted
5835 * branch at the younger end */
5836 if (task.mChildrenToReparent.size() > 0)
5837 {
5838 /* obey {parent,child} lock order */
5839 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
5840
5841 for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
5842 it != task.mChildrenToReparent.end();
5843 it++)
5844 {
5845 Medium *pMedium = *it;
5846 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
5847
5848 pMedium->deparent(); // removes pMedium from source
5849 pMedium->setParent(pTarget);
5850 }
5851 }
5852 }
5853
5854 /* unregister and uninitialize all media removed by the merge */
5855 MediumLockList::Base::iterator lockListBegin =
5856 task.mpMediumLockList->GetBegin();
5857 MediumLockList::Base::iterator lockListEnd =
5858 task.mpMediumLockList->GetEnd();
5859 for (MediumLockList::Base::iterator it = lockListBegin;
5860 it != lockListEnd;
5861 )
5862 {
5863 MediumLock &mediumLock = *it;
5864 /* Create a real copy of the medium pointer, as the medium
5865 * lock deletion below would invalidate the referenced object. */
5866 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
5867
5868 /* The target and all media not merged (readonly) are skipped */
5869 if ( pMedium == pTarget
5870 || pMedium->m->state == MediumState_LockedRead)
5871 {
5872 ++it;
5873 continue;
5874 }
5875
5876 rc2 = pMedium->m->pVirtualBox->unregisterHardDisk(pMedium,
5877 NULL /*pfNeedsSaveSettings*/);
5878 AssertComRC(rc2);
5879
5880 /* now, uninitialize the deleted medium (note that
5881 * due to the Deleting state, uninit() will not touch
5882 * the parent-child relationship so we need to
5883 * uninitialize each disk individually) */
5884
5885 /* note that the operation initiator medium (which is
5886 * normally also the source medium) is a special case
5887 * -- there is one more caller added by Task to it which
5888 * we must release. Also, if we are in sync mode, the
5889 * caller may still hold an AutoCaller instance for it
5890 * and therefore we cannot uninit() it (it's therefore
5891 * the caller's responsibility) */
5892 if (pMedium == this)
5893 {
5894 Assert(getChildren().size() == 0);
5895 Assert(m->backRefs.size() == 0);
5896 task.mMediumCaller.release();
5897 }
5898
5899 /* Delete the medium lock list entry, which also releases the
5900 * caller added by MergeChain before uninit() and updates the
5901 * iterator to point to the right place. */
5902 rc2 = task.mpMediumLockList->RemoveByIterator(it);
5903 AssertComRC(rc2);
5904
5905 if (task.isAsync() || pMedium != this)
5906 pMedium->uninit();
5907 }
5908 }
5909
5910 if (task.isAsync())
5911 {
5912 // in asynchronous mode, save settings now
5913 // for that we should hold only the VirtualBox lock
5914 AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
5915 m->pVirtualBox->saveSettings();
5916 }
5917 else
5918 // synchronous mode: report save settings result to caller
5919 if (task.m_pfNeedsSaveSettings)
5920 *task.m_pfNeedsSaveSettings = true;
5921
5922 if (FAILED(rc))
5923 {
5924 /* Here we come if either VDMerge() failed (in which case we
5925 * assume that it tried to do everything to make a further
5926 * retry possible -- e.g. not deleted intermediate media
5927 * and so on) or VirtualBox::saveSettings() failed (where we
5928 * should have the original tree but with intermediate storage
5929 * units deleted by VDMerge()). We have to only restore states
5930 * (through the MergeChain dtor) unless we are run synchronously
5931 * in which case it's the responsibility of the caller as stated
5932 * in the mergeTo() docs. The latter also implies that we
5933 * don't own the merge chain, so release it in this case. */
5934 if (task.isAsync())
5935 {
5936 Assert(task.mChildrenToReparent.size() == 0);
5937 cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
5938 }
5939 }
5940
5941 return rc;
5942}
5943
5944/**
5945 * Implementation code for the "clone" task.
5946 *
5947 * This only gets started from Medium::CloneTo() and always runs asynchronously.
5948 * As a result, we always save the VirtualBox.xml file when we're done here.
5949 *
5950 * @param task
5951 * @return
5952 */
5953HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
5954{
5955 HRESULT rc = S_OK;
5956
5957 const ComObjPtr<Medium> &pTarget = task.mTarget;
5958 const ComObjPtr<Medium> &pParent = task.mParent;
5959
5960 bool fCreatingTarget = false;
5961
5962 uint64_t size = 0, logicalSize = 0;
5963 MediumVariant_T variant = MediumVariant_Standard;
5964 bool fGenerateUuid = false;
5965
5966 try
5967 {
5968 /* Lock all in {parent,child} order. The lock is also used as a
5969 * signal from the task initiator (which releases it only after
5970 * RTThreadCreate()) that we can start the job. */
5971 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
5972
5973 fCreatingTarget = pTarget->m->state == MediumState_Creating;
5974
5975 /* The object may request a specific UUID (through a special form of
5976 * the setLocation() argument). Otherwise we have to generate it */
5977 Guid targetId = pTarget->m->id;
5978 fGenerateUuid = targetId.isEmpty();
5979 if (fGenerateUuid)
5980 {
5981 targetId.create();
5982 /* VirtualBox::registerHardDisk() will need UUID */
5983 unconst(pTarget->m->id) = targetId;
5984 }
5985
5986 PVBOXHDD hdd;
5987 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
5988 ComAssertRCThrow(vrc, E_FAIL);
5989
5990 try
5991 {
5992 /* Open all media in the source chain. */
5993 MediumLockList::Base::const_iterator sourceListBegin =
5994 task.mpSourceMediumLockList->GetBegin();
5995 MediumLockList::Base::const_iterator sourceListEnd =
5996 task.mpSourceMediumLockList->GetEnd();
5997 for (MediumLockList::Base::const_iterator it = sourceListBegin;
5998 it != sourceListEnd;
5999 ++it)
6000 {
6001 const MediumLock &mediumLock = *it;
6002 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6003 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6004
6005 /* sanity check */
6006 Assert(pMedium->m->state == MediumState_LockedRead);
6007
6008 /** Open all media in read-only mode. */
6009 vrc = VDOpen(hdd,
6010 pMedium->m->strFormat.c_str(),
6011 pMedium->m->strLocationFull.c_str(),
6012 VD_OPEN_FLAGS_READONLY,
6013 pMedium->m->vdDiskIfaces);
6014 if (RT_FAILURE(vrc))
6015 throw setError(VBOX_E_FILE_ERROR,
6016 tr("Could not open the medium storage unit '%s'%s"),
6017 pMedium->m->strLocationFull.c_str(),
6018 vdError(vrc).c_str());
6019 }
6020
6021 Utf8Str targetFormat(pTarget->m->strFormat);
6022 Utf8Str targetLocation(pTarget->m->strLocationFull);
6023
6024 Assert( pTarget->m->state == MediumState_Creating
6025 || pTarget->m->state == MediumState_LockedWrite);
6026 Assert(m->state == MediumState_LockedRead);
6027 Assert(pParent.isNull() || pParent->m->state == MediumState_LockedRead);
6028
6029 /* unlock before the potentially lengthy operation */
6030 thisLock.release();
6031
6032 /* ensure the target directory exists */
6033 rc = VirtualBox::ensureFilePathExists(targetLocation);
6034 if (FAILED(rc))
6035 throw rc;
6036
6037 PVBOXHDD targetHdd;
6038 vrc = VDCreate(m->vdDiskIfaces, &targetHdd);
6039 ComAssertRCThrow(vrc, E_FAIL);
6040
6041 try
6042 {
6043 /* Open all media in the target chain. */
6044 MediumLockList::Base::const_iterator targetListBegin =
6045 task.mpTargetMediumLockList->GetBegin();
6046 MediumLockList::Base::const_iterator targetListEnd =
6047 task.mpTargetMediumLockList->GetEnd();
6048 for (MediumLockList::Base::const_iterator it = targetListBegin;
6049 it != targetListEnd;
6050 ++it)
6051 {
6052 const MediumLock &mediumLock = *it;
6053 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6054
6055 /* If the target medium is not created yet there's no
6056 * reason to open it. */
6057 if (pMedium == pTarget && fCreatingTarget)
6058 continue;
6059
6060 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6061
6062 /* sanity check */
6063 Assert( pMedium->m->state == MediumState_LockedRead
6064 || pMedium->m->state == MediumState_LockedWrite);
6065
6066 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
6067 if (pMedium->m->state != MediumState_LockedWrite)
6068 uOpenFlags = VD_OPEN_FLAGS_READONLY;
6069 if (pMedium->m->type == MediumType_Shareable)
6070 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6071
6072 /* Open all media in appropriate mode. */
6073 vrc = VDOpen(targetHdd,
6074 pMedium->m->strFormat.c_str(),
6075 pMedium->m->strLocationFull.c_str(),
6076 uOpenFlags,
6077 pMedium->m->vdDiskIfaces);
6078 if (RT_FAILURE(vrc))
6079 throw setError(VBOX_E_FILE_ERROR,
6080 tr("Could not open the medium storage unit '%s'%s"),
6081 pMedium->m->strLocationFull.c_str(),
6082 vdError(vrc).c_str());
6083 }
6084
6085 /** @todo r=klaus target isn't locked, race getting the state */
6086 vrc = VDCopy(hdd,
6087 VD_LAST_IMAGE,
6088 targetHdd,
6089 targetFormat.c_str(),
6090 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
6091 false,
6092 0,
6093 task.mVariant,
6094 targetId.raw(),
6095 NULL,
6096 pTarget->m->vdDiskIfaces,
6097 task.mVDOperationIfaces);
6098 if (RT_FAILURE(vrc))
6099 throw setError(VBOX_E_FILE_ERROR,
6100 tr("Could not create the clone medium '%s'%s"),
6101 targetLocation.c_str(), vdError(vrc).c_str());
6102
6103 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
6104 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE) / _1M;
6105 unsigned uImageFlags;
6106 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
6107 if (RT_SUCCESS(vrc))
6108 variant = (MediumVariant_T)uImageFlags;
6109 }
6110 catch (HRESULT aRC) { rc = aRC; }
6111
6112 VDDestroy(targetHdd);
6113 }
6114 catch (HRESULT aRC) { rc = aRC; }
6115
6116 VDDestroy(hdd);
6117 }
6118 catch (HRESULT aRC) { rc = aRC; }
6119
6120 /* Only do the parent changes for newly created media. */
6121 if (SUCCEEDED(rc) && fCreatingTarget)
6122 {
6123 /* we set mParent & children() */
6124 AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6125
6126 Assert(pTarget->m->pParent.isNull());
6127
6128 if (pParent)
6129 {
6130 /* associate the clone with the parent and deassociate
6131 * from VirtualBox */
6132 pTarget->m->pParent = pParent;
6133 pParent->m->llChildren.push_back(pTarget);
6134
6135 /* register with mVirtualBox as the last step and move to
6136 * Created state only on success (leaving an orphan file is
6137 * better than breaking media registry consistency) */
6138 rc = pParent->m->pVirtualBox->registerHardDisk(pTarget, NULL /* pfNeedsSaveSettings */);
6139
6140 if (FAILED(rc))
6141 /* break parent association on failure to register */
6142 pTarget->deparent(); // removes target from parent
6143 }
6144 else
6145 {
6146 /* just register */
6147 rc = m->pVirtualBox->registerHardDisk(pTarget, NULL /* pfNeedsSaveSettings */);
6148 }
6149 }
6150
6151 if (fCreatingTarget)
6152 {
6153 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
6154
6155 if (SUCCEEDED(rc))
6156 {
6157 pTarget->m->state = MediumState_Created;
6158
6159 pTarget->m->size = size;
6160 pTarget->m->logicalSize = logicalSize;
6161 pTarget->m->variant = variant;
6162 }
6163 else
6164 {
6165 /* back to NotCreated on failure */
6166 pTarget->m->state = MediumState_NotCreated;
6167
6168 /* reset UUID to prevent it from being reused next time */
6169 if (fGenerateUuid)
6170 unconst(pTarget->m->id).clear();
6171 }
6172 }
6173
6174 // now, at the end of this task (always asynchronous), save the settings
6175 {
6176 AutoWriteLock vboxlock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
6177 m->pVirtualBox->saveSettings();
6178 }
6179
6180 /* Everything is explicitly unlocked when the task exits,
6181 * as the task destruction also destroys the source chain. */
6182
6183 /* Make sure the source chain is released early. It could happen
6184 * that we get a deadlock in Appliance::Import when Medium::Close
6185 * is called & the source chain is released at the same time. */
6186 task.mpSourceMediumLockList->Clear();
6187
6188 return rc;
6189}
6190
6191/**
6192 * Implementation code for the "delete" task.
6193 *
6194 * This task always gets started from Medium::deleteStorage() and can run
6195 * synchronously or asynchrously depending on the "wait" parameter passed to
6196 * that function.
6197 *
6198 * @param task
6199 * @return
6200 */
6201HRESULT Medium::taskDeleteHandler(Medium::DeleteTask &task)
6202{
6203 NOREF(task);
6204 HRESULT rc = S_OK;
6205
6206 try
6207 {
6208 /* The lock is also used as a signal from the task initiator (which
6209 * releases it only after RTThreadCreate()) that we can start the job */
6210 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6211
6212 PVBOXHDD hdd;
6213 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6214 ComAssertRCThrow(vrc, E_FAIL);
6215
6216 Utf8Str format(m->strFormat);
6217 Utf8Str location(m->strLocationFull);
6218
6219 /* unlock before the potentially lengthy operation */
6220 Assert(m->state == MediumState_Deleting);
6221 thisLock.release();
6222
6223 try
6224 {
6225 vrc = VDOpen(hdd,
6226 format.c_str(),
6227 location.c_str(),
6228 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
6229 m->vdDiskIfaces);
6230 if (RT_SUCCESS(vrc))
6231 vrc = VDClose(hdd, true /* fDelete */);
6232
6233 if (RT_FAILURE(vrc))
6234 throw setError(VBOX_E_FILE_ERROR,
6235 tr("Could not delete the medium storage unit '%s'%s"),
6236 location.c_str(), vdError(vrc).c_str());
6237
6238 }
6239 catch (HRESULT aRC) { rc = aRC; }
6240
6241 VDDestroy(hdd);
6242 }
6243 catch (HRESULT aRC) { rc = aRC; }
6244
6245 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6246
6247 /* go to the NotCreated state even on failure since the storage
6248 * may have been already partially deleted and cannot be used any
6249 * more. One will be able to manually re-open the storage if really
6250 * needed to re-register it. */
6251 m->state = MediumState_NotCreated;
6252
6253 /* Reset UUID to prevent Create* from reusing it again */
6254 unconst(m->id).clear();
6255
6256 return rc;
6257}
6258
6259/**
6260 * Implementation code for the "reset" task.
6261 *
6262 * This always gets started asynchronously from Medium::Reset().
6263 *
6264 * @param task
6265 * @return
6266 */
6267HRESULT Medium::taskResetHandler(Medium::ResetTask &task)
6268{
6269 HRESULT rc = S_OK;
6270
6271 uint64_t size = 0, logicalSize = 0;
6272 MediumVariant_T variant = MediumVariant_Standard;
6273
6274 try
6275 {
6276 /* The lock is also used as a signal from the task initiator (which
6277 * releases it only after RTThreadCreate()) that we can start the job */
6278 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6279
6280 /// @todo Below we use a pair of delete/create operations to reset
6281 /// the diff contents but the most efficient way will of course be
6282 /// to add a VDResetDiff() API call
6283
6284 PVBOXHDD hdd;
6285 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6286 ComAssertRCThrow(vrc, E_FAIL);
6287
6288 Guid id = m->id;
6289 Utf8Str format(m->strFormat);
6290 Utf8Str location(m->strLocationFull);
6291
6292 Medium *pParent = m->pParent;
6293 Guid parentId = pParent->m->id;
6294 Utf8Str parentFormat(pParent->m->strFormat);
6295 Utf8Str parentLocation(pParent->m->strLocationFull);
6296
6297 Assert(m->state == MediumState_LockedWrite);
6298
6299 /* unlock before the potentially lengthy operation */
6300 thisLock.release();
6301
6302 try
6303 {
6304 /* Open all media in the target chain but the last. */
6305 MediumLockList::Base::const_iterator targetListBegin =
6306 task.mpMediumLockList->GetBegin();
6307 MediumLockList::Base::const_iterator targetListEnd =
6308 task.mpMediumLockList->GetEnd();
6309 for (MediumLockList::Base::const_iterator it = targetListBegin;
6310 it != targetListEnd;
6311 ++it)
6312 {
6313 const MediumLock &mediumLock = *it;
6314 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6315
6316 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6317
6318 /* sanity check, "this" is checked above */
6319 Assert( pMedium == this
6320 || pMedium->m->state == MediumState_LockedRead);
6321
6322 /* Open all media in appropriate mode. */
6323 vrc = VDOpen(hdd,
6324 pMedium->m->strFormat.c_str(),
6325 pMedium->m->strLocationFull.c_str(),
6326 VD_OPEN_FLAGS_READONLY,
6327 pMedium->m->vdDiskIfaces);
6328 if (RT_FAILURE(vrc))
6329 throw setError(VBOX_E_FILE_ERROR,
6330 tr("Could not open the medium storage unit '%s'%s"),
6331 pMedium->m->strLocationFull.c_str(),
6332 vdError(vrc).c_str());
6333
6334 /* Done when we hit the media which should be reset */
6335 if (pMedium == this)
6336 break;
6337 }
6338
6339 /* first, delete the storage unit */
6340 vrc = VDClose(hdd, true /* fDelete */);
6341 if (RT_FAILURE(vrc))
6342 throw setError(VBOX_E_FILE_ERROR,
6343 tr("Could not delete the medium storage unit '%s'%s"),
6344 location.c_str(), vdError(vrc).c_str());
6345
6346 /* next, create it again */
6347 vrc = VDOpen(hdd,
6348 parentFormat.c_str(),
6349 parentLocation.c_str(),
6350 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
6351 m->vdDiskIfaces);
6352 if (RT_FAILURE(vrc))
6353 throw setError(VBOX_E_FILE_ERROR,
6354 tr("Could not open the medium storage unit '%s'%s"),
6355 parentLocation.c_str(), vdError(vrc).c_str());
6356
6357 vrc = VDCreateDiff(hdd,
6358 format.c_str(),
6359 location.c_str(),
6360 /// @todo use the same medium variant as before
6361 VD_IMAGE_FLAGS_NONE,
6362 NULL,
6363 id.raw(),
6364 parentId.raw(),
6365 VD_OPEN_FLAGS_NORMAL,
6366 m->vdDiskIfaces,
6367 task.mVDOperationIfaces);
6368 if (RT_FAILURE(vrc))
6369 throw setError(VBOX_E_FILE_ERROR,
6370 tr("Could not create the differencing medium storage unit '%s'%s"),
6371 location.c_str(), vdError(vrc).c_str());
6372
6373 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
6374 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE) / _1M;
6375 unsigned uImageFlags;
6376 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6377 if (RT_SUCCESS(vrc))
6378 variant = (MediumVariant_T)uImageFlags;
6379 }
6380 catch (HRESULT aRC) { rc = aRC; }
6381
6382 VDDestroy(hdd);
6383 }
6384 catch (HRESULT aRC) { rc = aRC; }
6385
6386 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6387
6388 m->size = size;
6389 m->logicalSize = logicalSize;
6390 m->variant = variant;
6391
6392 if (task.isAsync())
6393 {
6394 /* unlock ourselves when done */
6395 HRESULT rc2 = UnlockWrite(NULL);
6396 AssertComRC(rc2);
6397 }
6398
6399 /* Note that in sync mode, it's the caller's responsibility to
6400 * unlock the medium. */
6401
6402 return rc;
6403}
6404
6405/**
6406 * Implementation code for the "compact" task.
6407 *
6408 * @param task
6409 * @return
6410 */
6411HRESULT Medium::taskCompactHandler(Medium::CompactTask &task)
6412{
6413 HRESULT rc = S_OK;
6414
6415 /* Lock all in {parent,child} order. The lock is also used as a
6416 * signal from the task initiator (which releases it only after
6417 * RTThreadCreate()) that we can start the job. */
6418 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6419
6420 try
6421 {
6422 PVBOXHDD hdd;
6423 int vrc = VDCreate(m->vdDiskIfaces, &hdd);
6424 ComAssertRCThrow(vrc, E_FAIL);
6425
6426 try
6427 {
6428 /* Open all media in the chain. */
6429 MediumLockList::Base::const_iterator mediumListBegin =
6430 task.mpMediumLockList->GetBegin();
6431 MediumLockList::Base::const_iterator mediumListEnd =
6432 task.mpMediumLockList->GetEnd();
6433 MediumLockList::Base::const_iterator mediumListLast =
6434 mediumListEnd;
6435 mediumListLast--;
6436 for (MediumLockList::Base::const_iterator it = mediumListBegin;
6437 it != mediumListEnd;
6438 ++it)
6439 {
6440 const MediumLock &mediumLock = *it;
6441 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6442 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6443
6444 /* sanity check */
6445 if (it == mediumListLast)
6446 Assert(pMedium->m->state == MediumState_LockedWrite);
6447 else
6448 Assert(pMedium->m->state == MediumState_LockedRead);
6449
6450 /* Open all media but last in read-only mode. Do not handle
6451 * shareable media, as compaction and sharing are mutually
6452 * exclusive. */
6453 vrc = VDOpen(hdd,
6454 pMedium->m->strFormat.c_str(),
6455 pMedium->m->strLocationFull.c_str(),
6456 (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
6457 pMedium->m->vdDiskIfaces);
6458 if (RT_FAILURE(vrc))
6459 throw setError(VBOX_E_FILE_ERROR,
6460 tr("Could not open the medium storage unit '%s'%s"),
6461 pMedium->m->strLocationFull.c_str(),
6462 vdError(vrc).c_str());
6463 }
6464
6465 Assert(m->state == MediumState_LockedWrite);
6466
6467 Utf8Str location(m->strLocationFull);
6468
6469 /* unlock before the potentially lengthy operation */
6470 thisLock.release();
6471
6472 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
6473 if (RT_FAILURE(vrc))
6474 {
6475 if (vrc == VERR_NOT_SUPPORTED)
6476 throw setError(VBOX_E_NOT_SUPPORTED,
6477 tr("Compacting is not yet supported for medium '%s'"),
6478 location.c_str());
6479 else if (vrc == VERR_NOT_IMPLEMENTED)
6480 throw setError(E_NOTIMPL,
6481 tr("Compacting is not implemented, medium '%s'"),
6482 location.c_str());
6483 else
6484 throw setError(VBOX_E_FILE_ERROR,
6485 tr("Could not compact medium '%s'%s"),
6486 location.c_str(),
6487 vdError(vrc).c_str());
6488 }
6489 }
6490 catch (HRESULT aRC) { rc = aRC; }
6491
6492 VDDestroy(hdd);
6493 }
6494 catch (HRESULT aRC) { rc = aRC; }
6495
6496 /* Everything is explicitly unlocked when the task exits,
6497 * as the task destruction also destroys the media chain. */
6498
6499 return rc;
6500}
6501
6502/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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