VirtualBox

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

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

Main/MediumImpl: correct error code if a medium file is not accessible

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