VirtualBox

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

Last change on this file since 45552 was 45518, checked in by vboxsync, 12 years ago

Main: Code generator for (xp)com API implementations, including logging and parameter conversion, so far only used by MediumFormat. Next try, needed significant tweaks to work with xpcom (safearray handling fixes in the parameter conversion helpers), different STL implementation (which doesn't support declaring template type parameters as const), missing build dependencies (which didn't show on the dual core system used for writing the code), and finally the duplicate XPCOM classinfo and AddRef/Release/QueryInterface method definitions needed to be removed.

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

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