VirtualBox

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

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

Main: The change ulong to ULONG inside the loops where enumeration MediumVariant is converted.

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

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