VirtualBox

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

Last change on this file since 40532 was 40506, checked in by vboxsync, 13 years ago

Main/Medium: Hopefully final go at holding the Medium object lock as briefly as possible. Previously the code was simpler, but held the lock over long operations such as opening images, and this caused e.g. slow GUI startup time.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 268.5 KB
Line 
1/* $Id: MediumImpl.cpp 40506 2012-03-16 17:05:32Z 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
4088 // release the AutoCaller, as otherwise uninit() will simply hang
4089 autoCaller.release();
4090
4091 // Keep the locks held until after uninit, as otherwise the consistency
4092 // of the medium tree cannot be guaranteed.
4093 uninit();
4094
4095 LogFlowFuncLeave();
4096
4097 return rc;
4098}
4099
4100/**
4101 * Deletes the medium storage unit.
4102 *
4103 * If @a aProgress is not NULL but the object it points to is @c null then a new
4104 * progress object will be created and assigned to @a *aProgress on success,
4105 * otherwise the existing progress object is used. If Progress is NULL, then no
4106 * progress object is created/used at all.
4107 *
4108 * When @a aWait is @c false, this method will create a thread to perform the
4109 * delete operation asynchronously and will return immediately. Otherwise, it
4110 * will perform the operation on the calling thread and will not return to the
4111 * caller until the operation is completed. Note that @a aProgress cannot be
4112 * NULL when @a aWait is @c false (this method will assert in this case).
4113 *
4114 * @param aProgress Where to find/store a Progress object to track operation
4115 * completion.
4116 * @param aWait @c true if this method should block instead of creating
4117 * an asynchronous thread.
4118 *
4119 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
4120 * writing.
4121 */
4122HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
4123 bool aWait)
4124{
4125 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4126
4127 AutoCaller autoCaller(this);
4128 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4129
4130 HRESULT rc = S_OK;
4131 ComObjPtr<Progress> pProgress;
4132 Medium::Task *pTask = NULL;
4133
4134 try
4135 {
4136 /* we're accessing the media tree, and canClose() needs it too */
4137 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
4138 this->lockHandle()
4139 COMMA_LOCKVAL_SRC_POS);
4140 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
4141
4142 if ( !(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
4143 | MediumFormatCapabilities_CreateFixed)))
4144 throw setError(VBOX_E_NOT_SUPPORTED,
4145 tr("Medium format '%s' does not support storage deletion"),
4146 m->strFormat.c_str());
4147
4148 /* Note that we are fine with Inaccessible state too: a) for symmetry
4149 * with create calls and b) because it doesn't really harm to try, if
4150 * it is really inaccessible, the delete operation will fail anyway.
4151 * Accepting Inaccessible state is especially important because all
4152 * registered media are initially Inaccessible upon VBoxSVC startup
4153 * until COMGETTER(RefreshState) is called. Accept Deleting state
4154 * because some callers need to put the medium in this state early
4155 * to prevent races. */
4156 switch (m->state)
4157 {
4158 case MediumState_Created:
4159 case MediumState_Deleting:
4160 case MediumState_Inaccessible:
4161 break;
4162 default:
4163 throw setStateError();
4164 }
4165
4166 if (m->backRefs.size() != 0)
4167 {
4168 Utf8Str strMachines;
4169 for (BackRefList::const_iterator it = m->backRefs.begin();
4170 it != m->backRefs.end();
4171 ++it)
4172 {
4173 const BackRef &b = *it;
4174 if (strMachines.length())
4175 strMachines.append(", ");
4176 strMachines.append(b.machineId.toString().c_str());
4177 }
4178#ifdef DEBUG
4179 dumpBackRefs();
4180#endif
4181 throw setError(VBOX_E_OBJECT_IN_USE,
4182 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
4183 m->strLocationFull.c_str(),
4184 m->backRefs.size(),
4185 strMachines.c_str());
4186 }
4187
4188 rc = canClose();
4189 if (FAILED(rc))
4190 throw rc;
4191
4192 /* go to Deleting state, so that the medium is not actually locked */
4193 if (m->state != MediumState_Deleting)
4194 {
4195 rc = markForDeletion();
4196 if (FAILED(rc))
4197 throw rc;
4198 }
4199
4200 /* Build the medium lock list. */
4201 MediumLockList *pMediumLockList(new MediumLockList());
4202 multilock.release();
4203 rc = createMediumLockList(true /* fFailIfInaccessible */,
4204 true /* fMediumLockWrite */,
4205 NULL,
4206 *pMediumLockList);
4207 multilock.acquire();
4208 if (FAILED(rc))
4209 {
4210 delete pMediumLockList;
4211 throw rc;
4212 }
4213
4214 multilock.release();
4215 rc = pMediumLockList->Lock();
4216 multilock.acquire();
4217 if (FAILED(rc))
4218 {
4219 delete pMediumLockList;
4220 throw setError(rc,
4221 tr("Failed to lock media when deleting '%s'"),
4222 getLocationFull().c_str());
4223 }
4224
4225 /* try to remove from the list of known media before performing
4226 * actual deletion (we favor the consistency of the media registry
4227 * which would have been broken if unregisterWithVirtualBox() failed
4228 * after we successfully deleted the storage) */
4229 rc = unregisterWithVirtualBox();
4230 if (FAILED(rc))
4231 throw rc;
4232 // no longer need lock
4233 multilock.release();
4234
4235 if (aProgress != NULL)
4236 {
4237 /* use the existing progress object... */
4238 pProgress = *aProgress;
4239
4240 /* ...but create a new one if it is null */
4241 if (pProgress.isNull())
4242 {
4243 pProgress.createObject();
4244 rc = pProgress->init(m->pVirtualBox,
4245 static_cast<IMedium*>(this),
4246 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
4247 FALSE /* aCancelable */);
4248 if (FAILED(rc))
4249 throw rc;
4250 }
4251 }
4252
4253 /* setup task object to carry out the operation sync/async */
4254 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
4255 rc = pTask->rc();
4256 AssertComRC(rc);
4257 if (FAILED(rc))
4258 throw rc;
4259 }
4260 catch (HRESULT aRC) { rc = aRC; }
4261
4262 if (SUCCEEDED(rc))
4263 {
4264 if (aWait)
4265 rc = runNow(pTask);
4266 else
4267 rc = startThread(pTask);
4268
4269 if (SUCCEEDED(rc) && aProgress != NULL)
4270 *aProgress = pProgress;
4271
4272 }
4273 else
4274 {
4275 if (pTask)
4276 delete pTask;
4277
4278 /* Undo deleting state if necessary. */
4279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4280 /* Make sure that any error signalled by unmarkForDeletion() is not
4281 * ending up in the error list (if the caller uses MultiResult). It
4282 * usually is spurious, as in most cases the medium hasn't been marked
4283 * for deletion when the error was thrown above. */
4284 ErrorInfoKeeper eik;
4285 unmarkForDeletion();
4286 }
4287
4288 return rc;
4289}
4290
4291/**
4292 * Mark a medium for deletion.
4293 *
4294 * @note Caller must hold the write lock on this medium!
4295 */
4296HRESULT Medium::markForDeletion()
4297{
4298 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
4299 switch (m->state)
4300 {
4301 case MediumState_Created:
4302 case MediumState_Inaccessible:
4303 m->preLockState = m->state;
4304 m->state = MediumState_Deleting;
4305 return S_OK;
4306 default:
4307 return setStateError();
4308 }
4309}
4310
4311/**
4312 * Removes the "mark for deletion".
4313 *
4314 * @note Caller must hold the write lock on this medium!
4315 */
4316HRESULT Medium::unmarkForDeletion()
4317{
4318 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
4319 switch (m->state)
4320 {
4321 case MediumState_Deleting:
4322 m->state = m->preLockState;
4323 return S_OK;
4324 default:
4325 return setStateError();
4326 }
4327}
4328
4329/**
4330 * Mark a medium for deletion which is in locked state.
4331 *
4332 * @note Caller must hold the write lock on this medium!
4333 */
4334HRESULT Medium::markLockedForDeletion()
4335{
4336 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
4337 if ( ( m->state == MediumState_LockedRead
4338 || m->state == MediumState_LockedWrite)
4339 && m->preLockState == MediumState_Created)
4340 {
4341 m->preLockState = MediumState_Deleting;
4342 return S_OK;
4343 }
4344 else
4345 return setStateError();
4346}
4347
4348/**
4349 * Removes the "mark for deletion" for a medium in locked state.
4350 *
4351 * @note Caller must hold the write lock on this medium!
4352 */
4353HRESULT Medium::unmarkLockedForDeletion()
4354{
4355 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
4356 if ( ( m->state == MediumState_LockedRead
4357 || m->state == MediumState_LockedWrite)
4358 && m->preLockState == MediumState_Deleting)
4359 {
4360 m->preLockState = MediumState_Created;
4361 return S_OK;
4362 }
4363 else
4364 return setStateError();
4365}
4366
4367/**
4368 * Prepares this (source) medium, target medium and all intermediate media
4369 * for the merge operation.
4370 *
4371 * This method is to be called prior to calling the #mergeTo() to perform
4372 * necessary consistency checks and place involved media to appropriate
4373 * states. If #mergeTo() is not called or fails, the state modifications
4374 * performed by this method must be undone by #cancelMergeTo().
4375 *
4376 * See #mergeTo() for more information about merging.
4377 *
4378 * @param pTarget Target medium.
4379 * @param aMachineId Allowed machine attachment. NULL means do not check.
4380 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
4381 * do not check.
4382 * @param fLockMedia Flag whether to lock the medium lock list or not.
4383 * If set to false and the medium lock list locking fails
4384 * later you must call #cancelMergeTo().
4385 * @param fMergeForward Resulting merge direction (out).
4386 * @param pParentForTarget New parent for target medium after merge (out).
4387 * @param aChildrenToReparent List of children of the source which will have
4388 * to be reparented to the target after merge (out).
4389 * @param aMediumLockList Medium locking information (out).
4390 *
4391 * @note Locks medium tree for reading. Locks this object, aTarget and all
4392 * intermediate media for writing.
4393 */
4394HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
4395 const Guid *aMachineId,
4396 const Guid *aSnapshotId,
4397 bool fLockMedia,
4398 bool &fMergeForward,
4399 ComObjPtr<Medium> &pParentForTarget,
4400 MediaList &aChildrenToReparent,
4401 MediumLockList * &aMediumLockList)
4402{
4403 AssertReturn(pTarget != NULL, E_FAIL);
4404 AssertReturn(pTarget != this, E_FAIL);
4405
4406 AutoCaller autoCaller(this);
4407 AssertComRCReturnRC(autoCaller.rc());
4408
4409 AutoCaller targetCaller(pTarget);
4410 AssertComRCReturnRC(targetCaller.rc());
4411
4412 HRESULT rc = S_OK;
4413 fMergeForward = false;
4414 pParentForTarget.setNull();
4415 aChildrenToReparent.clear();
4416 Assert(aMediumLockList == NULL);
4417 aMediumLockList = NULL;
4418
4419 try
4420 {
4421 // locking: we need the tree lock first because we access parent pointers
4422 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4423
4424 /* more sanity checking and figuring out the merge direction */
4425 ComObjPtr<Medium> pMedium = getParent();
4426 while (!pMedium.isNull() && pMedium != pTarget)
4427 pMedium = pMedium->getParent();
4428 if (pMedium == pTarget)
4429 fMergeForward = false;
4430 else
4431 {
4432 pMedium = pTarget->getParent();
4433 while (!pMedium.isNull() && pMedium != this)
4434 pMedium = pMedium->getParent();
4435 if (pMedium == this)
4436 fMergeForward = true;
4437 else
4438 {
4439 Utf8Str tgtLoc;
4440 {
4441 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4442 tgtLoc = pTarget->getLocationFull();
4443 }
4444
4445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4446 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4447 tr("Media '%s' and '%s' are unrelated"),
4448 m->strLocationFull.c_str(), tgtLoc.c_str());
4449 }
4450 }
4451
4452 /* Build the lock list. */
4453 aMediumLockList = new MediumLockList();
4454 treeLock.release();
4455 if (fMergeForward)
4456 rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
4457 true /* fMediumLockWrite */,
4458 NULL,
4459 *aMediumLockList);
4460 else
4461 rc = createMediumLockList(true /* fFailIfInaccessible */,
4462 false /* fMediumLockWrite */,
4463 NULL,
4464 *aMediumLockList);
4465 treeLock.acquire();
4466 if (FAILED(rc))
4467 throw rc;
4468
4469 /* Sanity checking, must be after lock list creation as it depends on
4470 * valid medium states. The medium objects must be accessible. Only
4471 * do this if immediate locking is requested, otherwise it fails when
4472 * we construct a medium lock list for an already running VM. Snapshot
4473 * deletion uses this to simplify its life. */
4474 if (fLockMedia)
4475 {
4476 {
4477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4478 if (m->state != MediumState_Created)
4479 throw setStateError();
4480 }
4481 {
4482 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4483 if (pTarget->m->state != MediumState_Created)
4484 throw pTarget->setStateError();
4485 }
4486 }
4487
4488 /* check medium attachment and other sanity conditions */
4489 if (fMergeForward)
4490 {
4491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4492 if (getChildren().size() > 1)
4493 {
4494 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4495 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4496 m->strLocationFull.c_str(), getChildren().size());
4497 }
4498 /* One backreference is only allowed if the machine ID is not empty
4499 * and it matches the machine the medium is attached to (including
4500 * the snapshot ID if not empty). */
4501 if ( m->backRefs.size() != 0
4502 && ( !aMachineId
4503 || m->backRefs.size() != 1
4504 || aMachineId->isEmpty()
4505 || *getFirstMachineBackrefId() != *aMachineId
4506 || ( (!aSnapshotId || !aSnapshotId->isEmpty())
4507 && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
4508 throw setError(VBOX_E_OBJECT_IN_USE,
4509 tr("Medium '%s' is attached to %d virtual machines"),
4510 m->strLocationFull.c_str(), m->backRefs.size());
4511 if (m->type == MediumType_Immutable)
4512 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4513 tr("Medium '%s' is immutable"),
4514 m->strLocationFull.c_str());
4515 if (m->type == MediumType_MultiAttach)
4516 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4517 tr("Medium '%s' is multi-attach"),
4518 m->strLocationFull.c_str());
4519 }
4520 else
4521 {
4522 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4523 if (pTarget->getChildren().size() > 1)
4524 {
4525 throw setError(VBOX_E_OBJECT_IN_USE,
4526 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4527 pTarget->m->strLocationFull.c_str(),
4528 pTarget->getChildren().size());
4529 }
4530 if (pTarget->m->type == MediumType_Immutable)
4531 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4532 tr("Medium '%s' is immutable"),
4533 pTarget->m->strLocationFull.c_str());
4534 if (pTarget->m->type == MediumType_MultiAttach)
4535 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4536 tr("Medium '%s' is multi-attach"),
4537 pTarget->m->strLocationFull.c_str());
4538 }
4539 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
4540 ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
4541 for (pLast = pLastIntermediate;
4542 !pLast.isNull() && pLast != pTarget && pLast != this;
4543 pLast = pLast->getParent())
4544 {
4545 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4546 if (pLast->getChildren().size() > 1)
4547 {
4548 throw setError(VBOX_E_OBJECT_IN_USE,
4549 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4550 pLast->m->strLocationFull.c_str(),
4551 pLast->getChildren().size());
4552 }
4553 if (pLast->m->backRefs.size() != 0)
4554 throw setError(VBOX_E_OBJECT_IN_USE,
4555 tr("Medium '%s' is attached to %d virtual machines"),
4556 pLast->m->strLocationFull.c_str(),
4557 pLast->m->backRefs.size());
4558
4559 }
4560
4561 /* Update medium states appropriately */
4562 {
4563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4564
4565 if (m->state == MediumState_Created)
4566 {
4567 rc = markForDeletion();
4568 if (FAILED(rc))
4569 throw rc;
4570 }
4571 else
4572 {
4573 if (fLockMedia)
4574 throw setStateError();
4575 else if ( m->state == MediumState_LockedWrite
4576 || m->state == MediumState_LockedRead)
4577 {
4578 /* Either mark it for deletion in locked state or allow
4579 * others to have done so. */
4580 if (m->preLockState == MediumState_Created)
4581 markLockedForDeletion();
4582 else if (m->preLockState != MediumState_Deleting)
4583 throw setStateError();
4584 }
4585 else
4586 throw setStateError();
4587 }
4588 }
4589
4590 if (fMergeForward)
4591 {
4592 /* we will need parent to reparent target */
4593 pParentForTarget = getParent();
4594 }
4595 else
4596 {
4597 /* we will need to reparent children of the source */
4598 for (MediaList::const_iterator it = getChildren().begin();
4599 it != getChildren().end();
4600 ++it)
4601 {
4602 pMedium = *it;
4603 if (fLockMedia)
4604 {
4605 rc = pMedium->LockWrite(NULL);
4606 if (FAILED(rc))
4607 throw rc;
4608 }
4609
4610 aChildrenToReparent.push_back(pMedium);
4611 }
4612 }
4613 for (pLast = pLastIntermediate;
4614 !pLast.isNull() && pLast != pTarget && pLast != this;
4615 pLast = pLast->getParent())
4616 {
4617 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4618 if (pLast->m->state == MediumState_Created)
4619 {
4620 rc = pLast->markForDeletion();
4621 if (FAILED(rc))
4622 throw rc;
4623 }
4624 else
4625 throw pLast->setStateError();
4626 }
4627
4628 /* Tweak the lock list in the backward merge case, as the target
4629 * isn't marked to be locked for writing yet. */
4630 if (!fMergeForward)
4631 {
4632 MediumLockList::Base::iterator lockListBegin =
4633 aMediumLockList->GetBegin();
4634 MediumLockList::Base::iterator lockListEnd =
4635 aMediumLockList->GetEnd();
4636 lockListEnd--;
4637 for (MediumLockList::Base::iterator it = lockListBegin;
4638 it != lockListEnd;
4639 ++it)
4640 {
4641 MediumLock &mediumLock = *it;
4642 if (mediumLock.GetMedium() == pTarget)
4643 {
4644 HRESULT rc2 = mediumLock.UpdateLock(true);
4645 AssertComRC(rc2);
4646 break;
4647 }
4648 }
4649 }
4650
4651 if (fLockMedia)
4652 {
4653 treeLock.release();
4654 rc = aMediumLockList->Lock();
4655 treeLock.acquire();
4656 if (FAILED(rc))
4657 {
4658 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4659 throw setError(rc,
4660 tr("Failed to lock media when merging to '%s'"),
4661 pTarget->getLocationFull().c_str());
4662 }
4663 }
4664 }
4665 catch (HRESULT aRC) { rc = aRC; }
4666
4667 if (FAILED(rc))
4668 {
4669 delete aMediumLockList;
4670 aMediumLockList = NULL;
4671 }
4672
4673 return rc;
4674}
4675
4676/**
4677 * Merges this medium to the specified medium which must be either its
4678 * direct ancestor or descendant.
4679 *
4680 * Given this medium is SOURCE and the specified medium is TARGET, we will
4681 * get two variants of the merge operation:
4682 *
4683 * forward merge
4684 * ------------------------->
4685 * [Extra] <- SOURCE <- Intermediate <- TARGET
4686 * Any Del Del LockWr
4687 *
4688 *
4689 * backward merge
4690 * <-------------------------
4691 * TARGET <- Intermediate <- SOURCE <- [Extra]
4692 * LockWr Del Del LockWr
4693 *
4694 * Each diagram shows the involved media on the media chain where
4695 * SOURCE and TARGET belong. Under each medium there is a state value which
4696 * the medium must have at a time of the mergeTo() call.
4697 *
4698 * The media in the square braces may be absent (e.g. when the forward
4699 * operation takes place and SOURCE is the base medium, or when the backward
4700 * merge operation takes place and TARGET is the last child in the chain) but if
4701 * they present they are involved too as shown.
4702 *
4703 * Neither the source medium nor intermediate media may be attached to
4704 * any VM directly or in the snapshot, otherwise this method will assert.
4705 *
4706 * The #prepareMergeTo() method must be called prior to this method to place all
4707 * involved to necessary states and perform other consistency checks.
4708 *
4709 * If @a aWait is @c true then this method will perform the operation on the
4710 * calling thread and will not return to the caller until the operation is
4711 * completed. When this method succeeds, all intermediate medium objects in
4712 * the chain will be uninitialized, the state of the target medium (and all
4713 * involved extra media) will be restored. @a aMediumLockList will not be
4714 * deleted, whether the operation is successful or not. The caller has to do
4715 * this if appropriate. Note that this (source) medium is not uninitialized
4716 * because of possible AutoCaller instances held by the caller of this method
4717 * on the current thread. It's therefore the responsibility of the caller to
4718 * call Medium::uninit() after releasing all callers.
4719 *
4720 * If @a aWait is @c false then this method will create a thread to perform the
4721 * operation asynchronously and will return immediately. If the operation
4722 * succeeds, the thread will uninitialize the source medium object and all
4723 * intermediate medium objects in the chain, reset the state of the target
4724 * medium (and all involved extra media) and delete @a aMediumLockList.
4725 * If the operation fails, the thread will only reset the states of all
4726 * involved media and delete @a aMediumLockList.
4727 *
4728 * When this method fails (regardless of the @a aWait mode), it is a caller's
4729 * responsibility to undo state changes and delete @a aMediumLockList using
4730 * #cancelMergeTo().
4731 *
4732 * If @a aProgress is not NULL but the object it points to is @c null then a new
4733 * progress object will be created and assigned to @a *aProgress on success,
4734 * otherwise the existing progress object is used. If Progress is NULL, then no
4735 * progress object is created/used at all. Note that @a aProgress cannot be
4736 * NULL when @a aWait is @c false (this method will assert in this case).
4737 *
4738 * @param pTarget Target medium.
4739 * @param fMergeForward Merge direction.
4740 * @param pParentForTarget New parent for target medium after merge.
4741 * @param aChildrenToReparent List of children of the source which will have
4742 * to be reparented to the target after merge.
4743 * @param aMediumLockList Medium locking information.
4744 * @param aProgress Where to find/store a Progress object to track operation
4745 * completion.
4746 * @param aWait @c true if this method should block instead of creating
4747 * an asynchronous thread.
4748 *
4749 * @note Locks the tree lock for writing. Locks the media from the chain
4750 * for writing.
4751 */
4752HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
4753 bool fMergeForward,
4754 const ComObjPtr<Medium> &pParentForTarget,
4755 const MediaList &aChildrenToReparent,
4756 MediumLockList *aMediumLockList,
4757 ComObjPtr <Progress> *aProgress,
4758 bool aWait)
4759{
4760 AssertReturn(pTarget != NULL, E_FAIL);
4761 AssertReturn(pTarget != this, E_FAIL);
4762 AssertReturn(aMediumLockList != NULL, E_FAIL);
4763 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4764
4765 AutoCaller autoCaller(this);
4766 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4767
4768 AutoCaller targetCaller(pTarget);
4769 AssertComRCReturnRC(targetCaller.rc());
4770
4771 HRESULT rc = S_OK;
4772 ComObjPtr <Progress> pProgress;
4773 Medium::Task *pTask = NULL;
4774
4775 try
4776 {
4777 if (aProgress != NULL)
4778 {
4779 /* use the existing progress object... */
4780 pProgress = *aProgress;
4781
4782 /* ...but create a new one if it is null */
4783 if (pProgress.isNull())
4784 {
4785 Utf8Str tgtName;
4786 {
4787 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4788 tgtName = pTarget->getName();
4789 }
4790
4791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4792
4793 pProgress.createObject();
4794 rc = pProgress->init(m->pVirtualBox,
4795 static_cast<IMedium*>(this),
4796 BstrFmt(tr("Merging medium '%s' to '%s'"),
4797 getName().c_str(),
4798 tgtName.c_str()).raw(),
4799 TRUE /* aCancelable */);
4800 if (FAILED(rc))
4801 throw rc;
4802 }
4803 }
4804
4805 /* setup task object to carry out the operation sync/async */
4806 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
4807 pParentForTarget, aChildrenToReparent,
4808 pProgress, aMediumLockList,
4809 aWait /* fKeepMediumLockList */);
4810 rc = pTask->rc();
4811 AssertComRC(rc);
4812 if (FAILED(rc))
4813 throw rc;
4814 }
4815 catch (HRESULT aRC) { rc = aRC; }
4816
4817 if (SUCCEEDED(rc))
4818 {
4819 if (aWait)
4820 rc = runNow(pTask);
4821 else
4822 rc = startThread(pTask);
4823
4824 if (SUCCEEDED(rc) && aProgress != NULL)
4825 *aProgress = pProgress;
4826 }
4827 else if (pTask != NULL)
4828 delete pTask;
4829
4830 return rc;
4831}
4832
4833/**
4834 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
4835 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
4836 * the medium objects in @a aChildrenToReparent.
4837 *
4838 * @param aChildrenToReparent List of children of the source which will have
4839 * to be reparented to the target after merge.
4840 * @param aMediumLockList Medium locking information.
4841 *
4842 * @note Locks the media from the chain for writing.
4843 */
4844void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
4845 MediumLockList *aMediumLockList)
4846{
4847 AutoCaller autoCaller(this);
4848 AssertComRCReturnVoid(autoCaller.rc());
4849
4850 AssertReturnVoid(aMediumLockList != NULL);
4851
4852 /* Revert media marked for deletion to previous state. */
4853 HRESULT rc;
4854 MediumLockList::Base::const_iterator mediumListBegin =
4855 aMediumLockList->GetBegin();
4856 MediumLockList::Base::const_iterator mediumListEnd =
4857 aMediumLockList->GetEnd();
4858 for (MediumLockList::Base::const_iterator it = mediumListBegin;
4859 it != mediumListEnd;
4860 ++it)
4861 {
4862 const MediumLock &mediumLock = *it;
4863 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4864 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4865
4866 if (pMedium->m->state == MediumState_Deleting)
4867 {
4868 rc = pMedium->unmarkForDeletion();
4869 AssertComRC(rc);
4870 }
4871 }
4872
4873 /* the destructor will do the work */
4874 delete aMediumLockList;
4875
4876 /* unlock the children which had to be reparented */
4877 for (MediaList::const_iterator it = aChildrenToReparent.begin();
4878 it != aChildrenToReparent.end();
4879 ++it)
4880 {
4881 const ComObjPtr<Medium> &pMedium = *it;
4882
4883 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4884 pMedium->UnlockWrite(NULL);
4885 }
4886}
4887
4888/**
4889 * Fix the parent UUID of all children to point to this medium as their
4890 * parent.
4891 */
4892HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
4893{
4894 Assert(!isWriteLockOnCurrentThread());
4895 Assert(!m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4896 MediumLockList mediumLockList;
4897 HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
4898 false /* fMediumLockWrite */,
4899 this,
4900 mediumLockList);
4901 AssertComRCReturnRC(rc);
4902
4903 try
4904 {
4905 PVBOXHDD hdd;
4906 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
4907 ComAssertRCThrow(vrc, E_FAIL);
4908
4909 try
4910 {
4911 MediumLockList::Base::iterator lockListBegin =
4912 mediumLockList.GetBegin();
4913 MediumLockList::Base::iterator lockListEnd =
4914 mediumLockList.GetEnd();
4915 for (MediumLockList::Base::iterator it = lockListBegin;
4916 it != lockListEnd;
4917 ++it)
4918 {
4919 MediumLock &mediumLock = *it;
4920 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4921 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4922
4923 // open the medium
4924 vrc = VDOpen(hdd,
4925 pMedium->m->strFormat.c_str(),
4926 pMedium->m->strLocationFull.c_str(),
4927 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
4928 pMedium->m->vdImageIfaces);
4929 if (RT_FAILURE(vrc))
4930 throw vrc;
4931 }
4932
4933 for (MediaList::const_iterator it = childrenToReparent.begin();
4934 it != childrenToReparent.end();
4935 ++it)
4936 {
4937 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
4938 vrc = VDOpen(hdd,
4939 (*it)->m->strFormat.c_str(),
4940 (*it)->m->strLocationFull.c_str(),
4941 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
4942 (*it)->m->vdImageIfaces);
4943 if (RT_FAILURE(vrc))
4944 throw vrc;
4945
4946 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
4947 if (RT_FAILURE(vrc))
4948 throw vrc;
4949
4950 vrc = VDClose(hdd, false /* fDelete */);
4951 if (RT_FAILURE(vrc))
4952 throw vrc;
4953
4954 (*it)->UnlockWrite(NULL);
4955 }
4956 }
4957 catch (HRESULT aRC) { rc = aRC; }
4958 catch (int aVRC)
4959 {
4960 rc = setError(E_FAIL,
4961 tr("Could not update medium UUID references to parent '%s' (%s)"),
4962 m->strLocationFull.c_str(),
4963 vdError(aVRC).c_str());
4964 }
4965
4966 VDDestroy(hdd);
4967 }
4968 catch (HRESULT aRC) { rc = aRC; }
4969
4970 return rc;
4971}
4972
4973/**
4974 * Used by IAppliance to export disk images.
4975 *
4976 * @param aFilename Filename to create (UTF8).
4977 * @param aFormat Medium format for creating @a aFilename.
4978 * @param aVariant Which exact image format variant to use
4979 * for the destination image.
4980 * @param aVDImageIOCallbacks Pointer to the callback table for a
4981 * VDINTERFACEIO interface. May be NULL.
4982 * @param aVDImageIOUser Opaque data for the callbacks.
4983 * @param aProgress Progress object to use.
4984 * @return
4985 * @note The source format is defined by the Medium instance.
4986 */
4987HRESULT Medium::exportFile(const char *aFilename,
4988 const ComObjPtr<MediumFormat> &aFormat,
4989 MediumVariant_T aVariant,
4990 PVDINTERFACEIO aVDImageIOIf, void *aVDImageIOUser,
4991 const ComObjPtr<Progress> &aProgress)
4992{
4993 AssertPtrReturn(aFilename, E_INVALIDARG);
4994 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
4995 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
4996
4997 AutoCaller autoCaller(this);
4998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4999
5000 HRESULT rc = S_OK;
5001 Medium::Task *pTask = NULL;
5002
5003 try
5004 {
5005 // This needs no extra locks besides what is done in the called methods.
5006
5007 /* Build the source lock list. */
5008 MediumLockList *pSourceMediumLockList(new MediumLockList());
5009 rc = createMediumLockList(true /* fFailIfInaccessible */,
5010 false /* fMediumLockWrite */,
5011 NULL,
5012 *pSourceMediumLockList);
5013 if (FAILED(rc))
5014 {
5015 delete pSourceMediumLockList;
5016 throw rc;
5017 }
5018
5019 rc = pSourceMediumLockList->Lock();
5020 if (FAILED(rc))
5021 {
5022 delete pSourceMediumLockList;
5023 throw setError(rc,
5024 tr("Failed to lock source media '%s'"),
5025 getLocationFull().c_str());
5026 }
5027
5028 /* setup task object to carry out the operation asynchronously */
5029 pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
5030 aVariant, aVDImageIOIf,
5031 aVDImageIOUser, pSourceMediumLockList);
5032 rc = pTask->rc();
5033 AssertComRC(rc);
5034 if (FAILED(rc))
5035 throw rc;
5036 }
5037 catch (HRESULT aRC) { rc = aRC; }
5038
5039 if (SUCCEEDED(rc))
5040 rc = startThread(pTask);
5041 else if (pTask != NULL)
5042 delete pTask;
5043
5044 return rc;
5045}
5046
5047/**
5048 * Used by IAppliance to import disk images.
5049 *
5050 * @param aFilename Filename to read (UTF8).
5051 * @param aFormat Medium format for reading @a aFilename.
5052 * @param aVariant Which exact image format variant to use
5053 * for the destination image.
5054 * @param aVDImageIOCallbacks Pointer to the callback table for a
5055 * VDINTERFACEIO interface. May be NULL.
5056 * @param aVDImageIOUser Opaque data for the callbacks.
5057 * @param aParent Parent medium. May be NULL.
5058 * @param aProgress Progress object to use.
5059 * @return
5060 * @note The destination format is defined by the Medium instance.
5061 */
5062HRESULT Medium::importFile(const char *aFilename,
5063 const ComObjPtr<MediumFormat> &aFormat,
5064 MediumVariant_T aVariant,
5065 PVDINTERFACEIO aVDImageIOIf, void *aVDImageIOUser,
5066 const ComObjPtr<Medium> &aParent,
5067 const ComObjPtr<Progress> &aProgress)
5068{
5069 AssertPtrReturn(aFilename, E_INVALIDARG);
5070 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
5071 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
5072
5073 AutoCaller autoCaller(this);
5074 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5075
5076 HRESULT rc = S_OK;
5077 Medium::Task *pTask = NULL;
5078
5079 try
5080 {
5081 // locking: we need the tree lock first because we access parent pointers
5082 // and we need to write-lock the media involved
5083 uint32_t cHandles = 2;
5084 LockHandle* pHandles[3] = { &m->pVirtualBox->getMediaTreeLockHandle(),
5085 this->lockHandle() };
5086 /* Only add parent to the lock if it is not null */
5087 if (!aParent.isNull())
5088 pHandles[cHandles++] = aParent->lockHandle();
5089 AutoWriteLock alock(cHandles,
5090 pHandles
5091 COMMA_LOCKVAL_SRC_POS);
5092
5093 if ( m->state != MediumState_NotCreated
5094 && m->state != MediumState_Created)
5095 throw setStateError();
5096
5097 /* Build the target lock list. */
5098 MediumLockList *pTargetMediumLockList(new MediumLockList());
5099 alock.release();
5100 rc = createMediumLockList(true /* fFailIfInaccessible */,
5101 true /* fMediumLockWrite */,
5102 aParent,
5103 *pTargetMediumLockList);
5104 alock.acquire();
5105 if (FAILED(rc))
5106 {
5107 delete pTargetMediumLockList;
5108 throw rc;
5109 }
5110
5111 alock.release();
5112 rc = pTargetMediumLockList->Lock();
5113 alock.acquire();
5114 if (FAILED(rc))
5115 {
5116 delete pTargetMediumLockList;
5117 throw setError(rc,
5118 tr("Failed to lock target media '%s'"),
5119 getLocationFull().c_str());
5120 }
5121
5122 /* setup task object to carry out the operation asynchronously */
5123 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,
5124 aVariant, aVDImageIOIf,
5125 aVDImageIOUser, aParent,
5126 pTargetMediumLockList);
5127 rc = pTask->rc();
5128 AssertComRC(rc);
5129 if (FAILED(rc))
5130 throw rc;
5131
5132 if (m->state == MediumState_NotCreated)
5133 m->state = MediumState_Creating;
5134 }
5135 catch (HRESULT aRC) { rc = aRC; }
5136
5137 if (SUCCEEDED(rc))
5138 rc = startThread(pTask);
5139 else if (pTask != NULL)
5140 delete pTask;
5141
5142 return rc;
5143}
5144
5145/**
5146 * Internal version of the public CloneTo API which allows to enable certain
5147 * optimizations to improve speed during VM cloning.
5148 *
5149 * @param aTarget Target medium
5150 * @param aVariant Which exact image format variant to use
5151 * for the destination image.
5152 * @param aParent Parent medium. May be NULL.
5153 * @param aProgress Progress object to use.
5154 * @param idxSrcImageSame The last image in the source chain which has the
5155 * same content as the given image in the destination
5156 * chain. Use UINT32_MAX to disable this optimization.
5157 * @param idxDstImageSame The last image in the destination chain which has the
5158 * same content as the given image in the source chain.
5159 * Use UINT32_MAX to disable this optimization.
5160 * @return
5161 */
5162HRESULT Medium::cloneToEx(const ComObjPtr<Medium> &aTarget, ULONG aVariant,
5163 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
5164 uint32_t idxSrcImageSame, uint32_t idxDstImageSame)
5165{
5166 CheckComArgNotNull(aTarget);
5167 CheckComArgOutPointerValid(aProgress);
5168 ComAssertRet(aTarget != this, E_INVALIDARG);
5169
5170 AutoCaller autoCaller(this);
5171 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5172
5173 HRESULT rc = S_OK;
5174 ComObjPtr<Progress> pProgress;
5175 Medium::Task *pTask = NULL;
5176
5177 try
5178 {
5179 // locking: we need the tree lock first because we access parent pointers
5180 // and we need to write-lock the media involved
5181 uint32_t cHandles = 3;
5182 LockHandle* pHandles[4] = { &m->pVirtualBox->getMediaTreeLockHandle(),
5183 this->lockHandle(),
5184 aTarget->lockHandle() };
5185 /* Only add parent to the lock if it is not null */
5186 if (!aParent.isNull())
5187 pHandles[cHandles++] = aParent->lockHandle();
5188 AutoWriteLock alock(cHandles,
5189 pHandles
5190 COMMA_LOCKVAL_SRC_POS);
5191
5192 if ( aTarget->m->state != MediumState_NotCreated
5193 && aTarget->m->state != MediumState_Created)
5194 throw aTarget->setStateError();
5195
5196 /* Build the source lock list. */
5197 MediumLockList *pSourceMediumLockList(new MediumLockList());
5198 alock.release();
5199 rc = createMediumLockList(true /* fFailIfInaccessible */,
5200 false /* fMediumLockWrite */,
5201 NULL,
5202 *pSourceMediumLockList);
5203 alock.acquire();
5204 if (FAILED(rc))
5205 {
5206 delete pSourceMediumLockList;
5207 throw rc;
5208 }
5209
5210 /* Build the target lock list (including the to-be parent chain). */
5211 MediumLockList *pTargetMediumLockList(new MediumLockList());
5212 alock.release();
5213 rc = aTarget->createMediumLockList(true /* fFailIfInaccessible */,
5214 true /* fMediumLockWrite */,
5215 aParent,
5216 *pTargetMediumLockList);
5217 alock.acquire();
5218 if (FAILED(rc))
5219 {
5220 delete pSourceMediumLockList;
5221 delete pTargetMediumLockList;
5222 throw rc;
5223 }
5224
5225 alock.release();
5226 rc = pSourceMediumLockList->Lock();
5227 alock.acquire();
5228 if (FAILED(rc))
5229 {
5230 delete pSourceMediumLockList;
5231 delete pTargetMediumLockList;
5232 throw setError(rc,
5233 tr("Failed to lock source media '%s'"),
5234 getLocationFull().c_str());
5235 }
5236 alock.release();
5237 rc = pTargetMediumLockList->Lock();
5238 alock.acquire();
5239 if (FAILED(rc))
5240 {
5241 delete pSourceMediumLockList;
5242 delete pTargetMediumLockList;
5243 throw setError(rc,
5244 tr("Failed to lock target media '%s'"),
5245 aTarget->getLocationFull().c_str());
5246 }
5247
5248 pProgress.createObject();
5249 rc = pProgress->init(m->pVirtualBox,
5250 static_cast <IMedium *>(this),
5251 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
5252 TRUE /* aCancelable */);
5253 if (FAILED(rc))
5254 {
5255 delete pSourceMediumLockList;
5256 delete pTargetMediumLockList;
5257 throw rc;
5258 }
5259
5260 /* setup task object to carry out the operation asynchronously */
5261 pTask = new Medium::CloneTask(this, pProgress, aTarget,
5262 (MediumVariant_T)aVariant,
5263 aParent, idxSrcImageSame,
5264 idxDstImageSame, pSourceMediumLockList,
5265 pTargetMediumLockList);
5266 rc = pTask->rc();
5267 AssertComRC(rc);
5268 if (FAILED(rc))
5269 throw rc;
5270
5271 if (aTarget->m->state == MediumState_NotCreated)
5272 aTarget->m->state = MediumState_Creating;
5273 }
5274 catch (HRESULT aRC) { rc = aRC; }
5275
5276 if (SUCCEEDED(rc))
5277 {
5278 rc = startThread(pTask);
5279
5280 if (SUCCEEDED(rc))
5281 pProgress.queryInterfaceTo(aProgress);
5282 }
5283 else if (pTask != NULL)
5284 delete pTask;
5285
5286 return rc;
5287}
5288
5289////////////////////////////////////////////////////////////////////////////////
5290//
5291// Private methods
5292//
5293////////////////////////////////////////////////////////////////////////////////
5294
5295/**
5296 * Queries information from the medium.
5297 *
5298 * As a result of this call, the accessibility state and data members such as
5299 * size and description will be updated with the current information.
5300 *
5301 * @note This method may block during a system I/O call that checks storage
5302 * accessibility.
5303 *
5304 * @note Caller MUST NOT hold the media tree or medium lock.
5305 *
5306 * @note Locks mParent for reading. Locks this object for writing.
5307 *
5308 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
5309 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs())
5310 * @return
5311 */
5312HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
5313{
5314 Assert(!isWriteLockOnCurrentThread());
5315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5316
5317 if ( m->state != MediumState_Created
5318 && m->state != MediumState_Inaccessible
5319 && m->state != MediumState_LockedRead)
5320 return E_FAIL;
5321
5322 HRESULT rc = S_OK;
5323
5324 int vrc = VINF_SUCCESS;
5325
5326 /* check if a blocking queryInfo() call is in progress on some other thread,
5327 * and wait for it to finish if so instead of querying data ourselves */
5328 if (m->queryInfoRunning)
5329 {
5330 Assert( m->state == MediumState_LockedRead
5331 || m->state == MediumState_LockedWrite);
5332
5333 while (m->queryInfoRunning)
5334 {
5335 alock.release();
5336 {
5337 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5338 }
5339 alock.acquire();
5340 }
5341
5342 return S_OK;
5343 }
5344
5345 bool success = false;
5346 Utf8Str lastAccessError;
5347
5348 /* are we dealing with a new medium constructed using the existing
5349 * location? */
5350 bool isImport = m->id.isEmpty();
5351 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
5352
5353 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
5354 * media because that would prevent necessary modifications
5355 * when opening media of some third-party formats for the first
5356 * time in VirtualBox (such as VMDK for which VDOpen() needs to
5357 * generate an UUID if it is missing) */
5358 if ( m->hddOpenMode == OpenReadOnly
5359 || m->type == MediumType_Readonly
5360 || (!isImport && !fSetImageId && !fSetParentId)
5361 )
5362 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5363
5364 /* Open shareable medium with the appropriate flags */
5365 if (m->type == MediumType_Shareable)
5366 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
5367
5368 /* Lock the medium, which makes the behavior much more consistent */
5369 alock.release();
5370 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
5371 rc = LockRead(NULL);
5372 else
5373 rc = LockWrite(NULL);
5374 if (FAILED(rc)) return rc;
5375 alock.acquire();
5376
5377 /* Copies of the input state fields which are not read-only,
5378 * as we're dropping the lock. CAUTION: be extremely careful what
5379 * you do with the contents of this medium object, as you will
5380 * create races if there are concurrent changes. */
5381 Utf8Str format(m->strFormat);
5382 Utf8Str location(m->strLocationFull);
5383 ComObjPtr<MediumFormat> formatObj = m->formatObj;
5384
5385 /* "Output" values which can't be set because the lock isn't held
5386 * at the time the values are determined. */
5387 Guid mediumId = m->id;
5388 uint64_t mediumSize = 0;
5389 uint64_t mediumLogicalSize = 0;
5390
5391 /* Flag whether a base image has a non-zero parent UUID and thus
5392 * need repairing after it was closed again. */
5393 bool fRepairImageZeroParentUuid = false;
5394
5395 /* release the object lock before a lengthy operation, and take the
5396 * opportunity to have a media tree lock, too, which isn't held initially */
5397 m->queryInfoRunning = true;
5398 alock.release();
5399 Assert(!isWriteLockOnCurrentThread());
5400 Assert(!m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5401 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5402 treeLock.release();
5403
5404 /* Note that taking the queryInfoSem after leaving the object lock above
5405 * can lead to short spinning of the loops waiting for queryInfo() to
5406 * complete. This is unavoidable since the other order causes a lock order
5407 * violation: here it would be requesting the object lock (at the beginning
5408 * of the method), then queryInfoSem, and below the other way round. */
5409 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5410
5411 try
5412 {
5413 /* skip accessibility checks for host drives */
5414 if (m->hostDrive)
5415 {
5416 success = true;
5417 throw S_OK;
5418 }
5419
5420 PVBOXHDD hdd;
5421 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
5422 ComAssertRCThrow(vrc, E_FAIL);
5423
5424 try
5425 {
5426 /** @todo This kind of opening of media is assuming that diff
5427 * media can be opened as base media. Should be documented that
5428 * it must work for all medium format backends. */
5429 vrc = VDOpen(hdd,
5430 format.c_str(),
5431 location.c_str(),
5432 uOpenFlags | m->uOpenFlagsDef,
5433 m->vdImageIfaces);
5434 if (RT_FAILURE(vrc))
5435 {
5436 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
5437 location.c_str(), vdError(vrc).c_str());
5438 throw S_OK;
5439 }
5440
5441 if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
5442 {
5443 /* Modify the UUIDs if necessary. The associated fields are
5444 * not modified by other code, so no need to copy. */
5445 if (fSetImageId)
5446 {
5447 alock.acquire();
5448 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
5449 alock.release();
5450 ComAssertRCThrow(vrc, E_FAIL);
5451 mediumId = m->uuidImage;
5452 }
5453 if (fSetParentId)
5454 {
5455 alock.acquire();
5456 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
5457 alock.release();
5458 ComAssertRCThrow(vrc, E_FAIL);
5459 }
5460 /* zap the information, these are no long-term members */
5461 alock.acquire();
5462 unconst(m->uuidImage).clear();
5463 unconst(m->uuidParentImage).clear();
5464 alock.release();
5465
5466 /* check the UUID */
5467 RTUUID uuid;
5468 vrc = VDGetUuid(hdd, 0, &uuid);
5469 ComAssertRCThrow(vrc, E_FAIL);
5470
5471 if (isImport)
5472 {
5473 mediumId = uuid;
5474
5475 if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
5476 // only when importing a VDMK that has no UUID, create one in memory
5477 mediumId.create();
5478 }
5479 else
5480 {
5481 Assert(!mediumId.isEmpty());
5482
5483 if (mediumId != uuid)
5484 {
5485 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
5486 lastAccessError = Utf8StrFmt(
5487 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
5488 &uuid,
5489 location.c_str(),
5490 mediumId.raw(),
5491 m->pVirtualBox->settingsFilePath().c_str());
5492 throw S_OK;
5493 }
5494 }
5495 }
5496 else
5497 {
5498 /* the backend does not support storing UUIDs within the
5499 * underlying storage so use what we store in XML */
5500
5501 if (fSetImageId)
5502 {
5503 /* set the UUID if an API client wants to change it */
5504 alock.acquire();
5505 mediumId = m->uuidImage;
5506 alock.release();
5507 }
5508 else if (isImport)
5509 {
5510 /* generate an UUID for an imported UUID-less medium */
5511 mediumId.create();
5512 }
5513 }
5514
5515 /* get the medium variant */
5516 unsigned uImageFlags;
5517 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5518 ComAssertRCThrow(vrc, E_FAIL);
5519 alock.acquire();
5520 m->variant = (MediumVariant_T)uImageFlags;
5521 alock.release();
5522
5523 /* check/get the parent uuid and update corresponding state */
5524 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
5525 {
5526 RTUUID parentId;
5527 vrc = VDGetParentUuid(hdd, 0, &parentId);
5528 ComAssertRCThrow(vrc, E_FAIL);
5529
5530 /* streamOptimized VMDK images are only accepted as base
5531 * images, as this allows automatic repair of OVF appliances.
5532 * Since such images don't support random writes they will not
5533 * be created for diff images. Only an overly smart user might
5534 * manually create this case. Too bad for him. */
5535 if ( isImport
5536 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
5537 {
5538 /* the parent must be known to us. Note that we freely
5539 * call locking methods of mVirtualBox and parent, as all
5540 * relevant locks must be already held. There may be no
5541 * concurrent access to the just opened medium on other
5542 * threads yet (and init() will fail if this method reports
5543 * MediumState_Inaccessible) */
5544
5545 Guid id = parentId;
5546 ComObjPtr<Medium> pParent;
5547 rc = m->pVirtualBox->findHardDiskById(id, false /* aSetError */, &pParent);
5548 if (FAILED(rc))
5549 {
5550 lastAccessError = Utf8StrFmt(
5551 tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
5552 &parentId, location.c_str(),
5553 m->pVirtualBox->settingsFilePath().c_str());
5554 throw S_OK;
5555 }
5556
5557 /* we set mParent & children() */
5558 treeLock.acquire();
5559
5560 Assert(m->pParent.isNull());
5561 m->pParent = pParent;
5562 m->pParent->m->llChildren.push_back(this);
5563
5564 treeLock.release();
5565 }
5566 else
5567 {
5568 /* we access mParent */
5569 treeLock.acquire();
5570
5571 /* check that parent UUIDs match. Note that there's no need
5572 * for the parent's AutoCaller (our lifetime is bound to
5573 * it) */
5574
5575 if (m->pParent.isNull())
5576 {
5577 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
5578 * and 3.1.0-3.1.8 there are base images out there
5579 * which have a non-zero parent UUID. No point in
5580 * complaining about them, instead automatically
5581 * repair the problem. Later we can bring back the
5582 * error message, but we should wait until really
5583 * most users have repaired their images, either with
5584 * VBoxFixHdd or this way. */
5585#if 1
5586 fRepairImageZeroParentUuid = true;
5587#else /* 0 */
5588 lastAccessError = Utf8StrFmt(
5589 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
5590 location.c_str(),
5591 m->pVirtualBox->settingsFilePath().c_str());
5592 treeLock.release();
5593 throw S_OK;
5594#endif /* 0 */
5595 }
5596
5597 {
5598 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
5599 if ( !fRepairImageZeroParentUuid
5600 && m->pParent->getState() != MediumState_Inaccessible
5601 && m->pParent->getId() != parentId)
5602 {
5603 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
5604 lastAccessError = Utf8StrFmt(
5605 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
5606 &parentId, location.c_str(),
5607 m->pParent->getId().raw(),
5608 m->pVirtualBox->settingsFilePath().c_str());
5609 parentLock.release();
5610 treeLock.release();
5611 throw S_OK;
5612 }
5613 }
5614
5615 /// @todo NEWMEDIA what to do if the parent is not
5616 /// accessible while the diff is? Probably nothing. The
5617 /// real code will detect the mismatch anyway.
5618
5619 treeLock.release();
5620 }
5621 }
5622
5623 mediumSize = VDGetFileSize(hdd, 0);
5624 mediumLogicalSize = VDGetSize(hdd, 0);
5625
5626 success = true;
5627 }
5628 catch (HRESULT aRC)
5629 {
5630 rc = aRC;
5631 }
5632
5633 vrc = VDDestroy(hdd);
5634 if (RT_FAILURE(vrc))
5635 {
5636 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
5637 location.c_str(), vdError(vrc).c_str());
5638 success = false;
5639 throw S_OK;
5640 }
5641 }
5642 catch (HRESULT aRC)
5643 {
5644 rc = aRC;
5645 }
5646
5647 treeLock.acquire();
5648 alock.acquire();
5649
5650 if (isImport || fSetImageId)
5651 unconst(m->id) = mediumId;
5652
5653 if (success)
5654 {
5655 m->size = mediumSize;
5656 m->logicalSize = mediumLogicalSize;
5657 m->strLastAccessError.setNull();
5658 }
5659 else
5660 {
5661 m->strLastAccessError = lastAccessError;
5662 LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
5663 location.c_str(), m->strLastAccessError.c_str(),
5664 rc, vrc));
5665 }
5666
5667 /* unblock anyone waiting for the queryInfo results */
5668 qlock.release();
5669 m->queryInfoRunning = false;
5670
5671 /* Set the proper state according to the result of the check */
5672 if (success)
5673 m->preLockState = MediumState_Created;
5674 else
5675 m->preLockState = MediumState_Inaccessible;
5676
5677 HRESULT rc2;
5678 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
5679 rc2 = UnlockRead(NULL);
5680 else
5681 rc2 = UnlockWrite(NULL);
5682 if (SUCCEEDED(rc) && FAILED(rc2))
5683 rc = rc2;
5684 if (FAILED(rc)) return rc;
5685
5686 /* If this is a base image which incorrectly has a parent UUID set,
5687 * repair the image now by zeroing the parent UUID. This is only done
5688 * when we have structural information from a config file, on import
5689 * this is not possible. If someone would accidentally call openMedium
5690 * with a diff image before the base is registered this would destroy
5691 * the diff. Not acceptable. */
5692 if (fRepairImageZeroParentUuid)
5693 {
5694 rc = LockWrite(NULL);
5695 if (FAILED(rc)) return rc;
5696
5697 alock.release();
5698
5699 try
5700 {
5701 PVBOXHDD hdd;
5702 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
5703 ComAssertRCThrow(vrc, E_FAIL);
5704
5705 try
5706 {
5707 vrc = VDOpen(hdd,
5708 format.c_str(),
5709 location.c_str(),
5710 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
5711 m->vdImageIfaces);
5712 if (RT_FAILURE(vrc))
5713 throw S_OK;
5714
5715 RTUUID zeroParentUuid;
5716 RTUuidClear(&zeroParentUuid);
5717 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
5718 ComAssertRCThrow(vrc, E_FAIL);
5719 }
5720 catch (HRESULT aRC)
5721 {
5722 rc = aRC;
5723 }
5724
5725 VDDestroy(hdd);
5726 }
5727 catch (HRESULT aRC)
5728 {
5729 rc = aRC;
5730 }
5731
5732 rc = UnlockWrite(NULL);
5733 if (SUCCEEDED(rc) && FAILED(rc2))
5734 rc = rc2;
5735 if (FAILED(rc)) return rc;
5736 }
5737
5738 return rc;
5739}
5740
5741/**
5742 * Performs extra checks if the medium can be closed and returns S_OK in
5743 * this case. Otherwise, returns a respective error message. Called by
5744 * Close() under the medium tree lock and the medium lock.
5745 *
5746 * @note Also reused by Medium::Reset().
5747 *
5748 * @note Caller must hold the media tree write lock!
5749 */
5750HRESULT Medium::canClose()
5751{
5752 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5753
5754 if (getChildren().size() != 0)
5755 return setError(VBOX_E_OBJECT_IN_USE,
5756 tr("Cannot close medium '%s' because it has %d child media"),
5757 m->strLocationFull.c_str(), getChildren().size());
5758
5759 return S_OK;
5760}
5761
5762/**
5763 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
5764 *
5765 * @note Caller must have locked the media tree lock for writing!
5766 */
5767HRESULT Medium::unregisterWithVirtualBox()
5768{
5769 /* Note that we need to de-associate ourselves from the parent to let
5770 * unregisterMedium() properly save the registry */
5771
5772 /* we modify mParent and access children */
5773 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5774
5775 Medium *pParentBackup = m->pParent;
5776 AssertReturn(getChildren().size() == 0, E_FAIL);
5777 if (m->pParent)
5778 deparent();
5779
5780 HRESULT rc = m->pVirtualBox->unregisterMedium(this);
5781 if (FAILED(rc))
5782 {
5783 if (pParentBackup)
5784 {
5785 // re-associate with the parent as we are still relatives in the registry
5786 m->pParent = pParentBackup;
5787 m->pParent->m->llChildren.push_back(this);
5788 }
5789 }
5790 m->pVirtualBox->saveModifiedRegistries();
5791
5792 return rc;
5793}
5794
5795/**
5796 * Sets the extended error info according to the current media state.
5797 *
5798 * @note Must be called from under this object's write or read lock.
5799 */
5800HRESULT Medium::setStateError()
5801{
5802 HRESULT rc = E_FAIL;
5803
5804 switch (m->state)
5805 {
5806 case MediumState_NotCreated:
5807 {
5808 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5809 tr("Storage for the medium '%s' is not created"),
5810 m->strLocationFull.c_str());
5811 break;
5812 }
5813 case MediumState_Created:
5814 {
5815 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5816 tr("Storage for the medium '%s' is already created"),
5817 m->strLocationFull.c_str());
5818 break;
5819 }
5820 case MediumState_LockedRead:
5821 {
5822 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5823 tr("Medium '%s' is locked for reading by another task"),
5824 m->strLocationFull.c_str());
5825 break;
5826 }
5827 case MediumState_LockedWrite:
5828 {
5829 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5830 tr("Medium '%s' is locked for writing by another task"),
5831 m->strLocationFull.c_str());
5832 break;
5833 }
5834 case MediumState_Inaccessible:
5835 {
5836 /* be in sync with Console::powerUpThread() */
5837 if (!m->strLastAccessError.isEmpty())
5838 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5839 tr("Medium '%s' is not accessible. %s"),
5840 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
5841 else
5842 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5843 tr("Medium '%s' is not accessible"),
5844 m->strLocationFull.c_str());
5845 break;
5846 }
5847 case MediumState_Creating:
5848 {
5849 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5850 tr("Storage for the medium '%s' is being created"),
5851 m->strLocationFull.c_str());
5852 break;
5853 }
5854 case MediumState_Deleting:
5855 {
5856 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5857 tr("Storage for the medium '%s' is being deleted"),
5858 m->strLocationFull.c_str());
5859 break;
5860 }
5861 default:
5862 {
5863 AssertFailed();
5864 break;
5865 }
5866 }
5867
5868 return rc;
5869}
5870
5871/**
5872 * Sets the value of m->strLocationFull. The given location must be a fully
5873 * qualified path; relative paths are not supported here.
5874 *
5875 * As a special exception, if the specified location is a file path that ends with '/'
5876 * then the file name part will be generated by this method automatically in the format
5877 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
5878 * and assign to this medium, and <ext> is the default extension for this
5879 * medium's storage format. Note that this procedure requires the media state to
5880 * be NotCreated and will return a failure otherwise.
5881 *
5882 * @param aLocation Location of the storage unit. If the location is a FS-path,
5883 * then it can be relative to the VirtualBox home directory.
5884 * @param aFormat Optional fallback format if it is an import and the format
5885 * cannot be determined.
5886 *
5887 * @note Must be called from under this object's write lock.
5888 */
5889HRESULT Medium::setLocation(const Utf8Str &aLocation,
5890 const Utf8Str &aFormat /* = Utf8Str::Empty */)
5891{
5892 AssertReturn(!aLocation.isEmpty(), E_FAIL);
5893
5894 AutoCaller autoCaller(this);
5895 AssertComRCReturnRC(autoCaller.rc());
5896
5897 /* formatObj may be null only when initializing from an existing path and
5898 * no format is known yet */
5899 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
5900 || ( autoCaller.state() == InInit
5901 && m->state != MediumState_NotCreated
5902 && m->id.isEmpty()
5903 && m->strFormat.isEmpty()
5904 && m->formatObj.isNull()),
5905 E_FAIL);
5906
5907 /* are we dealing with a new medium constructed using the existing
5908 * location? */
5909 bool isImport = m->strFormat.isEmpty();
5910
5911 if ( isImport
5912 || ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
5913 && !m->hostDrive))
5914 {
5915 Guid id;
5916
5917 Utf8Str locationFull(aLocation);
5918
5919 if (m->state == MediumState_NotCreated)
5920 {
5921 /* must be a file (formatObj must be already known) */
5922 Assert(m->formatObj->getCapabilities() & MediumFormatCapabilities_File);
5923
5924 if (RTPathFilename(aLocation.c_str()) == NULL)
5925 {
5926 /* no file name is given (either an empty string or ends with a
5927 * slash), generate a new UUID + file name if the state allows
5928 * this */
5929
5930 ComAssertMsgRet(!m->formatObj->getFileExtensions().empty(),
5931 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
5932 E_FAIL);
5933
5934 Utf8Str strExt = m->formatObj->getFileExtensions().front();
5935 ComAssertMsgRet(!strExt.isEmpty(),
5936 ("Default extension must not be empty\n"),
5937 E_FAIL);
5938
5939 id.create();
5940
5941 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
5942 aLocation.c_str(), id.raw(), strExt.c_str());
5943 }
5944 }
5945
5946 // we must always have full paths now (if it refers to a file)
5947 if ( ( m->formatObj.isNull()
5948 || m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
5949 && !RTPathStartsWithRoot(locationFull.c_str()))
5950 return setError(VBOX_E_FILE_ERROR,
5951 tr("The given path '%s' is not fully qualified"),
5952 locationFull.c_str());
5953
5954 /* detect the backend from the storage unit if importing */
5955 if (isImport)
5956 {
5957 VDTYPE enmType = VDTYPE_INVALID;
5958 char *backendName = NULL;
5959
5960 int vrc = VINF_SUCCESS;
5961
5962 /* is it a file? */
5963 {
5964 RTFILE file;
5965 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
5966 if (RT_SUCCESS(vrc))
5967 RTFileClose(file);
5968 }
5969 if (RT_SUCCESS(vrc))
5970 {
5971 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
5972 locationFull.c_str(), &backendName, &enmType);
5973 }
5974 else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
5975 {
5976 /* assume it's not a file, restore the original location */
5977 locationFull = aLocation;
5978 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
5979 locationFull.c_str(), &backendName, &enmType);
5980 }
5981
5982 if (RT_FAILURE(vrc))
5983 {
5984 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
5985 return setError(VBOX_E_FILE_ERROR,
5986 tr("Could not find file for the medium '%s' (%Rrc)"),
5987 locationFull.c_str(), vrc);
5988 else if (aFormat.isEmpty())
5989 return setError(VBOX_E_IPRT_ERROR,
5990 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
5991 locationFull.c_str(), vrc);
5992 else
5993 {
5994 HRESULT rc = setFormat(aFormat);
5995 /* setFormat() must not fail since we've just used the backend so
5996 * the format object must be there */
5997 AssertComRCReturnRC(rc);
5998 }
5999 }
6000 else if ( enmType == VDTYPE_INVALID
6001 || m->devType != convertToDeviceType(enmType))
6002 {
6003 /*
6004 * The user tried to use a image as a device which is not supported
6005 * by the backend.
6006 */
6007 return setError(E_FAIL,
6008 tr("The medium '%s' can't be used as the requested device type"),
6009 locationFull.c_str());
6010 }
6011 else
6012 {
6013 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
6014
6015 HRESULT rc = setFormat(backendName);
6016 RTStrFree(backendName);
6017
6018 /* setFormat() must not fail since we've just used the backend so
6019 * the format object must be there */
6020 AssertComRCReturnRC(rc);
6021 }
6022 }
6023
6024 m->strLocationFull = locationFull;
6025
6026 /* is it still a file? */
6027 if ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
6028 && (m->state == MediumState_NotCreated)
6029 )
6030 /* assign a new UUID (this UUID will be used when calling
6031 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
6032 * also do that if we didn't generate it to make sure it is
6033 * either generated by us or reset to null */
6034 unconst(m->id) = id;
6035 }
6036 else
6037 m->strLocationFull = aLocation;
6038
6039 return S_OK;
6040}
6041
6042/**
6043 * Checks that the format ID is valid and sets it on success.
6044 *
6045 * Note that this method will caller-reference the format object on success!
6046 * This reference must be released somewhere to let the MediumFormat object be
6047 * uninitialized.
6048 *
6049 * @note Must be called from under this object's write lock.
6050 */
6051HRESULT Medium::setFormat(const Utf8Str &aFormat)
6052{
6053 /* get the format object first */
6054 {
6055 SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
6056 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
6057
6058 unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
6059 if (m->formatObj.isNull())
6060 return setError(E_INVALIDARG,
6061 tr("Invalid medium storage format '%s'"),
6062 aFormat.c_str());
6063
6064 /* reference the format permanently to prevent its unexpected
6065 * uninitialization */
6066 HRESULT rc = m->formatObj->addCaller();
6067 AssertComRCReturnRC(rc);
6068
6069 /* get properties (preinsert them as keys in the map). Note that the
6070 * map doesn't grow over the object life time since the set of
6071 * properties is meant to be constant. */
6072
6073 Assert(m->mapProperties.empty());
6074
6075 for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
6076 it != m->formatObj->getProperties().end();
6077 ++it)
6078 {
6079 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
6080 }
6081 }
6082
6083 unconst(m->strFormat) = aFormat;
6084
6085 return S_OK;
6086}
6087
6088/**
6089 * Converts the Medium device type to the VD type.
6090 */
6091VDTYPE Medium::convertDeviceType()
6092{
6093 VDTYPE enmType;
6094
6095 switch (m->devType)
6096 {
6097 case DeviceType_HardDisk:
6098 enmType = VDTYPE_HDD;
6099 break;
6100 case DeviceType_DVD:
6101 enmType = VDTYPE_DVD;
6102 break;
6103 case DeviceType_Floppy:
6104 enmType = VDTYPE_FLOPPY;
6105 break;
6106 default:
6107 ComAssertFailedRet(VDTYPE_INVALID);
6108 }
6109
6110 return enmType;
6111}
6112
6113/**
6114 * Converts from the VD type to the medium type.
6115 */
6116DeviceType_T Medium::convertToDeviceType(VDTYPE enmType)
6117{
6118 DeviceType_T devType;
6119
6120 switch (enmType)
6121 {
6122 case VDTYPE_HDD:
6123 devType = DeviceType_HardDisk;
6124 break;
6125 case VDTYPE_DVD:
6126 devType = DeviceType_DVD;
6127 break;
6128 case VDTYPE_FLOPPY:
6129 devType = DeviceType_Floppy;
6130 break;
6131 default:
6132 ComAssertFailedRet(DeviceType_Null);
6133 }
6134
6135 return devType;
6136}
6137
6138/**
6139 * Returns the last error message collected by the vdErrorCall callback and
6140 * resets it.
6141 *
6142 * The error message is returned prepended with a dot and a space, like this:
6143 * <code>
6144 * ". <error_text> (%Rrc)"
6145 * </code>
6146 * to make it easily appendable to a more general error message. The @c %Rrc
6147 * format string is given @a aVRC as an argument.
6148 *
6149 * If there is no last error message collected by vdErrorCall or if it is a
6150 * null or empty string, then this function returns the following text:
6151 * <code>
6152 * " (%Rrc)"
6153 * </code>
6154 *
6155 * @note Doesn't do any object locking; it is assumed that the caller makes sure
6156 * the callback isn't called by more than one thread at a time.
6157 *
6158 * @param aVRC VBox error code to use when no error message is provided.
6159 */
6160Utf8Str Medium::vdError(int aVRC)
6161{
6162 Utf8Str error;
6163
6164 if (m->vdError.isEmpty())
6165 error = Utf8StrFmt(" (%Rrc)", aVRC);
6166 else
6167 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
6168
6169 m->vdError.setNull();
6170
6171 return error;
6172}
6173
6174/**
6175 * Error message callback.
6176 *
6177 * Puts the reported error message to the m->vdError field.
6178 *
6179 * @note Doesn't do any object locking; it is assumed that the caller makes sure
6180 * the callback isn't called by more than one thread at a time.
6181 *
6182 * @param pvUser The opaque data passed on container creation.
6183 * @param rc The VBox error code.
6184 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
6185 * @param pszFormat Error message format string.
6186 * @param va Error message arguments.
6187 */
6188/*static*/
6189DECLCALLBACK(void) Medium::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
6190 const char *pszFormat, va_list va)
6191{
6192 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
6193
6194 Medium *that = static_cast<Medium*>(pvUser);
6195 AssertReturnVoid(that != NULL);
6196
6197 if (that->m->vdError.isEmpty())
6198 that->m->vdError =
6199 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
6200 else
6201 that->m->vdError =
6202 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
6203 Utf8Str(pszFormat, va).c_str(), rc);
6204}
6205
6206/* static */
6207DECLCALLBACK(bool) Medium::vdConfigAreKeysValid(void *pvUser,
6208 const char * /* pszzValid */)
6209{
6210 Medium *that = static_cast<Medium*>(pvUser);
6211 AssertReturn(that != NULL, false);
6212
6213 /* we always return true since the only keys we have are those found in
6214 * VDBACKENDINFO */
6215 return true;
6216}
6217
6218/* static */
6219DECLCALLBACK(int) Medium::vdConfigQuerySize(void *pvUser,
6220 const char *pszName,
6221 size_t *pcbValue)
6222{
6223 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
6224
6225 Medium *that = static_cast<Medium*>(pvUser);
6226 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
6227
6228 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
6229 if (it == that->m->mapProperties.end())
6230 return VERR_CFGM_VALUE_NOT_FOUND;
6231
6232 /* we interpret null values as "no value" in Medium */
6233 if (it->second.isEmpty())
6234 return VERR_CFGM_VALUE_NOT_FOUND;
6235
6236 *pcbValue = it->second.length() + 1 /* include terminator */;
6237
6238 return VINF_SUCCESS;
6239}
6240
6241/* static */
6242DECLCALLBACK(int) Medium::vdConfigQuery(void *pvUser,
6243 const char *pszName,
6244 char *pszValue,
6245 size_t cchValue)
6246{
6247 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
6248
6249 Medium *that = static_cast<Medium*>(pvUser);
6250 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
6251
6252 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
6253 if (it == that->m->mapProperties.end())
6254 return VERR_CFGM_VALUE_NOT_FOUND;
6255
6256 /* we interpret null values as "no value" in Medium */
6257 if (it->second.isEmpty())
6258 return VERR_CFGM_VALUE_NOT_FOUND;
6259
6260 const Utf8Str &value = it->second;
6261 if (value.length() >= cchValue)
6262 return VERR_CFGM_NOT_ENOUGH_SPACE;
6263
6264 memcpy(pszValue, value.c_str(), value.length() + 1);
6265
6266 return VINF_SUCCESS;
6267}
6268
6269DECLCALLBACK(int) Medium::vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
6270{
6271 PVDSOCKETINT pSocketInt = NULL;
6272
6273 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
6274 return VERR_NOT_SUPPORTED;
6275
6276 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
6277 if (!pSocketInt)
6278 return VERR_NO_MEMORY;
6279
6280 pSocketInt->hSocket = NIL_RTSOCKET;
6281 *pSock = pSocketInt;
6282 return VINF_SUCCESS;
6283}
6284
6285DECLCALLBACK(int) Medium::vdTcpSocketDestroy(VDSOCKET Sock)
6286{
6287 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6288
6289 if (pSocketInt->hSocket != NIL_RTSOCKET)
6290 RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
6291
6292 RTMemFree(pSocketInt);
6293
6294 return VINF_SUCCESS;
6295}
6296
6297DECLCALLBACK(int) Medium::vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
6298{
6299 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6300
6301 return RTTcpClientConnect(pszAddress, uPort, &pSocketInt->hSocket);
6302}
6303
6304DECLCALLBACK(int) Medium::vdTcpClientClose(VDSOCKET Sock)
6305{
6306 int rc = VINF_SUCCESS;
6307 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6308
6309 rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
6310 pSocketInt->hSocket = NIL_RTSOCKET;
6311 return rc;
6312}
6313
6314DECLCALLBACK(bool) Medium::vdTcpIsClientConnected(VDSOCKET Sock)
6315{
6316 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6317 return pSocketInt->hSocket != NIL_RTSOCKET;
6318}
6319
6320DECLCALLBACK(int) Medium::vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
6321{
6322 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6323 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
6324}
6325
6326DECLCALLBACK(int) Medium::vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
6327{
6328 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6329 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
6330}
6331
6332DECLCALLBACK(int) Medium::vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
6333{
6334 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6335 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
6336}
6337
6338DECLCALLBACK(int) Medium::vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
6339{
6340 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6341 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
6342}
6343
6344DECLCALLBACK(int) Medium::vdTcpFlush(VDSOCKET Sock)
6345{
6346 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6347 return RTTcpFlush(pSocketInt->hSocket);
6348}
6349
6350DECLCALLBACK(int) Medium::vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
6351{
6352 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6353 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
6354}
6355
6356DECLCALLBACK(int) Medium::vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
6357{
6358 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6359 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
6360}
6361
6362DECLCALLBACK(int) Medium::vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
6363{
6364 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6365 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
6366}
6367
6368/**
6369 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
6370 *
6371 * @note When the task is executed by this method, IProgress::notifyComplete()
6372 * is automatically called for the progress object associated with this
6373 * task when the task is finished to signal the operation completion for
6374 * other threads asynchronously waiting for it.
6375 */
6376HRESULT Medium::startThread(Medium::Task *pTask)
6377{
6378#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
6379 /* Extreme paranoia: The calling thread should not hold the medium
6380 * tree lock or any medium lock. Since there is no separate lock class
6381 * for medium objects be even more strict: no other object locks. */
6382 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
6383 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
6384#endif
6385
6386 /// @todo use a more descriptive task name
6387 int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
6388 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
6389 "Medium::Task");
6390 if (RT_FAILURE(vrc))
6391 {
6392 delete pTask;
6393 return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n", vrc);
6394 }
6395
6396 return S_OK;
6397}
6398
6399/**
6400 * Runs Medium::Task::handler() on the current thread instead of creating
6401 * a new one.
6402 *
6403 * This call implies that it is made on another temporary thread created for
6404 * some asynchronous task. Avoid calling it from a normal thread since the task
6405 * operations are potentially lengthy and will block the calling thread in this
6406 * case.
6407 *
6408 * @note When the task is executed by this method, IProgress::notifyComplete()
6409 * is not called for the progress object associated with this task when
6410 * the task is finished. Instead, the result of the operation is returned
6411 * by this method directly and it's the caller's responsibility to
6412 * complete the progress object in this case.
6413 */
6414HRESULT Medium::runNow(Medium::Task *pTask)
6415{
6416#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
6417 /* Extreme paranoia: The calling thread should not hold the medium
6418 * tree lock or any medium lock. Since there is no separate lock class
6419 * for medium objects be even more strict: no other object locks. */
6420 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
6421 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
6422#endif
6423
6424 /* NIL_RTTHREAD indicates synchronous call. */
6425 return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
6426}
6427
6428/**
6429 * Implementation code for the "create base" task.
6430 *
6431 * This only gets started from Medium::CreateBaseStorage() and always runs
6432 * asynchronously. As a result, we always save the VirtualBox.xml file when
6433 * we're done here.
6434 *
6435 * @param task
6436 * @return
6437 */
6438HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
6439{
6440 HRESULT rc = S_OK;
6441
6442 /* these parameters we need after creation */
6443 uint64_t size = 0, logicalSize = 0;
6444 MediumVariant_T variant = MediumVariant_Standard;
6445 bool fGenerateUuid = false;
6446
6447 try
6448 {
6449 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6450
6451 /* The object may request a specific UUID (through a special form of
6452 * the setLocation() argument). Otherwise we have to generate it */
6453 Guid id = m->id;
6454 fGenerateUuid = id.isEmpty();
6455 if (fGenerateUuid)
6456 {
6457 id.create();
6458 /* VirtualBox::registerMedium() will need UUID */
6459 unconst(m->id) = id;
6460 }
6461
6462 Utf8Str format(m->strFormat);
6463 Utf8Str location(m->strLocationFull);
6464 uint64_t capabilities = m->formatObj->getCapabilities();
6465 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
6466 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
6467 Assert(m->state == MediumState_Creating);
6468
6469 PVBOXHDD hdd;
6470 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6471 ComAssertRCThrow(vrc, E_FAIL);
6472
6473 /* unlock before the potentially lengthy operation */
6474 thisLock.release();
6475
6476 try
6477 {
6478 /* ensure the directory exists */
6479 if (capabilities & MediumFormatCapabilities_File)
6480 {
6481 rc = VirtualBox::ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
6482 if (FAILED(rc))
6483 throw rc;
6484 }
6485
6486 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
6487
6488 vrc = VDCreateBase(hdd,
6489 format.c_str(),
6490 location.c_str(),
6491 task.mSize,
6492 task.mVariant & ~MediumVariant_NoCreateDir,
6493 NULL,
6494 &geo,
6495 &geo,
6496 id.raw(),
6497 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
6498 m->vdImageIfaces,
6499 task.mVDOperationIfaces);
6500 if (RT_FAILURE(vrc))
6501 throw setError(VBOX_E_FILE_ERROR,
6502 tr("Could not create the medium storage unit '%s'%s"),
6503 location.c_str(), vdError(vrc).c_str());
6504
6505 size = VDGetFileSize(hdd, 0);
6506 logicalSize = VDGetSize(hdd, 0);
6507 unsigned uImageFlags;
6508 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6509 if (RT_SUCCESS(vrc))
6510 variant = (MediumVariant_T)uImageFlags;
6511 }
6512 catch (HRESULT aRC) { rc = aRC; }
6513
6514 VDDestroy(hdd);
6515 }
6516 catch (HRESULT aRC) { rc = aRC; }
6517
6518 if (SUCCEEDED(rc))
6519 {
6520 /* register with mVirtualBox as the last step and move to
6521 * Created state only on success (leaving an orphan file is
6522 * better than breaking media registry consistency) */
6523 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6524 ComObjPtr<Medium> pMedium;
6525 rc = m->pVirtualBox->registerMedium(this, &pMedium, DeviceType_HardDisk);
6526 Assert(this == pMedium);
6527 }
6528
6529 // re-acquire the lock before changing state
6530 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 if (SUCCEEDED(rc))
6533 {
6534 m->state = MediumState_Created;
6535
6536 m->size = size;
6537 m->logicalSize = logicalSize;
6538 m->variant = variant;
6539
6540 thisLock.release();
6541 markRegistriesModified();
6542 if (task.isAsync())
6543 {
6544 // in asynchronous mode, save settings now
6545 m->pVirtualBox->saveModifiedRegistries();
6546 }
6547 }
6548 else
6549 {
6550 /* back to NotCreated on failure */
6551 m->state = MediumState_NotCreated;
6552
6553 /* reset UUID to prevent it from being reused next time */
6554 if (fGenerateUuid)
6555 unconst(m->id).clear();
6556 }
6557
6558 return rc;
6559}
6560
6561/**
6562 * Implementation code for the "create diff" task.
6563 *
6564 * This task always gets started from Medium::createDiffStorage() and can run
6565 * synchronously or asynchronously depending on the "wait" parameter passed to
6566 * that function. If we run synchronously, the caller expects the medium
6567 * registry modification to be set before returning; otherwise (in asynchronous
6568 * mode), we save the settings ourselves.
6569 *
6570 * @param task
6571 * @return
6572 */
6573HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
6574{
6575 HRESULT rcTmp = S_OK;
6576
6577 const ComObjPtr<Medium> &pTarget = task.mTarget;
6578
6579 uint64_t size = 0, logicalSize = 0;
6580 MediumVariant_T variant = MediumVariant_Standard;
6581 bool fGenerateUuid = false;
6582
6583 try
6584 {
6585 /* Lock both in {parent,child} order. */
6586 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6587
6588 /* The object may request a specific UUID (through a special form of
6589 * the setLocation() argument). Otherwise we have to generate it */
6590 Guid targetId = pTarget->m->id;
6591 fGenerateUuid = targetId.isEmpty();
6592 if (fGenerateUuid)
6593 {
6594 targetId.create();
6595 /* VirtualBox::registerMedium() will need UUID */
6596 unconst(pTarget->m->id) = targetId;
6597 }
6598
6599 Guid id = m->id;
6600
6601 Utf8Str targetFormat(pTarget->m->strFormat);
6602 Utf8Str targetLocation(pTarget->m->strLocationFull);
6603 uint64_t capabilities = pTarget->m->formatObj->getCapabilities();
6604 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
6605
6606 Assert(pTarget->m->state == MediumState_Creating);
6607 Assert(m->state == MediumState_LockedRead);
6608
6609 PVBOXHDD hdd;
6610 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6611 ComAssertRCThrow(vrc, E_FAIL);
6612
6613 /* the two media are now protected by their non-default states;
6614 * unlock the media before the potentially lengthy operation */
6615 mediaLock.release();
6616
6617 try
6618 {
6619 /* Open all media in the target chain but the last. */
6620 MediumLockList::Base::const_iterator targetListBegin =
6621 task.mpMediumLockList->GetBegin();
6622 MediumLockList::Base::const_iterator targetListEnd =
6623 task.mpMediumLockList->GetEnd();
6624 for (MediumLockList::Base::const_iterator it = targetListBegin;
6625 it != targetListEnd;
6626 ++it)
6627 {
6628 const MediumLock &mediumLock = *it;
6629 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6630
6631 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6632
6633 /* Skip over the target diff medium */
6634 if (pMedium->m->state == MediumState_Creating)
6635 continue;
6636
6637 /* sanity check */
6638 Assert(pMedium->m->state == MediumState_LockedRead);
6639
6640 /* Open all media in appropriate mode. */
6641 vrc = VDOpen(hdd,
6642 pMedium->m->strFormat.c_str(),
6643 pMedium->m->strLocationFull.c_str(),
6644 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
6645 pMedium->m->vdImageIfaces);
6646 if (RT_FAILURE(vrc))
6647 throw setError(VBOX_E_FILE_ERROR,
6648 tr("Could not open the medium storage unit '%s'%s"),
6649 pMedium->m->strLocationFull.c_str(),
6650 vdError(vrc).c_str());
6651 }
6652
6653 /* ensure the target directory exists */
6654 if (capabilities & MediumFormatCapabilities_File)
6655 {
6656 HRESULT rc = VirtualBox::ensureFilePathExists(targetLocation, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
6657 if (FAILED(rc))
6658 throw rc;
6659 }
6660
6661 vrc = VDCreateDiff(hdd,
6662 targetFormat.c_str(),
6663 targetLocation.c_str(),
6664 (task.mVariant & ~MediumVariant_NoCreateDir) | VD_IMAGE_FLAGS_DIFF,
6665 NULL,
6666 targetId.raw(),
6667 id.raw(),
6668 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
6669 pTarget->m->vdImageIfaces,
6670 task.mVDOperationIfaces);
6671 if (RT_FAILURE(vrc))
6672 throw setError(VBOX_E_FILE_ERROR,
6673 tr("Could not create the differencing medium storage unit '%s'%s"),
6674 targetLocation.c_str(), vdError(vrc).c_str());
6675
6676 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
6677 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
6678 unsigned uImageFlags;
6679 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6680 if (RT_SUCCESS(vrc))
6681 variant = (MediumVariant_T)uImageFlags;
6682 }
6683 catch (HRESULT aRC) { rcTmp = aRC; }
6684
6685 VDDestroy(hdd);
6686 }
6687 catch (HRESULT aRC) { rcTmp = aRC; }
6688
6689 MultiResult mrc(rcTmp);
6690
6691 if (SUCCEEDED(mrc))
6692 {
6693 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6694
6695 Assert(pTarget->m->pParent.isNull());
6696
6697 /* associate the child with the parent */
6698 pTarget->m->pParent = this;
6699 m->llChildren.push_back(pTarget);
6700
6701 /** @todo r=klaus neither target nor base() are locked,
6702 * potential race! */
6703 /* diffs for immutable media are auto-reset by default */
6704 pTarget->m->autoReset = (getBase()->m->type == MediumType_Immutable);
6705
6706 /* register with mVirtualBox as the last step and move to
6707 * Created state only on success (leaving an orphan file is
6708 * better than breaking media registry consistency) */
6709 ComObjPtr<Medium> pMedium;
6710 mrc = m->pVirtualBox->registerMedium(pTarget, &pMedium, DeviceType_HardDisk);
6711 Assert(pTarget == pMedium);
6712
6713 if (FAILED(mrc))
6714 /* break the parent association on failure to register */
6715 deparent();
6716 }
6717
6718 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6719
6720 if (SUCCEEDED(mrc))
6721 {
6722 pTarget->m->state = MediumState_Created;
6723
6724 pTarget->m->size = size;
6725 pTarget->m->logicalSize = logicalSize;
6726 pTarget->m->variant = variant;
6727 }
6728 else
6729 {
6730 /* back to NotCreated on failure */
6731 pTarget->m->state = MediumState_NotCreated;
6732
6733 pTarget->m->autoReset = false;
6734
6735 /* reset UUID to prevent it from being reused next time */
6736 if (fGenerateUuid)
6737 unconst(pTarget->m->id).clear();
6738 }
6739
6740 // deregister the task registered in createDiffStorage()
6741 Assert(m->numCreateDiffTasks != 0);
6742 --m->numCreateDiffTasks;
6743
6744 mediaLock.release();
6745 markRegistriesModified();
6746 if (task.isAsync())
6747 {
6748 // in asynchronous mode, save settings now
6749 m->pVirtualBox->saveModifiedRegistries();
6750 }
6751
6752 /* Note that in sync mode, it's the caller's responsibility to
6753 * unlock the medium. */
6754
6755 return mrc;
6756}
6757
6758/**
6759 * Implementation code for the "merge" task.
6760 *
6761 * This task always gets started from Medium::mergeTo() and can run
6762 * synchronously or asynchronously depending on the "wait" parameter passed to
6763 * that function. If we run synchronously, the caller expects the medium
6764 * registry modification to be set before returning; otherwise (in asynchronous
6765 * mode), we save the settings ourselves.
6766 *
6767 * @param task
6768 * @return
6769 */
6770HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
6771{
6772 HRESULT rcTmp = S_OK;
6773
6774 const ComObjPtr<Medium> &pTarget = task.mTarget;
6775
6776 try
6777 {
6778 PVBOXHDD hdd;
6779 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6780 ComAssertRCThrow(vrc, E_FAIL);
6781
6782 try
6783 {
6784 // Similar code appears in SessionMachine::onlineMergeMedium, so
6785 // if you make any changes below check whether they are applicable
6786 // in that context as well.
6787
6788 unsigned uTargetIdx = VD_LAST_IMAGE;
6789 unsigned uSourceIdx = VD_LAST_IMAGE;
6790 /* Open all media in the chain. */
6791 MediumLockList::Base::iterator lockListBegin =
6792 task.mpMediumLockList->GetBegin();
6793 MediumLockList::Base::iterator lockListEnd =
6794 task.mpMediumLockList->GetEnd();
6795 unsigned i = 0;
6796 for (MediumLockList::Base::iterator it = lockListBegin;
6797 it != lockListEnd;
6798 ++it)
6799 {
6800 MediumLock &mediumLock = *it;
6801 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6802
6803 if (pMedium == this)
6804 uSourceIdx = i;
6805 else if (pMedium == pTarget)
6806 uTargetIdx = i;
6807
6808 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6809
6810 /*
6811 * complex sanity (sane complexity)
6812 *
6813 * The current medium must be in the Deleting (medium is merged)
6814 * or LockedRead (parent medium) state if it is not the target.
6815 * If it is the target it must be in the LockedWrite state.
6816 */
6817 Assert( ( pMedium != pTarget
6818 && ( pMedium->m->state == MediumState_Deleting
6819 || pMedium->m->state == MediumState_LockedRead))
6820 || ( pMedium == pTarget
6821 && pMedium->m->state == MediumState_LockedWrite));
6822
6823 /*
6824 * Medium must be the target, in the LockedRead state
6825 * or Deleting state where it is not allowed to be attached
6826 * to a virtual machine.
6827 */
6828 Assert( pMedium == pTarget
6829 || pMedium->m->state == MediumState_LockedRead
6830 || ( pMedium->m->backRefs.size() == 0
6831 && pMedium->m->state == MediumState_Deleting));
6832 /* The source medium must be in Deleting state. */
6833 Assert( pMedium != this
6834 || pMedium->m->state == MediumState_Deleting);
6835
6836 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
6837
6838 if ( pMedium->m->state == MediumState_LockedRead
6839 || pMedium->m->state == MediumState_Deleting)
6840 uOpenFlags = VD_OPEN_FLAGS_READONLY;
6841 if (pMedium->m->type == MediumType_Shareable)
6842 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6843
6844 /* Open the medium */
6845 vrc = VDOpen(hdd,
6846 pMedium->m->strFormat.c_str(),
6847 pMedium->m->strLocationFull.c_str(),
6848 uOpenFlags | m->uOpenFlagsDef,
6849 pMedium->m->vdImageIfaces);
6850 if (RT_FAILURE(vrc))
6851 throw vrc;
6852
6853 i++;
6854 }
6855
6856 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
6857 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
6858
6859 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
6860 task.mVDOperationIfaces);
6861 if (RT_FAILURE(vrc))
6862 throw vrc;
6863
6864 /* update parent UUIDs */
6865 if (!task.mfMergeForward)
6866 {
6867 /* we need to update UUIDs of all source's children
6868 * which cannot be part of the container at once so
6869 * add each one in there individually */
6870 if (task.mChildrenToReparent.size() > 0)
6871 {
6872 for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
6873 it != task.mChildrenToReparent.end();
6874 ++it)
6875 {
6876 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
6877 vrc = VDOpen(hdd,
6878 (*it)->m->strFormat.c_str(),
6879 (*it)->m->strLocationFull.c_str(),
6880 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
6881 (*it)->m->vdImageIfaces);
6882 if (RT_FAILURE(vrc))
6883 throw vrc;
6884
6885 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
6886 pTarget->m->id.raw());
6887 if (RT_FAILURE(vrc))
6888 throw vrc;
6889
6890 vrc = VDClose(hdd, false /* fDelete */);
6891 if (RT_FAILURE(vrc))
6892 throw vrc;
6893
6894 (*it)->UnlockWrite(NULL);
6895 }
6896 }
6897 }
6898 }
6899 catch (HRESULT aRC) { rcTmp = aRC; }
6900 catch (int aVRC)
6901 {
6902 rcTmp = setError(VBOX_E_FILE_ERROR,
6903 tr("Could not merge the medium '%s' to '%s'%s"),
6904 m->strLocationFull.c_str(),
6905 pTarget->m->strLocationFull.c_str(),
6906 vdError(aVRC).c_str());
6907 }
6908
6909 VDDestroy(hdd);
6910 }
6911 catch (HRESULT aRC) { rcTmp = aRC; }
6912
6913 ErrorInfoKeeper eik;
6914 MultiResult mrc(rcTmp);
6915 HRESULT rc2;
6916
6917 if (SUCCEEDED(mrc))
6918 {
6919 /* all media but the target were successfully deleted by
6920 * VDMerge; reparent the last one and uninitialize deleted media. */
6921
6922 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6923
6924 if (task.mfMergeForward)
6925 {
6926 /* first, unregister the target since it may become a base
6927 * medium which needs re-registration */
6928 rc2 = m->pVirtualBox->unregisterMedium(pTarget);
6929 AssertComRC(rc2);
6930
6931 /* then, reparent it and disconnect the deleted branch at
6932 * both ends (chain->parent() is source's parent) */
6933 pTarget->deparent();
6934 pTarget->m->pParent = task.mParentForTarget;
6935 if (pTarget->m->pParent)
6936 {
6937 pTarget->m->pParent->m->llChildren.push_back(pTarget);
6938 deparent();
6939 }
6940
6941 /* then, register again */
6942 ComObjPtr<Medium> pMedium;
6943 rc2 = m->pVirtualBox->registerMedium(pTarget, &pMedium,
6944 DeviceType_HardDisk);
6945 AssertComRC(rc2);
6946 }
6947 else
6948 {
6949 Assert(pTarget->getChildren().size() == 1);
6950 Medium *targetChild = pTarget->getChildren().front();
6951
6952 /* disconnect the deleted branch at the elder end */
6953 targetChild->deparent();
6954
6955 /* reparent source's children and disconnect the deleted
6956 * branch at the younger end */
6957 if (task.mChildrenToReparent.size() > 0)
6958 {
6959 /* obey {parent,child} lock order */
6960 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
6961
6962 for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
6963 it != task.mChildrenToReparent.end();
6964 it++)
6965 {
6966 Medium *pMedium = *it;
6967 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
6968
6969 pMedium->deparent(); // removes pMedium from source
6970 pMedium->setParent(pTarget);
6971 }
6972 }
6973 }
6974
6975 /* unregister and uninitialize all media removed by the merge */
6976 MediumLockList::Base::iterator lockListBegin =
6977 task.mpMediumLockList->GetBegin();
6978 MediumLockList::Base::iterator lockListEnd =
6979 task.mpMediumLockList->GetEnd();
6980 for (MediumLockList::Base::iterator it = lockListBegin;
6981 it != lockListEnd;
6982 )
6983 {
6984 MediumLock &mediumLock = *it;
6985 /* Create a real copy of the medium pointer, as the medium
6986 * lock deletion below would invalidate the referenced object. */
6987 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
6988
6989 /* The target and all media not merged (readonly) are skipped */
6990 if ( pMedium == pTarget
6991 || pMedium->m->state == MediumState_LockedRead)
6992 {
6993 ++it;
6994 continue;
6995 }
6996
6997 rc2 = pMedium->m->pVirtualBox->unregisterMedium(pMedium);
6998 AssertComRC(rc2);
6999
7000 /* now, uninitialize the deleted medium (note that
7001 * due to the Deleting state, uninit() will not touch
7002 * the parent-child relationship so we need to
7003 * uninitialize each disk individually) */
7004
7005 /* note that the operation initiator medium (which is
7006 * normally also the source medium) is a special case
7007 * -- there is one more caller added by Task to it which
7008 * we must release. Also, if we are in sync mode, the
7009 * caller may still hold an AutoCaller instance for it
7010 * and therefore we cannot uninit() it (it's therefore
7011 * the caller's responsibility) */
7012 if (pMedium == this)
7013 {
7014 Assert(getChildren().size() == 0);
7015 Assert(m->backRefs.size() == 0);
7016 task.mMediumCaller.release();
7017 }
7018
7019 /* Delete the medium lock list entry, which also releases the
7020 * caller added by MergeChain before uninit() and updates the
7021 * iterator to point to the right place. */
7022 rc2 = task.mpMediumLockList->RemoveByIterator(it);
7023 AssertComRC(rc2);
7024
7025 if (task.isAsync() || pMedium != this)
7026 pMedium->uninit();
7027 }
7028 }
7029
7030 markRegistriesModified();
7031 if (task.isAsync())
7032 {
7033 // in asynchronous mode, save settings now
7034 eik.restore();
7035 m->pVirtualBox->saveModifiedRegistries();
7036 eik.fetch();
7037 }
7038
7039 if (FAILED(mrc))
7040 {
7041 /* Here we come if either VDMerge() failed (in which case we
7042 * assume that it tried to do everything to make a further
7043 * retry possible -- e.g. not deleted intermediate media
7044 * and so on) or VirtualBox::saveRegistries() failed (where we
7045 * should have the original tree but with intermediate storage
7046 * units deleted by VDMerge()). We have to only restore states
7047 * (through the MergeChain dtor) unless we are run synchronously
7048 * in which case it's the responsibility of the caller as stated
7049 * in the mergeTo() docs. The latter also implies that we
7050 * don't own the merge chain, so release it in this case. */
7051 if (task.isAsync())
7052 {
7053 Assert(task.mChildrenToReparent.size() == 0);
7054 cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
7055 }
7056 }
7057
7058 return mrc;
7059}
7060
7061/**
7062 * Implementation code for the "clone" task.
7063 *
7064 * This only gets started from Medium::CloneTo() and always runs asynchronously.
7065 * As a result, we always save the VirtualBox.xml file when we're done here.
7066 *
7067 * @param task
7068 * @return
7069 */
7070HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
7071{
7072 HRESULT rcTmp = S_OK;
7073
7074 const ComObjPtr<Medium> &pTarget = task.mTarget;
7075 const ComObjPtr<Medium> &pParent = task.mParent;
7076
7077 bool fCreatingTarget = false;
7078
7079 uint64_t size = 0, logicalSize = 0;
7080 MediumVariant_T variant = MediumVariant_Standard;
7081 bool fGenerateUuid = false;
7082
7083 try
7084 {
7085 /* Lock all in {parent,child} order. The lock is also used as a
7086 * signal from the task initiator (which releases it only after
7087 * RTThreadCreate()) that we can start the job. */
7088 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
7089
7090 fCreatingTarget = pTarget->m->state == MediumState_Creating;
7091
7092 /* The object may request a specific UUID (through a special form of
7093 * the setLocation() argument). Otherwise we have to generate it */
7094 Guid targetId = pTarget->m->id;
7095 fGenerateUuid = targetId.isEmpty();
7096 if (fGenerateUuid)
7097 {
7098 targetId.create();
7099 /* VirtualBox::registerMedium() will need UUID */
7100 unconst(pTarget->m->id) = targetId;
7101 }
7102
7103 PVBOXHDD hdd;
7104 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7105 ComAssertRCThrow(vrc, E_FAIL);
7106
7107 try
7108 {
7109 /* Open all media in the source chain. */
7110 MediumLockList::Base::const_iterator sourceListBegin =
7111 task.mpSourceMediumLockList->GetBegin();
7112 MediumLockList::Base::const_iterator sourceListEnd =
7113 task.mpSourceMediumLockList->GetEnd();
7114 for (MediumLockList::Base::const_iterator it = sourceListBegin;
7115 it != sourceListEnd;
7116 ++it)
7117 {
7118 const MediumLock &mediumLock = *it;
7119 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7120 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7121
7122 /* sanity check */
7123 Assert(pMedium->m->state == MediumState_LockedRead);
7124
7125 /** Open all media in read-only mode. */
7126 vrc = VDOpen(hdd,
7127 pMedium->m->strFormat.c_str(),
7128 pMedium->m->strLocationFull.c_str(),
7129 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
7130 pMedium->m->vdImageIfaces);
7131 if (RT_FAILURE(vrc))
7132 throw setError(VBOX_E_FILE_ERROR,
7133 tr("Could not open the medium storage unit '%s'%s"),
7134 pMedium->m->strLocationFull.c_str(),
7135 vdError(vrc).c_str());
7136 }
7137
7138 Utf8Str targetFormat(pTarget->m->strFormat);
7139 Utf8Str targetLocation(pTarget->m->strLocationFull);
7140 uint64_t capabilities = pTarget->m->formatObj->getCapabilities();
7141
7142 Assert( pTarget->m->state == MediumState_Creating
7143 || pTarget->m->state == MediumState_LockedWrite);
7144 Assert(m->state == MediumState_LockedRead);
7145 Assert( pParent.isNull()
7146 || pParent->m->state == MediumState_LockedRead);
7147
7148 /* unlock before the potentially lengthy operation */
7149 thisLock.release();
7150
7151 /* ensure the target directory exists */
7152 if (capabilities & MediumFormatCapabilities_File)
7153 {
7154 HRESULT rc = VirtualBox::ensureFilePathExists(targetLocation, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
7155 if (FAILED(rc))
7156 throw rc;
7157 }
7158
7159 PVBOXHDD targetHdd;
7160 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
7161 ComAssertRCThrow(vrc, E_FAIL);
7162
7163 try
7164 {
7165 /* Open all media in the target chain. */
7166 MediumLockList::Base::const_iterator targetListBegin =
7167 task.mpTargetMediumLockList->GetBegin();
7168 MediumLockList::Base::const_iterator targetListEnd =
7169 task.mpTargetMediumLockList->GetEnd();
7170 for (MediumLockList::Base::const_iterator it = targetListBegin;
7171 it != targetListEnd;
7172 ++it)
7173 {
7174 const MediumLock &mediumLock = *it;
7175 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7176
7177 /* If the target medium is not created yet there's no
7178 * reason to open it. */
7179 if (pMedium == pTarget && fCreatingTarget)
7180 continue;
7181
7182 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7183
7184 /* sanity check */
7185 Assert( pMedium->m->state == MediumState_LockedRead
7186 || pMedium->m->state == MediumState_LockedWrite);
7187
7188 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7189 if (pMedium->m->state != MediumState_LockedWrite)
7190 uOpenFlags = VD_OPEN_FLAGS_READONLY;
7191 if (pMedium->m->type == MediumType_Shareable)
7192 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7193
7194 /* Open all media in appropriate mode. */
7195 vrc = VDOpen(targetHdd,
7196 pMedium->m->strFormat.c_str(),
7197 pMedium->m->strLocationFull.c_str(),
7198 uOpenFlags | m->uOpenFlagsDef,
7199 pMedium->m->vdImageIfaces);
7200 if (RT_FAILURE(vrc))
7201 throw setError(VBOX_E_FILE_ERROR,
7202 tr("Could not open the medium storage unit '%s'%s"),
7203 pMedium->m->strLocationFull.c_str(),
7204 vdError(vrc).c_str());
7205 }
7206
7207 /** @todo r=klaus target isn't locked, race getting the state */
7208 if (task.midxSrcImageSame == UINT32_MAX)
7209 {
7210 vrc = VDCopy(hdd,
7211 VD_LAST_IMAGE,
7212 targetHdd,
7213 targetFormat.c_str(),
7214 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
7215 false /* fMoveByRename */,
7216 0 /* cbSize */,
7217 task.mVariant & ~MediumVariant_NoCreateDir,
7218 targetId.raw(),
7219 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7220 NULL /* pVDIfsOperation */,
7221 pTarget->m->vdImageIfaces,
7222 task.mVDOperationIfaces);
7223 }
7224 else
7225 {
7226 vrc = VDCopyEx(hdd,
7227 VD_LAST_IMAGE,
7228 targetHdd,
7229 targetFormat.c_str(),
7230 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
7231 false /* fMoveByRename */,
7232 0 /* cbSize */,
7233 task.midxSrcImageSame,
7234 task.midxDstImageSame,
7235 task.mVariant & ~MediumVariant_NoCreateDir,
7236 targetId.raw(),
7237 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7238 NULL /* pVDIfsOperation */,
7239 pTarget->m->vdImageIfaces,
7240 task.mVDOperationIfaces);
7241 }
7242 if (RT_FAILURE(vrc))
7243 throw setError(VBOX_E_FILE_ERROR,
7244 tr("Could not create the clone medium '%s'%s"),
7245 targetLocation.c_str(), vdError(vrc).c_str());
7246
7247 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
7248 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
7249 unsigned uImageFlags;
7250 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
7251 if (RT_SUCCESS(vrc))
7252 variant = (MediumVariant_T)uImageFlags;
7253 }
7254 catch (HRESULT aRC) { rcTmp = aRC; }
7255
7256 VDDestroy(targetHdd);
7257 }
7258 catch (HRESULT aRC) { rcTmp = aRC; }
7259
7260 VDDestroy(hdd);
7261 }
7262 catch (HRESULT aRC) { rcTmp = aRC; }
7263
7264 ErrorInfoKeeper eik;
7265 MultiResult mrc(rcTmp);
7266
7267 /* Only do the parent changes for newly created media. */
7268 if (SUCCEEDED(mrc) && fCreatingTarget)
7269 {
7270 /* we set mParent & children() */
7271 AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7272
7273 Assert(pTarget->m->pParent.isNull());
7274
7275 if (pParent)
7276 {
7277 /* associate the clone with the parent and deassociate
7278 * from VirtualBox */
7279 pTarget->m->pParent = pParent;
7280 pParent->m->llChildren.push_back(pTarget);
7281
7282 /* register with mVirtualBox as the last step and move to
7283 * Created state only on success (leaving an orphan file is
7284 * better than breaking media registry consistency) */
7285 eik.restore();
7286 ComObjPtr<Medium> pMedium;
7287 mrc = pParent->m->pVirtualBox->registerMedium(pTarget, &pMedium,
7288 DeviceType_HardDisk);
7289 Assert(pTarget == pMedium);
7290 eik.fetch();
7291
7292 if (FAILED(mrc))
7293 /* break parent association on failure to register */
7294 pTarget->deparent(); // removes target from parent
7295 }
7296 else
7297 {
7298 /* just register */
7299 eik.restore();
7300 ComObjPtr<Medium> pMedium;
7301 mrc = m->pVirtualBox->registerMedium(pTarget, &pMedium,
7302 DeviceType_HardDisk);
7303 Assert(pTarget == pMedium);
7304 eik.fetch();
7305 }
7306 }
7307
7308 if (fCreatingTarget)
7309 {
7310 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
7311
7312 if (SUCCEEDED(mrc))
7313 {
7314 pTarget->m->state = MediumState_Created;
7315
7316 pTarget->m->size = size;
7317 pTarget->m->logicalSize = logicalSize;
7318 pTarget->m->variant = variant;
7319 }
7320 else
7321 {
7322 /* back to NotCreated on failure */
7323 pTarget->m->state = MediumState_NotCreated;
7324
7325 /* reset UUID to prevent it from being reused next time */
7326 if (fGenerateUuid)
7327 unconst(pTarget->m->id).clear();
7328 }
7329 }
7330
7331 // now, at the end of this task (always asynchronous), save the settings
7332 if (SUCCEEDED(mrc))
7333 {
7334 // save the settings
7335 markRegistriesModified();
7336 /* collect multiple errors */
7337 eik.restore();
7338 m->pVirtualBox->saveModifiedRegistries();
7339 eik.fetch();
7340 }
7341
7342 /* Everything is explicitly unlocked when the task exits,
7343 * as the task destruction also destroys the source chain. */
7344
7345 /* Make sure the source chain is released early. It could happen
7346 * that we get a deadlock in Appliance::Import when Medium::Close
7347 * is called & the source chain is released at the same time. */
7348 task.mpSourceMediumLockList->Clear();
7349
7350 return mrc;
7351}
7352
7353/**
7354 * Implementation code for the "delete" task.
7355 *
7356 * This task always gets started from Medium::deleteStorage() and can run
7357 * synchronously or asynchronously depending on the "wait" parameter passed to
7358 * that function.
7359 *
7360 * @param task
7361 * @return
7362 */
7363HRESULT Medium::taskDeleteHandler(Medium::DeleteTask &task)
7364{
7365 NOREF(task);
7366 HRESULT rc = S_OK;
7367
7368 try
7369 {
7370 /* The lock is also used as a signal from the task initiator (which
7371 * releases it only after RTThreadCreate()) that we can start the job */
7372 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7373
7374 PVBOXHDD hdd;
7375 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7376 ComAssertRCThrow(vrc, E_FAIL);
7377
7378 Utf8Str format(m->strFormat);
7379 Utf8Str location(m->strLocationFull);
7380
7381 /* unlock before the potentially lengthy operation */
7382 Assert(m->state == MediumState_Deleting);
7383 thisLock.release();
7384
7385 try
7386 {
7387 vrc = VDOpen(hdd,
7388 format.c_str(),
7389 location.c_str(),
7390 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7391 m->vdImageIfaces);
7392 if (RT_SUCCESS(vrc))
7393 vrc = VDClose(hdd, true /* fDelete */);
7394
7395 if (RT_FAILURE(vrc))
7396 throw setError(VBOX_E_FILE_ERROR,
7397 tr("Could not delete the medium storage unit '%s'%s"),
7398 location.c_str(), vdError(vrc).c_str());
7399
7400 }
7401 catch (HRESULT aRC) { rc = aRC; }
7402
7403 VDDestroy(hdd);
7404 }
7405 catch (HRESULT aRC) { rc = aRC; }
7406
7407 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7408
7409 /* go to the NotCreated state even on failure since the storage
7410 * may have been already partially deleted and cannot be used any
7411 * more. One will be able to manually re-open the storage if really
7412 * needed to re-register it. */
7413 m->state = MediumState_NotCreated;
7414
7415 /* Reset UUID to prevent Create* from reusing it again */
7416 unconst(m->id).clear();
7417
7418 return rc;
7419}
7420
7421/**
7422 * Implementation code for the "reset" task.
7423 *
7424 * This always gets started asynchronously from Medium::Reset().
7425 *
7426 * @param task
7427 * @return
7428 */
7429HRESULT Medium::taskResetHandler(Medium::ResetTask &task)
7430{
7431 HRESULT rc = S_OK;
7432
7433 uint64_t size = 0, logicalSize = 0;
7434 MediumVariant_T variant = MediumVariant_Standard;
7435
7436 try
7437 {
7438 /* The lock is also used as a signal from the task initiator (which
7439 * releases it only after RTThreadCreate()) that we can start the job */
7440 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7441
7442 /// @todo Below we use a pair of delete/create operations to reset
7443 /// the diff contents but the most efficient way will of course be
7444 /// to add a VDResetDiff() API call
7445
7446 PVBOXHDD hdd;
7447 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7448 ComAssertRCThrow(vrc, E_FAIL);
7449
7450 Guid id = m->id;
7451 Utf8Str format(m->strFormat);
7452 Utf8Str location(m->strLocationFull);
7453
7454 Medium *pParent = m->pParent;
7455 Guid parentId = pParent->m->id;
7456 Utf8Str parentFormat(pParent->m->strFormat);
7457 Utf8Str parentLocation(pParent->m->strLocationFull);
7458
7459 Assert(m->state == MediumState_LockedWrite);
7460
7461 /* unlock before the potentially lengthy operation */
7462 thisLock.release();
7463
7464 try
7465 {
7466 /* Open all media in the target chain but the last. */
7467 MediumLockList::Base::const_iterator targetListBegin =
7468 task.mpMediumLockList->GetBegin();
7469 MediumLockList::Base::const_iterator targetListEnd =
7470 task.mpMediumLockList->GetEnd();
7471 for (MediumLockList::Base::const_iterator it = targetListBegin;
7472 it != targetListEnd;
7473 ++it)
7474 {
7475 const MediumLock &mediumLock = *it;
7476 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7477
7478 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7479
7480 /* sanity check, "this" is checked above */
7481 Assert( pMedium == this
7482 || pMedium->m->state == MediumState_LockedRead);
7483
7484 /* Open all media in appropriate mode. */
7485 vrc = VDOpen(hdd,
7486 pMedium->m->strFormat.c_str(),
7487 pMedium->m->strLocationFull.c_str(),
7488 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
7489 pMedium->m->vdImageIfaces);
7490 if (RT_FAILURE(vrc))
7491 throw setError(VBOX_E_FILE_ERROR,
7492 tr("Could not open the medium storage unit '%s'%s"),
7493 pMedium->m->strLocationFull.c_str(),
7494 vdError(vrc).c_str());
7495
7496 /* Done when we hit the media which should be reset */
7497 if (pMedium == this)
7498 break;
7499 }
7500
7501 /* first, delete the storage unit */
7502 vrc = VDClose(hdd, true /* fDelete */);
7503 if (RT_FAILURE(vrc))
7504 throw setError(VBOX_E_FILE_ERROR,
7505 tr("Could not delete the medium storage unit '%s'%s"),
7506 location.c_str(), vdError(vrc).c_str());
7507
7508 /* next, create it again */
7509 vrc = VDOpen(hdd,
7510 parentFormat.c_str(),
7511 parentLocation.c_str(),
7512 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7513 m->vdImageIfaces);
7514 if (RT_FAILURE(vrc))
7515 throw setError(VBOX_E_FILE_ERROR,
7516 tr("Could not open the medium storage unit '%s'%s"),
7517 parentLocation.c_str(), vdError(vrc).c_str());
7518
7519 vrc = VDCreateDiff(hdd,
7520 format.c_str(),
7521 location.c_str(),
7522 /// @todo use the same medium variant as before
7523 VD_IMAGE_FLAGS_NONE,
7524 NULL,
7525 id.raw(),
7526 parentId.raw(),
7527 VD_OPEN_FLAGS_NORMAL,
7528 m->vdImageIfaces,
7529 task.mVDOperationIfaces);
7530 if (RT_FAILURE(vrc))
7531 throw setError(VBOX_E_FILE_ERROR,
7532 tr("Could not create the differencing medium storage unit '%s'%s"),
7533 location.c_str(), vdError(vrc).c_str());
7534
7535 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
7536 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
7537 unsigned uImageFlags;
7538 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7539 if (RT_SUCCESS(vrc))
7540 variant = (MediumVariant_T)uImageFlags;
7541 }
7542 catch (HRESULT aRC) { rc = aRC; }
7543
7544 VDDestroy(hdd);
7545 }
7546 catch (HRESULT aRC) { rc = aRC; }
7547
7548 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7549
7550 m->size = size;
7551 m->logicalSize = logicalSize;
7552 m->variant = variant;
7553
7554 if (task.isAsync())
7555 {
7556 /* unlock ourselves when done */
7557 HRESULT rc2 = UnlockWrite(NULL);
7558 AssertComRC(rc2);
7559 }
7560
7561 /* Note that in sync mode, it's the caller's responsibility to
7562 * unlock the medium. */
7563
7564 return rc;
7565}
7566
7567/**
7568 * Implementation code for the "compact" task.
7569 *
7570 * @param task
7571 * @return
7572 */
7573HRESULT Medium::taskCompactHandler(Medium::CompactTask &task)
7574{
7575 HRESULT rc = S_OK;
7576
7577 /* Lock all in {parent,child} order. The lock is also used as a
7578 * signal from the task initiator (which releases it only after
7579 * RTThreadCreate()) that we can start the job. */
7580 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7581
7582 try
7583 {
7584 PVBOXHDD hdd;
7585 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7586 ComAssertRCThrow(vrc, E_FAIL);
7587
7588 try
7589 {
7590 /* Open all media in the chain. */
7591 MediumLockList::Base::const_iterator mediumListBegin =
7592 task.mpMediumLockList->GetBegin();
7593 MediumLockList::Base::const_iterator mediumListEnd =
7594 task.mpMediumLockList->GetEnd();
7595 MediumLockList::Base::const_iterator mediumListLast =
7596 mediumListEnd;
7597 mediumListLast--;
7598 for (MediumLockList::Base::const_iterator it = mediumListBegin;
7599 it != mediumListEnd;
7600 ++it)
7601 {
7602 const MediumLock &mediumLock = *it;
7603 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7604 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7605
7606 /* sanity check */
7607 if (it == mediumListLast)
7608 Assert(pMedium->m->state == MediumState_LockedWrite);
7609 else
7610 Assert(pMedium->m->state == MediumState_LockedRead);
7611
7612 /* Open all media but last in read-only mode. Do not handle
7613 * shareable media, as compaction and sharing are mutually
7614 * exclusive. */
7615 vrc = VDOpen(hdd,
7616 pMedium->m->strFormat.c_str(),
7617 pMedium->m->strLocationFull.c_str(),
7618 m->uOpenFlagsDef | (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
7619 pMedium->m->vdImageIfaces);
7620 if (RT_FAILURE(vrc))
7621 throw setError(VBOX_E_FILE_ERROR,
7622 tr("Could not open the medium storage unit '%s'%s"),
7623 pMedium->m->strLocationFull.c_str(),
7624 vdError(vrc).c_str());
7625 }
7626
7627 Assert(m->state == MediumState_LockedWrite);
7628
7629 Utf8Str location(m->strLocationFull);
7630
7631 /* unlock before the potentially lengthy operation */
7632 thisLock.release();
7633
7634 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
7635 if (RT_FAILURE(vrc))
7636 {
7637 if (vrc == VERR_NOT_SUPPORTED)
7638 throw setError(VBOX_E_NOT_SUPPORTED,
7639 tr("Compacting is not yet supported for medium '%s'"),
7640 location.c_str());
7641 else if (vrc == VERR_NOT_IMPLEMENTED)
7642 throw setError(E_NOTIMPL,
7643 tr("Compacting is not implemented, medium '%s'"),
7644 location.c_str());
7645 else
7646 throw setError(VBOX_E_FILE_ERROR,
7647 tr("Could not compact medium '%s'%s"),
7648 location.c_str(),
7649 vdError(vrc).c_str());
7650 }
7651 }
7652 catch (HRESULT aRC) { rc = aRC; }
7653
7654 VDDestroy(hdd);
7655 }
7656 catch (HRESULT aRC) { rc = aRC; }
7657
7658 /* Everything is explicitly unlocked when the task exits,
7659 * as the task destruction also destroys the media chain. */
7660
7661 return rc;
7662}
7663
7664/**
7665 * Implementation code for the "resize" task.
7666 *
7667 * @param task
7668 * @return
7669 */
7670HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task)
7671{
7672 HRESULT rc = S_OK;
7673
7674 /* Lock all in {parent,child} order. The lock is also used as a
7675 * signal from the task initiator (which releases it only after
7676 * RTThreadCreate()) that we can start the job. */
7677 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7678
7679 try
7680 {
7681 PVBOXHDD hdd;
7682 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7683 ComAssertRCThrow(vrc, E_FAIL);
7684
7685 try
7686 {
7687 /* Open all media in the chain. */
7688 MediumLockList::Base::const_iterator mediumListBegin =
7689 task.mpMediumLockList->GetBegin();
7690 MediumLockList::Base::const_iterator mediumListEnd =
7691 task.mpMediumLockList->GetEnd();
7692 MediumLockList::Base::const_iterator mediumListLast =
7693 mediumListEnd;
7694 mediumListLast--;
7695 for (MediumLockList::Base::const_iterator it = mediumListBegin;
7696 it != mediumListEnd;
7697 ++it)
7698 {
7699 const MediumLock &mediumLock = *it;
7700 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7701 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7702
7703 /* sanity check */
7704 if (it == mediumListLast)
7705 Assert(pMedium->m->state == MediumState_LockedWrite);
7706 else
7707 Assert(pMedium->m->state == MediumState_LockedRead);
7708
7709 /* Open all media but last in read-only mode. Do not handle
7710 * shareable media, as compaction and sharing are mutually
7711 * exclusive. */
7712 vrc = VDOpen(hdd,
7713 pMedium->m->strFormat.c_str(),
7714 pMedium->m->strLocationFull.c_str(),
7715 m->uOpenFlagsDef | (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
7716 pMedium->m->vdImageIfaces);
7717 if (RT_FAILURE(vrc))
7718 throw setError(VBOX_E_FILE_ERROR,
7719 tr("Could not open the medium storage unit '%s'%s"),
7720 pMedium->m->strLocationFull.c_str(),
7721 vdError(vrc).c_str());
7722 }
7723
7724 Assert(m->state == MediumState_LockedWrite);
7725
7726 Utf8Str location(m->strLocationFull);
7727
7728 /* unlock before the potentially lengthy operation */
7729 thisLock.release();
7730
7731 VDGEOMETRY geo = {0, 0, 0}; /* auto */
7732 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
7733 if (RT_FAILURE(vrc))
7734 {
7735 if (vrc == VERR_NOT_SUPPORTED)
7736 throw setError(VBOX_E_NOT_SUPPORTED,
7737 tr("Compacting is not yet supported for medium '%s'"),
7738 location.c_str());
7739 else if (vrc == VERR_NOT_IMPLEMENTED)
7740 throw setError(E_NOTIMPL,
7741 tr("Compacting is not implemented, medium '%s'"),
7742 location.c_str());
7743 else
7744 throw setError(VBOX_E_FILE_ERROR,
7745 tr("Could not compact medium '%s'%s"),
7746 location.c_str(),
7747 vdError(vrc).c_str());
7748 }
7749 }
7750 catch (HRESULT aRC) { rc = aRC; }
7751
7752 VDDestroy(hdd);
7753 }
7754 catch (HRESULT aRC) { rc = aRC; }
7755
7756 /* Everything is explicitly unlocked when the task exits,
7757 * as the task destruction also destroys the media chain. */
7758
7759 return rc;
7760}
7761
7762/**
7763 * Implementation code for the "export" task.
7764 *
7765 * This only gets started from Medium::exportFile() and always runs
7766 * asynchronously. It doesn't touch anything configuration related, so
7767 * we never save the VirtualBox.xml file here.
7768 *
7769 * @param task
7770 * @return
7771 */
7772HRESULT Medium::taskExportHandler(Medium::ExportTask &task)
7773{
7774 HRESULT rc = S_OK;
7775
7776 try
7777 {
7778 /* Lock all in {parent,child} order. The lock is also used as a
7779 * signal from the task initiator (which releases it only after
7780 * RTThreadCreate()) that we can start the job. */
7781 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7782
7783 PVBOXHDD hdd;
7784 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7785 ComAssertRCThrow(vrc, E_FAIL);
7786
7787 try
7788 {
7789 /* Open all media in the source chain. */
7790 MediumLockList::Base::const_iterator sourceListBegin =
7791 task.mpSourceMediumLockList->GetBegin();
7792 MediumLockList::Base::const_iterator sourceListEnd =
7793 task.mpSourceMediumLockList->GetEnd();
7794 for (MediumLockList::Base::const_iterator it = sourceListBegin;
7795 it != sourceListEnd;
7796 ++it)
7797 {
7798 const MediumLock &mediumLock = *it;
7799 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7800 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7801
7802 /* sanity check */
7803 Assert(pMedium->m->state == MediumState_LockedRead);
7804
7805 /* Open all media in read-only mode. */
7806 vrc = VDOpen(hdd,
7807 pMedium->m->strFormat.c_str(),
7808 pMedium->m->strLocationFull.c_str(),
7809 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
7810 pMedium->m->vdImageIfaces);
7811 if (RT_FAILURE(vrc))
7812 throw setError(VBOX_E_FILE_ERROR,
7813 tr("Could not open the medium storage unit '%s'%s"),
7814 pMedium->m->strLocationFull.c_str(),
7815 vdError(vrc).c_str());
7816 }
7817
7818 Utf8Str targetFormat(task.mFormat->getId());
7819 Utf8Str targetLocation(task.mFilename);
7820 uint64_t capabilities = task.mFormat->getCapabilities();
7821
7822 Assert(m->state == MediumState_LockedRead);
7823
7824 /* unlock before the potentially lengthy operation */
7825 thisLock.release();
7826
7827 /* ensure the target directory exists */
7828 if (capabilities & MediumFormatCapabilities_File)
7829 {
7830 rc = VirtualBox::ensureFilePathExists(targetLocation, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
7831 if (FAILED(rc))
7832 throw rc;
7833 }
7834
7835 PVBOXHDD targetHdd;
7836 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
7837 ComAssertRCThrow(vrc, E_FAIL);
7838
7839 try
7840 {
7841 vrc = VDCopy(hdd,
7842 VD_LAST_IMAGE,
7843 targetHdd,
7844 targetFormat.c_str(),
7845 targetLocation.c_str(),
7846 false /* fMoveByRename */,
7847 0 /* cbSize */,
7848 task.mVariant & ~MediumVariant_NoCreateDir,
7849 NULL /* pDstUuid */,
7850 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
7851 NULL /* pVDIfsOperation */,
7852 task.mVDImageIfaces,
7853 task.mVDOperationIfaces);
7854 if (RT_FAILURE(vrc))
7855 throw setError(VBOX_E_FILE_ERROR,
7856 tr("Could not create the clone medium '%s'%s"),
7857 targetLocation.c_str(), vdError(vrc).c_str());
7858 }
7859 catch (HRESULT aRC) { rc = aRC; }
7860
7861 VDDestroy(targetHdd);
7862 }
7863 catch (HRESULT aRC) { rc = aRC; }
7864
7865 VDDestroy(hdd);
7866 }
7867 catch (HRESULT aRC) { rc = aRC; }
7868
7869 /* Everything is explicitly unlocked when the task exits,
7870 * as the task destruction also destroys the source chain. */
7871
7872 /* Make sure the source chain is released early, otherwise it can
7873 * lead to deadlocks with concurrent IAppliance activities. */
7874 task.mpSourceMediumLockList->Clear();
7875
7876 return rc;
7877}
7878
7879/**
7880 * Implementation code for the "import" task.
7881 *
7882 * This only gets started from Medium::importFile() and always runs
7883 * asynchronously. It potentially touches the media registry, so we
7884 * always save the VirtualBox.xml file when we're done here.
7885 *
7886 * @param task
7887 * @return
7888 */
7889HRESULT Medium::taskImportHandler(Medium::ImportTask &task)
7890{
7891 HRESULT rcTmp = S_OK;
7892
7893 const ComObjPtr<Medium> &pParent = task.mParent;
7894
7895 bool fCreatingTarget = false;
7896
7897 uint64_t size = 0, logicalSize = 0;
7898 MediumVariant_T variant = MediumVariant_Standard;
7899 bool fGenerateUuid = false;
7900
7901 try
7902 {
7903 /* Lock all in {parent,child} order. The lock is also used as a
7904 * signal from the task initiator (which releases it only after
7905 * RTThreadCreate()) that we can start the job. */
7906 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
7907
7908 fCreatingTarget = m->state == MediumState_Creating;
7909
7910 /* The object may request a specific UUID (through a special form of
7911 * the setLocation() argument). Otherwise we have to generate it */
7912 Guid targetId = m->id;
7913 fGenerateUuid = targetId.isEmpty();
7914 if (fGenerateUuid)
7915 {
7916 targetId.create();
7917 /* VirtualBox::registerMedium() will need UUID */
7918 unconst(m->id) = targetId;
7919 }
7920
7921
7922 PVBOXHDD hdd;
7923 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7924 ComAssertRCThrow(vrc, E_FAIL);
7925
7926 try
7927 {
7928 /* Open source medium. */
7929 vrc = VDOpen(hdd,
7930 task.mFormat->getId().c_str(),
7931 task.mFilename.c_str(),
7932 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
7933 task.mVDImageIfaces);
7934 if (RT_FAILURE(vrc))
7935 throw setError(VBOX_E_FILE_ERROR,
7936 tr("Could not open the medium storage unit '%s'%s"),
7937 task.mFilename.c_str(),
7938 vdError(vrc).c_str());
7939
7940 Utf8Str targetFormat(m->strFormat);
7941 Utf8Str targetLocation(m->strLocationFull);
7942 uint64_t capabilities = task.mFormat->getCapabilities();
7943
7944 Assert( m->state == MediumState_Creating
7945 || m->state == MediumState_LockedWrite);
7946 Assert( pParent.isNull()
7947 || pParent->m->state == MediumState_LockedRead);
7948
7949 /* unlock before the potentially lengthy operation */
7950 thisLock.release();
7951
7952 /* ensure the target directory exists */
7953 if (capabilities & MediumFormatCapabilities_File)
7954 {
7955 HRESULT rc = VirtualBox::ensureFilePathExists(targetLocation, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
7956 if (FAILED(rc))
7957 throw rc;
7958 }
7959
7960 PVBOXHDD targetHdd;
7961 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
7962 ComAssertRCThrow(vrc, E_FAIL);
7963
7964 try
7965 {
7966 /* Open all media in the target chain. */
7967 MediumLockList::Base::const_iterator targetListBegin =
7968 task.mpTargetMediumLockList->GetBegin();
7969 MediumLockList::Base::const_iterator targetListEnd =
7970 task.mpTargetMediumLockList->GetEnd();
7971 for (MediumLockList::Base::const_iterator it = targetListBegin;
7972 it != targetListEnd;
7973 ++it)
7974 {
7975 const MediumLock &mediumLock = *it;
7976 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7977
7978 /* If the target medium is not created yet there's no
7979 * reason to open it. */
7980 if (pMedium == this && fCreatingTarget)
7981 continue;
7982
7983 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7984
7985 /* sanity check */
7986 Assert( pMedium->m->state == MediumState_LockedRead
7987 || pMedium->m->state == MediumState_LockedWrite);
7988
7989 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7990 if (pMedium->m->state != MediumState_LockedWrite)
7991 uOpenFlags = VD_OPEN_FLAGS_READONLY;
7992 if (pMedium->m->type == MediumType_Shareable)
7993 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7994
7995 /* Open all media in appropriate mode. */
7996 vrc = VDOpen(targetHdd,
7997 pMedium->m->strFormat.c_str(),
7998 pMedium->m->strLocationFull.c_str(),
7999 uOpenFlags | m->uOpenFlagsDef,
8000 pMedium->m->vdImageIfaces);
8001 if (RT_FAILURE(vrc))
8002 throw setError(VBOX_E_FILE_ERROR,
8003 tr("Could not open the medium storage unit '%s'%s"),
8004 pMedium->m->strLocationFull.c_str(),
8005 vdError(vrc).c_str());
8006 }
8007
8008 /** @todo r=klaus target isn't locked, race getting the state */
8009 vrc = VDCopy(hdd,
8010 VD_LAST_IMAGE,
8011 targetHdd,
8012 targetFormat.c_str(),
8013 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
8014 false /* fMoveByRename */,
8015 0 /* cbSize */,
8016 task.mVariant & ~MediumVariant_NoCreateDir,
8017 targetId.raw(),
8018 VD_OPEN_FLAGS_NORMAL,
8019 NULL /* pVDIfsOperation */,
8020 m->vdImageIfaces,
8021 task.mVDOperationIfaces);
8022 if (RT_FAILURE(vrc))
8023 throw setError(VBOX_E_FILE_ERROR,
8024 tr("Could not create the clone medium '%s'%s"),
8025 targetLocation.c_str(), vdError(vrc).c_str());
8026
8027 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
8028 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
8029 unsigned uImageFlags;
8030 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
8031 if (RT_SUCCESS(vrc))
8032 variant = (MediumVariant_T)uImageFlags;
8033 }
8034 catch (HRESULT aRC) { rcTmp = aRC; }
8035
8036 VDDestroy(targetHdd);
8037 }
8038 catch (HRESULT aRC) { rcTmp = aRC; }
8039
8040 VDDestroy(hdd);
8041 }
8042 catch (HRESULT aRC) { rcTmp = aRC; }
8043
8044 ErrorInfoKeeper eik;
8045 MultiResult mrc(rcTmp);
8046
8047 /* Only do the parent changes for newly created media. */
8048 if (SUCCEEDED(mrc) && fCreatingTarget)
8049 {
8050 /* we set mParent & children() */
8051 AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8052
8053 Assert(m->pParent.isNull());
8054
8055 if (pParent)
8056 {
8057 /* associate the clone with the parent and deassociate
8058 * from VirtualBox */
8059 m->pParent = pParent;
8060 pParent->m->llChildren.push_back(this);
8061
8062 /* register with mVirtualBox as the last step and move to
8063 * Created state only on success (leaving an orphan file is
8064 * better than breaking media registry consistency) */
8065 eik.restore();
8066 ComObjPtr<Medium> pMedium;
8067 mrc = pParent->m->pVirtualBox->registerMedium(this, &pMedium,
8068 DeviceType_HardDisk);
8069 Assert(this == pMedium);
8070 eik.fetch();
8071
8072 if (FAILED(mrc))
8073 /* break parent association on failure to register */
8074 this->deparent(); // removes target from parent
8075 }
8076 else
8077 {
8078 /* just register */
8079 eik.restore();
8080 ComObjPtr<Medium> pMedium;
8081 mrc = m->pVirtualBox->registerMedium(this, &pMedium, DeviceType_HardDisk);
8082 Assert(this == pMedium);
8083 eik.fetch();
8084 }
8085 }
8086
8087 if (fCreatingTarget)
8088 {
8089 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
8090
8091 if (SUCCEEDED(mrc))
8092 {
8093 m->state = MediumState_Created;
8094
8095 m->size = size;
8096 m->logicalSize = logicalSize;
8097 m->variant = variant;
8098 }
8099 else
8100 {
8101 /* back to NotCreated on failure */
8102 m->state = MediumState_NotCreated;
8103
8104 /* reset UUID to prevent it from being reused next time */
8105 if (fGenerateUuid)
8106 unconst(m->id).clear();
8107 }
8108 }
8109
8110 // now, at the end of this task (always asynchronous), save the settings
8111 {
8112 // save the settings
8113 markRegistriesModified();
8114 /* collect multiple errors */
8115 eik.restore();
8116 m->pVirtualBox->saveModifiedRegistries();
8117 eik.fetch();
8118 }
8119
8120 /* Everything is explicitly unlocked when the task exits,
8121 * as the task destruction also destroys the target chain. */
8122
8123 /* Make sure the target chain is released early, otherwise it can
8124 * lead to deadlocks with concurrent IAppliance activities. */
8125 task.mpTargetMediumLockList->Clear();
8126
8127 return mrc;
8128}
8129
8130/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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