VirtualBox

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

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

Main: Check for already used UUIDs when registering a new non file based Medium and recreate if there are conflicts

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

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