VirtualBox

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

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

Main: Implemenation of per-machine media registries; VirtualBox::openMedium() no longer adds media to the global registry, instead a media are stored in a machine XML registry after Machine::AttachDevice() has been called; Machine::AttachDevice() now takes an IMedium object instead of a UUID; also make Machine::Unregister() work again for inaccessible machines

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