VirtualBox

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

Last change on this file since 38469 was 38469, checked in by vboxsync, 14 years ago

VD: Interface cleanup. Merge the two involved structures (generic interface descriptor and callback table) into one, remove the duplicated interface wrappers in the backends and move the interface definitions into separate headers separating public and private interfaces.

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