VirtualBox

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

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

Main: protect Medium::setIDs depending on medium state

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