VirtualBox

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

最後變更 在這個檔案從32691是 32691,由 vboxsync 提交於 15 年 前

Storage/VBoxHDD+iSCSI+DrvVD,Main/Medium: Make the IO and TCPNET interfaces per i
mage, fix comments, remove forgotten dead code, handle disk and image interfaces

separately in Medium, whitespace cleanup

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

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