VirtualBox

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

Last change on this file since 48233 was 47920, checked in by vboxsync, 11 years ago

Main/Medium: allow free form properties in the Special/ namespace, previously only properties defined by the VD backend were possible

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

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