VirtualBox

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

Last change on this file since 40432 was 40432, checked in by vboxsync, 13 years ago

Main/VirtualBox+Machine+Medium: redesign the medium registry save logic, as the local lists in the threads caused inconsistencies with concurrent operations and the return of spurious errors

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

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