VirtualBox

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

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

Main: convert more Medium and MediumFormat internals to Utf8Str

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