VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MediumImpl.cpp@ 40904

Last change on this file since 40904 was 40542, checked in by vboxsync, 13 years ago

Main/Medium: Update medium registries when closing/deleting a medium. Regression.

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

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