VirtualBox

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

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

The changes inside loops where enumeration MediumVariant is converted. For correct work under both systems Windows and Linux because enumeration is defined differently on these systems.

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