VirtualBox

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

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

Main: comments

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

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