VirtualBox

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

最後變更 在這個檔案從44372是 44365,由 vboxsync 提交於 12 年 前

Main: change API. Interface IMedium was touched. Handling enumeration MediumVariant was changed. PR5434.

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

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