VirtualBox

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

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

Main/SnapshotImpl: Select merge direction based on the image size

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 280.9 KB
Line 
1/* $Id: MediumImpl.cpp 44395 2013-01-26 19:52:47Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "MediumImpl.h"
19#include "ProgressImpl.h"
20#include "SystemPropertiesImpl.h"
21#include "VirtualBoxImpl.h"
22
23#include "AutoCaller.h"
24#include "Logging.h"
25
26#include <VBox/com/array.h>
27#include "VBox/com/MultiResult.h"
28#include "VBox/com/ErrorInfo.h"
29
30#include <VBox/err.h>
31#include <VBox/settings.h>
32
33#include <iprt/param.h>
34#include <iprt/path.h>
35#include <iprt/file.h>
36#include <iprt/tcp.h>
37#include <iprt/cpp/utils.h>
38
39#include <VBox/vd.h>
40
41#include <algorithm>
42#include <list>
43
44typedef std::list<Guid> GuidList;
45
46////////////////////////////////////////////////////////////////////////////////
47//
48// Medium data definition
49//
50////////////////////////////////////////////////////////////////////////////////
51
52/** Describes how a machine refers to this medium. */
53struct BackRef
54{
55 /** Equality predicate for stdc++. */
56 struct EqualsTo : public std::unary_function <BackRef, bool>
57 {
58 explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
59
60 bool operator()(const argument_type &aThat) const
61 {
62 return aThat.machineId == machineId;
63 }
64
65 const Guid machineId;
66 };
67
68 BackRef(const Guid &aMachineId,
69 const Guid &aSnapshotId = Guid::Empty)
70 : machineId(aMachineId),
71 fInCurState(aSnapshotId.isZero())
72 {
73 if (aSnapshotId.isValid() && !aSnapshotId.isZero())
74 llSnapshotIds.push_back(aSnapshotId);
75 }
76
77 Guid machineId;
78 bool fInCurState : 1;
79 GuidList llSnapshotIds;
80};
81
82typedef std::list<BackRef> BackRefList;
83
84struct Medium::Data
85{
86 Data()
87 : pVirtualBox(NULL),
88 state(MediumState_NotCreated),
89 variant(MediumVariant_Standard),
90 size(0),
91 readers(0),
92 preLockState(MediumState_NotCreated),
93 queryInfoSem(LOCKCLASS_MEDIUMQUERY),
94 queryInfoRunning(false),
95 type(MediumType_Normal),
96 devType(DeviceType_HardDisk),
97 logicalSize(0),
98 hddOpenMode(OpenReadWrite),
99 autoReset(false),
100 hostDrive(false),
101 implicit(false),
102 uOpenFlagsDef(VD_OPEN_FLAGS_IGNORE_FLUSH),
103 numCreateDiffTasks(0),
104 vdDiskIfaces(NULL),
105 vdImageIfaces(NULL)
106 { }
107
108 /** weak VirtualBox parent */
109 VirtualBox * const pVirtualBox;
110
111 // pParent and llChildren are protected by VirtualBox::getMediaTreeLockHandle()
112 ComObjPtr<Medium> pParent;
113 MediaList llChildren; // to add a child, just call push_back; to remove a child, call child->deparent() which does a lookup
114
115 GuidList llRegistryIDs; // media registries in which this medium is listed
116
117 const Guid id;
118 Utf8Str strDescription;
119 MediumState_T state;
120 MediumVariant_T variant;
121 Utf8Str strLocationFull;
122 uint64_t size;
123 Utf8Str strLastAccessError;
124
125 BackRefList backRefs;
126
127 size_t readers;
128 MediumState_T preLockState;
129
130 /** Special synchronization for operations which must wait for
131 * Medium::queryInfo in another thread to complete. Using a SemRW is
132 * not quite ideal, but at least it is subject to the lock validator,
133 * unlike the SemEventMulti which we had here for many years. Catching
134 * possible deadlocks is more important than a tiny bit of efficiency. */
135 RWLockHandle queryInfoSem;
136 bool queryInfoRunning : 1;
137
138 const Utf8Str strFormat;
139 ComObjPtr<MediumFormat> formatObj;
140
141 MediumType_T type;
142 DeviceType_T devType;
143 uint64_t logicalSize;
144
145 HDDOpenMode hddOpenMode;
146
147 bool autoReset : 1;
148
149 /** New UUID to be set on the next Medium::queryInfo call. */
150 const Guid uuidImage;
151 /** New parent UUID to be set on the next Medium::queryInfo call. */
152 const Guid uuidParentImage;
153
154 bool hostDrive : 1;
155
156 settings::StringsMap mapProperties;
157
158 bool implicit : 1;
159
160 /** Default flags passed to VDOpen(). */
161 unsigned uOpenFlagsDef;
162
163 uint32_t numCreateDiffTasks;
164
165 Utf8Str vdError; /*< Error remembered by the VD error callback. */
166
167 VDINTERFACEERROR vdIfError;
168
169 VDINTERFACECONFIG vdIfConfig;
170
171 VDINTERFACETCPNET vdIfTcpNet;
172
173 PVDINTERFACE vdDiskIfaces;
174 PVDINTERFACE vdImageIfaces;
175};
176
177typedef struct VDSOCKETINT
178{
179 /** Socket handle. */
180 RTSOCKET hSocket;
181} VDSOCKETINT, *PVDSOCKETINT;
182
183////////////////////////////////////////////////////////////////////////////////
184//
185// Globals
186//
187////////////////////////////////////////////////////////////////////////////////
188
189/**
190 * Medium::Task class for asynchronous operations.
191 *
192 * @note Instances of this class must be created using new() because the
193 * task thread function will delete them when the task is complete.
194 *
195 * @note The constructor of this class adds a caller on the managed Medium
196 * object which is automatically released upon destruction.
197 */
198class Medium::Task
199{
200public:
201 Task(Medium *aMedium, Progress *aProgress)
202 : mVDOperationIfaces(NULL),
203 mMedium(aMedium),
204 mMediumCaller(aMedium),
205 mThread(NIL_RTTHREAD),
206 mProgress(aProgress),
207 mVirtualBoxCaller(NULL)
208 {
209 AssertReturnVoidStmt(aMedium, mRC = E_FAIL);
210 mRC = mMediumCaller.rc();
211 if (FAILED(mRC))
212 return;
213
214 /* Get strong VirtualBox reference, see below. */
215 VirtualBox *pVirtualBox = aMedium->m->pVirtualBox;
216 mVirtualBox = pVirtualBox;
217 mVirtualBoxCaller.attach(pVirtualBox);
218 mRC = mVirtualBoxCaller.rc();
219 if (FAILED(mRC))
220 return;
221
222 /* Set up a per-operation progress interface, can be used freely (for
223 * binary operations you can use it either on the source or target). */
224 mVDIfProgress.pfnProgress = vdProgressCall;
225 int vrc = VDInterfaceAdd(&mVDIfProgress.Core,
226 "Medium::Task::vdInterfaceProgress",
227 VDINTERFACETYPE_PROGRESS,
228 mProgress,
229 sizeof(VDINTERFACEPROGRESS),
230 &mVDOperationIfaces);
231 AssertRC(vrc);
232 if (RT_FAILURE(vrc))
233 mRC = E_FAIL;
234 }
235
236 // Make all destructors virtual. Just in case.
237 virtual ~Task()
238 {}
239
240 HRESULT rc() const { return mRC; }
241 bool isOk() const { return SUCCEEDED(rc()); }
242
243 static int fntMediumTask(RTTHREAD aThread, void *pvUser);
244
245 bool isAsync() { return mThread != NIL_RTTHREAD; }
246
247 PVDINTERFACE mVDOperationIfaces;
248
249 const ComObjPtr<Medium> mMedium;
250 AutoCaller mMediumCaller;
251
252 friend HRESULT Medium::runNow(Medium::Task*);
253
254protected:
255 HRESULT mRC;
256 RTTHREAD mThread;
257
258private:
259 virtual HRESULT handler() = 0;
260
261 const ComObjPtr<Progress> mProgress;
262
263 static DECLCALLBACK(int) vdProgressCall(void *pvUser, unsigned uPercent);
264
265 VDINTERFACEPROGRESS mVDIfProgress;
266
267 /* Must have a strong VirtualBox reference during a task otherwise the
268 * reference count might drop to 0 while a task is still running. This
269 * would result in weird behavior, including deadlocks due to uninit and
270 * locking order issues. The deadlock often is not detectable because the
271 * uninit uses event semaphores which sabotages deadlock detection. */
272 ComObjPtr<VirtualBox> mVirtualBox;
273 AutoCaller mVirtualBoxCaller;
274};
275
276class Medium::CreateBaseTask : public Medium::Task
277{
278public:
279 CreateBaseTask(Medium *aMedium,
280 Progress *aProgress,
281 uint64_t aSize,
282 MediumVariant_T aVariant)
283 : Medium::Task(aMedium, aProgress),
284 mSize(aSize),
285 mVariant(aVariant)
286 {}
287
288 uint64_t mSize;
289 MediumVariant_T mVariant;
290
291private:
292 virtual HRESULT handler();
293};
294
295class Medium::CreateDiffTask : public Medium::Task
296{
297public:
298 CreateDiffTask(Medium *aMedium,
299 Progress *aProgress,
300 Medium *aTarget,
301 MediumVariant_T aVariant,
302 MediumLockList *aMediumLockList,
303 bool fKeepMediumLockList = false)
304 : Medium::Task(aMedium, aProgress),
305 mpMediumLockList(aMediumLockList),
306 mTarget(aTarget),
307 mVariant(aVariant),
308 mTargetCaller(aTarget),
309 mfKeepMediumLockList(fKeepMediumLockList)
310 {
311 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
312 mRC = mTargetCaller.rc();
313 if (FAILED(mRC))
314 return;
315 }
316
317 ~CreateDiffTask()
318 {
319 if (!mfKeepMediumLockList && mpMediumLockList)
320 delete mpMediumLockList;
321 }
322
323 MediumLockList *mpMediumLockList;
324
325 const ComObjPtr<Medium> mTarget;
326 MediumVariant_T mVariant;
327
328private:
329 virtual HRESULT handler();
330
331 AutoCaller mTargetCaller;
332 bool mfKeepMediumLockList;
333};
334
335class Medium::CloneTask : public Medium::Task
336{
337public:
338 CloneTask(Medium *aMedium,
339 Progress *aProgress,
340 Medium *aTarget,
341 MediumVariant_T aVariant,
342 Medium *aParent,
343 uint32_t idxSrcImageSame,
344 uint32_t idxDstImageSame,
345 MediumLockList *aSourceMediumLockList,
346 MediumLockList *aTargetMediumLockList,
347 bool fKeepSourceMediumLockList = false,
348 bool fKeepTargetMediumLockList = false)
349 : Medium::Task(aMedium, aProgress),
350 mTarget(aTarget),
351 mParent(aParent),
352 mpSourceMediumLockList(aSourceMediumLockList),
353 mpTargetMediumLockList(aTargetMediumLockList),
354 mVariant(aVariant),
355 midxSrcImageSame(idxSrcImageSame),
356 midxDstImageSame(idxDstImageSame),
357 mTargetCaller(aTarget),
358 mParentCaller(aParent),
359 mfKeepSourceMediumLockList(fKeepSourceMediumLockList),
360 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
361 {
362 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
363 mRC = mTargetCaller.rc();
364 if (FAILED(mRC))
365 return;
366 /* aParent may be NULL */
367 mRC = mParentCaller.rc();
368 if (FAILED(mRC))
369 return;
370 AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
371 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
372 }
373
374 ~CloneTask()
375 {
376 if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
377 delete mpSourceMediumLockList;
378 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
379 delete mpTargetMediumLockList;
380 }
381
382 const ComObjPtr<Medium> mTarget;
383 const ComObjPtr<Medium> mParent;
384 MediumLockList *mpSourceMediumLockList;
385 MediumLockList *mpTargetMediumLockList;
386 MediumVariant_T mVariant;
387 uint32_t midxSrcImageSame;
388 uint32_t midxDstImageSame;
389
390private:
391 virtual HRESULT handler();
392
393 AutoCaller mTargetCaller;
394 AutoCaller mParentCaller;
395 bool mfKeepSourceMediumLockList;
396 bool mfKeepTargetMediumLockList;
397};
398
399class Medium::CompactTask : public Medium::Task
400{
401public:
402 CompactTask(Medium *aMedium,
403 Progress *aProgress,
404 MediumLockList *aMediumLockList,
405 bool fKeepMediumLockList = false)
406 : Medium::Task(aMedium, aProgress),
407 mpMediumLockList(aMediumLockList),
408 mfKeepMediumLockList(fKeepMediumLockList)
409 {
410 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
411 }
412
413 ~CompactTask()
414 {
415 if (!mfKeepMediumLockList && mpMediumLockList)
416 delete mpMediumLockList;
417 }
418
419 MediumLockList *mpMediumLockList;
420
421private:
422 virtual HRESULT handler();
423
424 bool mfKeepMediumLockList;
425};
426
427class Medium::ResizeTask : public Medium::Task
428{
429public:
430 ResizeTask(Medium *aMedium,
431 uint64_t aSize,
432 Progress *aProgress,
433 MediumLockList *aMediumLockList,
434 bool fKeepMediumLockList = false)
435 : Medium::Task(aMedium, aProgress),
436 mSize(aSize),
437 mpMediumLockList(aMediumLockList),
438 mfKeepMediumLockList(fKeepMediumLockList)
439 {
440 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
441 }
442
443 ~ResizeTask()
444 {
445 if (!mfKeepMediumLockList && mpMediumLockList)
446 delete mpMediumLockList;
447 }
448
449 uint64_t mSize;
450 MediumLockList *mpMediumLockList;
451
452private:
453 virtual HRESULT handler();
454
455 bool mfKeepMediumLockList;
456};
457
458class Medium::ResetTask : public Medium::Task
459{
460public:
461 ResetTask(Medium *aMedium,
462 Progress *aProgress,
463 MediumLockList *aMediumLockList,
464 bool fKeepMediumLockList = false)
465 : Medium::Task(aMedium, aProgress),
466 mpMediumLockList(aMediumLockList),
467 mfKeepMediumLockList(fKeepMediumLockList)
468 {}
469
470 ~ResetTask()
471 {
472 if (!mfKeepMediumLockList && mpMediumLockList)
473 delete mpMediumLockList;
474 }
475
476 MediumLockList *mpMediumLockList;
477
478private:
479 virtual HRESULT handler();
480
481 bool mfKeepMediumLockList;
482};
483
484class Medium::DeleteTask : public Medium::Task
485{
486public:
487 DeleteTask(Medium *aMedium,
488 Progress *aProgress,
489 MediumLockList *aMediumLockList,
490 bool fKeepMediumLockList = false)
491 : Medium::Task(aMedium, aProgress),
492 mpMediumLockList(aMediumLockList),
493 mfKeepMediumLockList(fKeepMediumLockList)
494 {}
495
496 ~DeleteTask()
497 {
498 if (!mfKeepMediumLockList && mpMediumLockList)
499 delete mpMediumLockList;
500 }
501
502 MediumLockList *mpMediumLockList;
503
504private:
505 virtual HRESULT handler();
506
507 bool mfKeepMediumLockList;
508};
509
510class Medium::MergeTask : public Medium::Task
511{
512public:
513 MergeTask(Medium *aMedium,
514 Medium *aTarget,
515 bool fMergeForward,
516 Medium *aParentForTarget,
517 const MediaList &aChildrenToReparent,
518 Progress *aProgress,
519 MediumLockList *aMediumLockList,
520 bool fKeepMediumLockList = false)
521 : Medium::Task(aMedium, aProgress),
522 mTarget(aTarget),
523 mfMergeForward(fMergeForward),
524 mParentForTarget(aParentForTarget),
525 mChildrenToReparent(aChildrenToReparent),
526 mpMediumLockList(aMediumLockList),
527 mTargetCaller(aTarget),
528 mParentForTargetCaller(aParentForTarget),
529 mfChildrenCaller(false),
530 mfKeepMediumLockList(fKeepMediumLockList)
531 {
532 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
533 for (MediaList::const_iterator it = mChildrenToReparent.begin();
534 it != mChildrenToReparent.end();
535 ++it)
536 {
537 HRESULT rc2 = (*it)->addCaller();
538 if (FAILED(rc2))
539 {
540 mRC = E_FAIL;
541 for (MediaList::const_iterator it2 = mChildrenToReparent.begin();
542 it2 != it;
543 --it2)
544 {
545 (*it2)->releaseCaller();
546 }
547 return;
548 }
549 }
550 mfChildrenCaller = true;
551 }
552
553 ~MergeTask()
554 {
555 if (!mfKeepMediumLockList && mpMediumLockList)
556 delete mpMediumLockList;
557 if (mfChildrenCaller)
558 {
559 for (MediaList::const_iterator it = mChildrenToReparent.begin();
560 it != mChildrenToReparent.end();
561 ++it)
562 {
563 (*it)->releaseCaller();
564 }
565 }
566 }
567
568 const ComObjPtr<Medium> mTarget;
569 bool mfMergeForward;
570 /* When mChildrenToReparent is empty then mParentForTarget is non-null.
571 * In other words: they are used in different cases. */
572 const ComObjPtr<Medium> mParentForTarget;
573 MediaList mChildrenToReparent;
574 MediumLockList *mpMediumLockList;
575
576private:
577 virtual HRESULT handler();
578
579 AutoCaller mTargetCaller;
580 AutoCaller mParentForTargetCaller;
581 bool mfChildrenCaller;
582 bool mfKeepMediumLockList;
583};
584
585class Medium::ExportTask : public Medium::Task
586{
587public:
588 ExportTask(Medium *aMedium,
589 Progress *aProgress,
590 const char *aFilename,
591 MediumFormat *aFormat,
592 MediumVariant_T aVariant,
593 VDINTERFACEIO *aVDImageIOIf,
594 void *aVDImageIOUser,
595 MediumLockList *aSourceMediumLockList,
596 bool fKeepSourceMediumLockList = false)
597 : Medium::Task(aMedium, aProgress),
598 mpSourceMediumLockList(aSourceMediumLockList),
599 mFilename(aFilename),
600 mFormat(aFormat),
601 mVariant(aVariant),
602 mfKeepSourceMediumLockList(fKeepSourceMediumLockList)
603 {
604 AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
605
606 mVDImageIfaces = aMedium->m->vdImageIfaces;
607 if (aVDImageIOIf)
608 {
609 int vrc = VDInterfaceAdd(&aVDImageIOIf->Core, "Medium::vdInterfaceIO",
610 VDINTERFACETYPE_IO, aVDImageIOUser,
611 sizeof(VDINTERFACEIO), &mVDImageIfaces);
612 AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
613 }
614 }
615
616 ~ExportTask()
617 {
618 if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
619 delete mpSourceMediumLockList;
620 }
621
622 MediumLockList *mpSourceMediumLockList;
623 Utf8Str mFilename;
624 ComObjPtr<MediumFormat> mFormat;
625 MediumVariant_T mVariant;
626 PVDINTERFACE mVDImageIfaces;
627
628private:
629 virtual HRESULT handler();
630
631 bool mfKeepSourceMediumLockList;
632};
633
634class Medium::ImportTask : public Medium::Task
635{
636public:
637 ImportTask(Medium *aMedium,
638 Progress *aProgress,
639 const char *aFilename,
640 MediumFormat *aFormat,
641 MediumVariant_T aVariant,
642 VDINTERFACEIO *aVDImageIOIf,
643 void *aVDImageIOUser,
644 Medium *aParent,
645 MediumLockList *aTargetMediumLockList,
646 bool fKeepTargetMediumLockList = false)
647 : Medium::Task(aMedium, aProgress),
648 mFilename(aFilename),
649 mFormat(aFormat),
650 mVariant(aVariant),
651 mParent(aParent),
652 mpTargetMediumLockList(aTargetMediumLockList),
653 mParentCaller(aParent),
654 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
655 {
656 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
657 /* aParent may be NULL */
658 mRC = mParentCaller.rc();
659 if (FAILED(mRC))
660 return;
661
662 mVDImageIfaces = aMedium->m->vdImageIfaces;
663 if (aVDImageIOIf)
664 {
665 int vrc = VDInterfaceAdd(&aVDImageIOIf->Core, "Medium::vdInterfaceIO",
666 VDINTERFACETYPE_IO, aVDImageIOUser,
667 sizeof(VDINTERFACEIO), &mVDImageIfaces);
668 AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
669 }
670 }
671
672 ~ImportTask()
673 {
674 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
675 delete mpTargetMediumLockList;
676 }
677
678 Utf8Str mFilename;
679 ComObjPtr<MediumFormat> mFormat;
680 MediumVariant_T mVariant;
681 const ComObjPtr<Medium> mParent;
682 MediumLockList *mpTargetMediumLockList;
683 PVDINTERFACE mVDImageIfaces;
684
685private:
686 virtual HRESULT handler();
687
688 AutoCaller mParentCaller;
689 bool mfKeepTargetMediumLockList;
690};
691
692/**
693 * Thread function for time-consuming medium tasks.
694 *
695 * @param pvUser Pointer to the Medium::Task instance.
696 */
697/* static */
698DECLCALLBACK(int) Medium::Task::fntMediumTask(RTTHREAD aThread, void *pvUser)
699{
700 LogFlowFuncEnter();
701 AssertReturn(pvUser, (int)E_INVALIDARG);
702 Medium::Task *pTask = static_cast<Medium::Task *>(pvUser);
703
704 pTask->mThread = aThread;
705
706 HRESULT rc = pTask->handler();
707
708 /* complete the progress if run asynchronously */
709 if (pTask->isAsync())
710 {
711 if (!pTask->mProgress.isNull())
712 pTask->mProgress->notifyComplete(rc);
713 }
714
715 /* pTask is no longer needed, delete it. */
716 delete pTask;
717
718 LogFlowFunc(("rc=%Rhrc\n", rc));
719 LogFlowFuncLeave();
720
721 return (int)rc;
722}
723
724/**
725 * PFNVDPROGRESS callback handler for Task operations.
726 *
727 * @param pvUser Pointer to the Progress instance.
728 * @param uPercent Completion percentage (0-100).
729 */
730/*static*/
731DECLCALLBACK(int) Medium::Task::vdProgressCall(void *pvUser, unsigned uPercent)
732{
733 Progress *that = static_cast<Progress *>(pvUser);
734
735 if (that != NULL)
736 {
737 /* update the progress object, capping it at 99% as the final percent
738 * is used for additional operations like setting the UUIDs and similar. */
739 HRESULT rc = that->SetCurrentOperationProgress(uPercent * 99 / 100);
740 if (FAILED(rc))
741 {
742 if (rc == E_FAIL)
743 return VERR_CANCELLED;
744 else
745 return VERR_INVALID_STATE;
746 }
747 }
748
749 return VINF_SUCCESS;
750}
751
752/**
753 * Implementation code for the "create base" task.
754 */
755HRESULT Medium::CreateBaseTask::handler()
756{
757 return mMedium->taskCreateBaseHandler(*this);
758}
759
760/**
761 * Implementation code for the "create diff" task.
762 */
763HRESULT Medium::CreateDiffTask::handler()
764{
765 return mMedium->taskCreateDiffHandler(*this);
766}
767
768/**
769 * Implementation code for the "clone" task.
770 */
771HRESULT Medium::CloneTask::handler()
772{
773 return mMedium->taskCloneHandler(*this);
774}
775
776/**
777 * Implementation code for the "compact" task.
778 */
779HRESULT Medium::CompactTask::handler()
780{
781 return mMedium->taskCompactHandler(*this);
782}
783
784/**
785 * Implementation code for the "resize" task.
786 */
787HRESULT Medium::ResizeTask::handler()
788{
789 return mMedium->taskResizeHandler(*this);
790}
791
792
793/**
794 * Implementation code for the "reset" task.
795 */
796HRESULT Medium::ResetTask::handler()
797{
798 return mMedium->taskResetHandler(*this);
799}
800
801/**
802 * Implementation code for the "delete" task.
803 */
804HRESULT Medium::DeleteTask::handler()
805{
806 return mMedium->taskDeleteHandler(*this);
807}
808
809/**
810 * Implementation code for the "merge" task.
811 */
812HRESULT Medium::MergeTask::handler()
813{
814 return mMedium->taskMergeHandler(*this);
815}
816
817/**
818 * Implementation code for the "export" task.
819 */
820HRESULT Medium::ExportTask::handler()
821{
822 return mMedium->taskExportHandler(*this);
823}
824
825/**
826 * Implementation code for the "import" task.
827 */
828HRESULT Medium::ImportTask::handler()
829{
830 return mMedium->taskImportHandler(*this);
831}
832
833////////////////////////////////////////////////////////////////////////////////
834//
835// Medium constructor / destructor
836//
837////////////////////////////////////////////////////////////////////////////////
838
839DEFINE_EMPTY_CTOR_DTOR(Medium)
840
841HRESULT Medium::FinalConstruct()
842{
843 m = new Data;
844
845 /* Initialize the callbacks of the VD error interface */
846 m->vdIfError.pfnError = vdErrorCall;
847 m->vdIfError.pfnMessage = NULL;
848
849 /* Initialize the callbacks of the VD config interface */
850 m->vdIfConfig.pfnAreKeysValid = vdConfigAreKeysValid;
851 m->vdIfConfig.pfnQuerySize = vdConfigQuerySize;
852 m->vdIfConfig.pfnQuery = vdConfigQuery;
853
854 /* Initialize the callbacks of the VD TCP interface (we always use the host
855 * IP stack for now) */
856 m->vdIfTcpNet.pfnSocketCreate = vdTcpSocketCreate;
857 m->vdIfTcpNet.pfnSocketDestroy = vdTcpSocketDestroy;
858 m->vdIfTcpNet.pfnClientConnect = vdTcpClientConnect;
859 m->vdIfTcpNet.pfnClientClose = vdTcpClientClose;
860 m->vdIfTcpNet.pfnIsClientConnected = vdTcpIsClientConnected;
861 m->vdIfTcpNet.pfnSelectOne = vdTcpSelectOne;
862 m->vdIfTcpNet.pfnRead = vdTcpRead;
863 m->vdIfTcpNet.pfnWrite = vdTcpWrite;
864 m->vdIfTcpNet.pfnSgWrite = vdTcpSgWrite;
865 m->vdIfTcpNet.pfnFlush = vdTcpFlush;
866 m->vdIfTcpNet.pfnSetSendCoalescing = vdTcpSetSendCoalescing;
867 m->vdIfTcpNet.pfnGetLocalAddress = vdTcpGetLocalAddress;
868 m->vdIfTcpNet.pfnGetPeerAddress = vdTcpGetPeerAddress;
869 m->vdIfTcpNet.pfnSelectOneEx = NULL;
870 m->vdIfTcpNet.pfnPoke = NULL;
871
872 /* Initialize the per-disk interface chain (could be done more globally,
873 * but it's not wasting much time or space so it's not worth it). */
874 int vrc;
875 vrc = VDInterfaceAdd(&m->vdIfError.Core,
876 "Medium::vdInterfaceError",
877 VDINTERFACETYPE_ERROR, this,
878 sizeof(VDINTERFACEERROR), &m->vdDiskIfaces);
879 AssertRCReturn(vrc, E_FAIL);
880
881 /* Initialize the per-image interface chain */
882 vrc = VDInterfaceAdd(&m->vdIfConfig.Core,
883 "Medium::vdInterfaceConfig",
884 VDINTERFACETYPE_CONFIG, this,
885 sizeof(VDINTERFACECONFIG), &m->vdImageIfaces);
886 AssertRCReturn(vrc, E_FAIL);
887
888 vrc = VDInterfaceAdd(&m->vdIfTcpNet.Core,
889 "Medium::vdInterfaceTcpNet",
890 VDINTERFACETYPE_TCPNET, this,
891 sizeof(VDINTERFACETCPNET), &m->vdImageIfaces);
892 AssertRCReturn(vrc, E_FAIL);
893
894 return BaseFinalConstruct();
895}
896
897void Medium::FinalRelease()
898{
899 uninit();
900
901 delete m;
902
903 BaseFinalRelease();
904}
905
906/**
907 * Initializes an empty hard disk object without creating or opening an associated
908 * storage unit.
909 *
910 * This gets called by VirtualBox::CreateHardDisk() in which case uuidMachineRegistry
911 * is empty since starting with VirtualBox 4.0, we no longer add opened media to a
912 * registry automatically (this is deferred until the medium is attached to a machine).
913 *
914 * This also gets called when VirtualBox creates diff images; in this case uuidMachineRegistry
915 * is set to the registry of the parent image to make sure they all end up in the same
916 * file.
917 *
918 * For hard disks that don't have the MediumFormatCapabilities_CreateFixed or
919 * MediumFormatCapabilities_CreateDynamic capability (and therefore cannot be created or deleted
920 * with the means of VirtualBox) the associated storage unit is assumed to be
921 * ready for use so the state of the hard disk object will be set to Created.
922 *
923 * @param aVirtualBox VirtualBox object.
924 * @param aFormat
925 * @param aLocation Storage unit location.
926 * @param uuidMachineRegistry The registry to which this medium should be added (global registry UUID or machine UUID or empty if none).
927 */
928HRESULT Medium::init(VirtualBox *aVirtualBox,
929 const Utf8Str &aFormat,
930 const Utf8Str &aLocation,
931 const Guid &uuidMachineRegistry)
932{
933 AssertReturn(aVirtualBox != NULL, E_FAIL);
934 AssertReturn(!aFormat.isEmpty(), E_FAIL);
935
936 /* Enclose the state transition NotReady->InInit->Ready */
937 AutoInitSpan autoInitSpan(this);
938 AssertReturn(autoInitSpan.isOk(), E_FAIL);
939
940 HRESULT rc = S_OK;
941
942 unconst(m->pVirtualBox) = aVirtualBox;
943
944 if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
945 m->llRegistryIDs.push_back(uuidMachineRegistry);
946
947 /* no storage yet */
948 m->state = MediumState_NotCreated;
949
950 /* cannot be a host drive */
951 m->hostDrive = false;
952
953 /* No storage unit is created yet, no need to call Medium::queryInfo */
954
955 rc = setFormat(aFormat);
956 if (FAILED(rc)) return rc;
957
958 rc = setLocation(aLocation);
959 if (FAILED(rc)) return rc;
960
961 if (!(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateFixed
962 | MediumFormatCapabilities_CreateDynamic))
963 )
964 {
965 /* Storage for hard disks of this format can neither be explicitly
966 * created by VirtualBox nor deleted, so we place the hard disk to
967 * Inaccessible state here and also add it to the registry. The
968 * state means that one has to use RefreshState() to update the
969 * medium format specific fields. */
970 m->state = MediumState_Inaccessible;
971 // create new UUID
972 unconst(m->id).create();
973
974 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
975 ComObjPtr<Medium> pMedium;
976
977 /*
978 * Check whether the UUID is taken already and create a new one
979 * if required.
980 * Try this only a limited amount of times in case the PRNG is broken
981 * in some way to prevent an endless loop.
982 */
983 for (unsigned i = 0; i < 5; i++)
984 {
985 bool fInUse;
986
987 fInUse = m->pVirtualBox->isMediaUuidInUse(m->id, DeviceType_HardDisk);
988 if (fInUse)
989 {
990 // create new UUID
991 unconst(m->id).create();
992 }
993 else
994 break;
995 }
996
997 rc = m->pVirtualBox->registerMedium(this, &pMedium, DeviceType_HardDisk);
998 Assert(this == pMedium || FAILED(rc));
999 }
1000
1001 /* Confirm a successful initialization when it's the case */
1002 if (SUCCEEDED(rc))
1003 autoInitSpan.setSucceeded();
1004
1005 return rc;
1006}
1007
1008/**
1009 * Initializes the medium object by opening the storage unit at the specified
1010 * location. The enOpenMode parameter defines whether the medium will be opened
1011 * read/write or read-only.
1012 *
1013 * This gets called by VirtualBox::OpenMedium() and also by
1014 * Machine::AttachDevice() and createImplicitDiffs() when new diff
1015 * images are created.
1016 *
1017 * There is no registry for this case since starting with VirtualBox 4.0, we
1018 * no longer add opened media to a registry automatically (this is deferred
1019 * until the medium is attached to a machine).
1020 *
1021 * For hard disks, the UUID, format and the parent of this medium will be
1022 * determined when reading the medium storage unit. For DVD and floppy images,
1023 * which have no UUIDs in their storage units, new UUIDs are created.
1024 * If the detected or set parent is not known to VirtualBox, then this method
1025 * will fail.
1026 *
1027 * @param aVirtualBox VirtualBox object.
1028 * @param aLocation Storage unit location.
1029 * @param enOpenMode Whether to open the medium read/write or read-only.
1030 * @param fForceNewUuid Whether a new UUID should be set to avoid duplicates.
1031 * @param aDeviceType Device type of medium.
1032 */
1033HRESULT Medium::init(VirtualBox *aVirtualBox,
1034 const Utf8Str &aLocation,
1035 HDDOpenMode enOpenMode,
1036 bool fForceNewUuid,
1037 DeviceType_T aDeviceType)
1038{
1039 AssertReturn(aVirtualBox, E_INVALIDARG);
1040 AssertReturn(!aLocation.isEmpty(), E_INVALIDARG);
1041
1042 HRESULT rc = S_OK;
1043
1044 {
1045 /* Enclose the state transition NotReady->InInit->Ready */
1046 AutoInitSpan autoInitSpan(this);
1047 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1048
1049 unconst(m->pVirtualBox) = aVirtualBox;
1050
1051 /* there must be a storage unit */
1052 m->state = MediumState_Created;
1053
1054 /* remember device type for correct unregistering later */
1055 m->devType = aDeviceType;
1056
1057 /* cannot be a host drive */
1058 m->hostDrive = false;
1059
1060 /* remember the open mode (defaults to ReadWrite) */
1061 m->hddOpenMode = enOpenMode;
1062
1063 if (aDeviceType == DeviceType_DVD)
1064 m->type = MediumType_Readonly;
1065 else if (aDeviceType == DeviceType_Floppy)
1066 m->type = MediumType_Writethrough;
1067
1068 rc = setLocation(aLocation);
1069 if (FAILED(rc)) return rc;
1070
1071 /* get all the information about the medium from the storage unit */
1072 if (fForceNewUuid)
1073 unconst(m->uuidImage).create();
1074
1075 m->state = MediumState_Inaccessible;
1076 m->strLastAccessError = tr("Accessibility check was not yet performed");
1077
1078 /* Confirm a successful initialization before the call to queryInfo.
1079 * Otherwise we can end up with a AutoCaller deadlock because the
1080 * medium becomes visible but is not marked as initialized. Causes
1081 * locking trouble (e.g. trying to save media registries) which is
1082 * hard to solve. */
1083 autoInitSpan.setSucceeded();
1084 }
1085
1086 /* we're normal code from now on, no longer init */
1087 AutoCaller autoCaller(this);
1088 if (FAILED(autoCaller.rc()))
1089 return autoCaller.rc();
1090
1091 /* need to call queryInfo immediately to correctly place the medium in
1092 * the respective media tree and update other information such as uuid */
1093 rc = queryInfo(fForceNewUuid /* fSetImageId */, false /* fSetParentId */);
1094 if (SUCCEEDED(rc))
1095 {
1096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 /* if the storage unit is not accessible, it's not acceptable for the
1099 * newly opened media so convert this into an error */
1100 if (m->state == MediumState_Inaccessible)
1101 {
1102 Assert(!m->strLastAccessError.isEmpty());
1103 rc = setError(E_FAIL, "%s", m->strLastAccessError.c_str());
1104 alock.release();
1105 autoCaller.release();
1106 uninit();
1107 }
1108 else
1109 {
1110 AssertStmt(!m->id.isZero(),
1111 alock.release(); autoCaller.release(); uninit(); return E_FAIL);
1112
1113 /* storage format must be detected by Medium::queryInfo if the
1114 * medium is accessible */
1115 AssertStmt(!m->strFormat.isEmpty(),
1116 alock.release(); autoCaller.release(); uninit(); return E_FAIL);
1117 }
1118 }
1119 else
1120 {
1121 /* opening this image failed, mark the object as dead */
1122 autoCaller.release();
1123 uninit();
1124 }
1125
1126 return rc;
1127}
1128
1129/**
1130 * Initializes the medium object by loading its data from the given settings
1131 * node. In this mode, the medium will always be opened read/write.
1132 *
1133 * In this case, since we're loading from a registry, uuidMachineRegistry is
1134 * always set: it's either the global registry UUID or a machine UUID when
1135 * loading from a per-machine registry.
1136 *
1137 * @param aVirtualBox VirtualBox object.
1138 * @param aParent Parent medium disk or NULL for a root (base) medium.
1139 * @param aDeviceType Device type of the medium.
1140 * @param uuidMachineRegistry The registry to which this medium should be added (global registry UUID or machine UUID).
1141 * @param aNode Configuration settings.
1142 * @param strMachineFolder The machine folder with which to resolve relative paths; if empty, then we use the VirtualBox home directory
1143 *
1144 * @note Locks the medium tree for writing.
1145 */
1146HRESULT Medium::init(VirtualBox *aVirtualBox,
1147 Medium *aParent,
1148 DeviceType_T aDeviceType,
1149 const Guid &uuidMachineRegistry,
1150 const settings::Medium &data,
1151 const Utf8Str &strMachineFolder)
1152{
1153 using namespace settings;
1154
1155 AssertReturn(aVirtualBox, E_INVALIDARG);
1156
1157 /* Enclose the state transition NotReady->InInit->Ready */
1158 AutoInitSpan autoInitSpan(this);
1159 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1160
1161 HRESULT rc = S_OK;
1162
1163 unconst(m->pVirtualBox) = aVirtualBox;
1164
1165 if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
1166 m->llRegistryIDs.push_back(uuidMachineRegistry);
1167
1168 /* register with VirtualBox/parent early, since uninit() will
1169 * unconditionally unregister on failure */
1170 if (aParent)
1171 {
1172 // differencing medium: add to parent
1173 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1174 m->pParent = aParent;
1175 aParent->m->llChildren.push_back(this);
1176 }
1177
1178 /* see below why we don't call Medium::queryInfo (and therefore treat
1179 * the medium as inaccessible for now */
1180 m->state = MediumState_Inaccessible;
1181 m->strLastAccessError = tr("Accessibility check was not yet performed");
1182
1183 /* required */
1184 unconst(m->id) = data.uuid;
1185
1186 /* assume not a host drive */
1187 m->hostDrive = false;
1188
1189 /* optional */
1190 m->strDescription = data.strDescription;
1191
1192 /* required */
1193 if (aDeviceType == DeviceType_HardDisk)
1194 {
1195 AssertReturn(!data.strFormat.isEmpty(), E_FAIL);
1196 rc = setFormat(data.strFormat);
1197 if (FAILED(rc)) return rc;
1198 }
1199 else
1200 {
1201 /// @todo handle host drive settings here as well?
1202 if (!data.strFormat.isEmpty())
1203 rc = setFormat(data.strFormat);
1204 else
1205 rc = setFormat("RAW");
1206 if (FAILED(rc)) return rc;
1207 }
1208
1209 /* optional, only for diffs, default is false; we can only auto-reset
1210 * diff media so they must have a parent */
1211 if (aParent != NULL)
1212 m->autoReset = data.fAutoReset;
1213 else
1214 m->autoReset = false;
1215
1216 /* properties (after setting the format as it populates the map). Note that
1217 * if some properties are not supported but present in the settings file,
1218 * they will still be read and accessible (for possible backward
1219 * compatibility; we can also clean them up from the XML upon next
1220 * XML format version change if we wish) */
1221 for (settings::StringsMap::const_iterator it = data.properties.begin();
1222 it != data.properties.end();
1223 ++it)
1224 {
1225 const Utf8Str &name = it->first;
1226 const Utf8Str &value = it->second;
1227 m->mapProperties[name] = value;
1228 }
1229
1230 /* try to decrypt an optional iSCSI initiator secret */
1231 settings::StringsMap::const_iterator itCph = data.properties.find("InitiatorSecretEncrypted");
1232 if ( itCph != data.properties.end()
1233 && !itCph->second.isEmpty())
1234 {
1235 Utf8Str strPlaintext;
1236 int vrc = m->pVirtualBox->decryptSetting(&strPlaintext, itCph->second);
1237 if (RT_SUCCESS(vrc))
1238 m->mapProperties["InitiatorSecret"] = strPlaintext;
1239 }
1240
1241 Utf8Str strFull;
1242 if (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
1243 {
1244 // compose full path of the medium, if it's not fully qualified...
1245 // slightly convoluted logic here. If the caller has given us a
1246 // machine folder, then a relative path will be relative to that:
1247 if ( !strMachineFolder.isEmpty()
1248 && !RTPathStartsWithRoot(data.strLocation.c_str())
1249 )
1250 {
1251 strFull = strMachineFolder;
1252 strFull += RTPATH_SLASH;
1253 strFull += data.strLocation;
1254 }
1255 else
1256 {
1257 // Otherwise use the old VirtualBox "make absolute path" logic:
1258 rc = m->pVirtualBox->calculateFullPath(data.strLocation, strFull);
1259 if (FAILED(rc)) return rc;
1260 }
1261 }
1262 else
1263 strFull = data.strLocation;
1264
1265 rc = setLocation(strFull);
1266 if (FAILED(rc)) return rc;
1267
1268 if (aDeviceType == DeviceType_HardDisk)
1269 {
1270 /* type is only for base hard disks */
1271 if (m->pParent.isNull())
1272 m->type = data.hdType;
1273 }
1274 else if (aDeviceType == DeviceType_DVD)
1275 m->type = MediumType_Readonly;
1276 else
1277 m->type = MediumType_Writethrough;
1278
1279 /* remember device type for correct unregistering later */
1280 m->devType = aDeviceType;
1281
1282 LogFlowThisFunc(("m->strLocationFull='%s', m->strFormat=%s, m->id={%RTuuid}\n",
1283 m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw()));
1284
1285 /* Don't call Medium::queryInfo for registered media to prevent the calling
1286 * thread (i.e. the VirtualBox server startup thread) from an unexpected
1287 * freeze but mark it as initially inaccessible instead. The vital UUID,
1288 * location and format properties are read from the registry file above; to
1289 * get the actual state and the rest of the data, the user will have to call
1290 * COMGETTER(State). */
1291
1292 AutoWriteLock treeLock(aVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1293
1294 /* load all children */
1295 for (settings::MediaList::const_iterator it = data.llChildren.begin();
1296 it != data.llChildren.end();
1297 ++it)
1298 {
1299 const settings::Medium &med = *it;
1300
1301 ComObjPtr<Medium> pHD;
1302 pHD.createObject();
1303 rc = pHD->init(aVirtualBox,
1304 this, // parent
1305 aDeviceType,
1306 uuidMachineRegistry,
1307 med, // child data
1308 strMachineFolder);
1309 if (FAILED(rc)) break;
1310
1311 rc = m->pVirtualBox->registerMedium(pHD, &pHD, DeviceType_HardDisk);
1312 if (FAILED(rc)) break;
1313 }
1314
1315 /* Confirm a successful initialization when it's the case */
1316 if (SUCCEEDED(rc))
1317 autoInitSpan.setSucceeded();
1318
1319 return rc;
1320}
1321
1322/**
1323 * Initializes the medium object by providing the host drive information.
1324 * Not used for anything but the host floppy/host DVD case.
1325 *
1326 * There is no registry for this case.
1327 *
1328 * @param aVirtualBox VirtualBox object.
1329 * @param aDeviceType Device type of the medium.
1330 * @param aLocation Location of the host drive.
1331 * @param aDescription Comment for this host drive.
1332 *
1333 * @note Locks VirtualBox lock for writing.
1334 */
1335HRESULT Medium::init(VirtualBox *aVirtualBox,
1336 DeviceType_T aDeviceType,
1337 const Utf8Str &aLocation,
1338 const Utf8Str &aDescription /* = Utf8Str::Empty */)
1339{
1340 ComAssertRet(aDeviceType == DeviceType_DVD || aDeviceType == DeviceType_Floppy, E_INVALIDARG);
1341 ComAssertRet(!aLocation.isEmpty(), E_INVALIDARG);
1342
1343 /* Enclose the state transition NotReady->InInit->Ready */
1344 AutoInitSpan autoInitSpan(this);
1345 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1346
1347 unconst(m->pVirtualBox) = aVirtualBox;
1348
1349 // We do not store host drives in VirtualBox.xml or anywhere else, so if we want
1350 // host drives to be identifiable by UUID and not give the drive a different UUID
1351 // every time VirtualBox starts, we need to fake a reproducible UUID here:
1352 RTUUID uuid;
1353 RTUuidClear(&uuid);
1354 if (aDeviceType == DeviceType_DVD)
1355 memcpy(&uuid.au8[0], "DVD", 3);
1356 else
1357 memcpy(&uuid.au8[0], "FD", 2);
1358 /* use device name, adjusted to the end of uuid, shortened if necessary */
1359 size_t lenLocation = aLocation.length();
1360 if (lenLocation > 12)
1361 memcpy(&uuid.au8[4], aLocation.c_str() + (lenLocation - 12), 12);
1362 else
1363 memcpy(&uuid.au8[4 + 12 - lenLocation], aLocation.c_str(), lenLocation);
1364 unconst(m->id) = uuid;
1365
1366 if (aDeviceType == DeviceType_DVD)
1367 m->type = MediumType_Readonly;
1368 else
1369 m->type = MediumType_Writethrough;
1370 m->devType = aDeviceType;
1371 m->state = MediumState_Created;
1372 m->hostDrive = true;
1373 HRESULT rc = setFormat("RAW");
1374 if (FAILED(rc)) return rc;
1375 rc = setLocation(aLocation);
1376 if (FAILED(rc)) return rc;
1377 m->strDescription = aDescription;
1378
1379 autoInitSpan.setSucceeded();
1380 return S_OK;
1381}
1382
1383/**
1384 * Uninitializes the instance.
1385 *
1386 * Called either from FinalRelease() or by the parent when it gets destroyed.
1387 *
1388 * @note All children of this medium get uninitialized by calling their
1389 * uninit() methods.
1390 */
1391void Medium::uninit()
1392{
1393 /* Enclose the state transition Ready->InUninit->NotReady */
1394 AutoUninitSpan autoUninitSpan(this);
1395 if (autoUninitSpan.uninitDone())
1396 return;
1397
1398 if (!m->formatObj.isNull())
1399 {
1400 /* remove the caller reference we added in setFormat() */
1401 m->formatObj->releaseCaller();
1402 m->formatObj.setNull();
1403 }
1404
1405 if (m->state == MediumState_Deleting)
1406 {
1407 /* This medium has been already deleted (directly or as part of a
1408 * merge). Reparenting has already been done. */
1409 Assert(m->pParent.isNull());
1410 }
1411 else
1412 {
1413 MediaList::iterator it;
1414 for (it = m->llChildren.begin();
1415 it != m->llChildren.end();
1416 ++it)
1417 {
1418 Medium *pChild = *it;
1419 pChild->m->pParent.setNull();
1420 pChild->uninit();
1421 }
1422 m->llChildren.clear(); // this unsets all the ComPtrs and probably calls delete
1423
1424 if (m->pParent)
1425 {
1426 // this is a differencing disk: then remove it from the parent's children list
1427 deparent();
1428 }
1429 }
1430
1431 unconst(m->pVirtualBox) = NULL;
1432}
1433
1434/**
1435 * Internal helper that removes "this" from the list of children of its
1436 * parent. Used in uninit() and other places when reparenting is necessary.
1437 *
1438 * The caller must hold the medium tree lock!
1439 */
1440void Medium::deparent()
1441{
1442 MediaList &llParent = m->pParent->m->llChildren;
1443 for (MediaList::iterator it = llParent.begin();
1444 it != llParent.end();
1445 ++it)
1446 {
1447 Medium *pParentsChild = *it;
1448 if (this == pParentsChild)
1449 {
1450 llParent.erase(it);
1451 break;
1452 }
1453 }
1454 m->pParent.setNull();
1455}
1456
1457/**
1458 * Internal helper that removes "this" from the list of children of its
1459 * parent. Used in uninit() and other places when reparenting is necessary.
1460 *
1461 * The caller must hold the medium tree lock!
1462 */
1463void Medium::setParent(const ComObjPtr<Medium> &pParent)
1464{
1465 m->pParent = pParent;
1466 if (pParent)
1467 pParent->m->llChildren.push_back(this);
1468}
1469
1470
1471////////////////////////////////////////////////////////////////////////////////
1472//
1473// IMedium public methods
1474//
1475////////////////////////////////////////////////////////////////////////////////
1476
1477STDMETHODIMP Medium::COMGETTER(Id)(BSTR *aId)
1478{
1479 CheckComArgOutPointerValid(aId);
1480
1481 AutoCaller autoCaller(this);
1482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1483
1484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1485
1486 m->id.toUtf16().cloneTo(aId);
1487
1488 return S_OK;
1489}
1490
1491STDMETHODIMP Medium::COMGETTER(Description)(BSTR *aDescription)
1492{
1493 CheckComArgOutPointerValid(aDescription);
1494
1495 AutoCaller autoCaller(this);
1496 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1497
1498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1499
1500 m->strDescription.cloneTo(aDescription);
1501
1502 return S_OK;
1503}
1504
1505STDMETHODIMP Medium::COMSETTER(Description)(IN_BSTR aDescription)
1506{
1507 AutoCaller autoCaller(this);
1508 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1509
1510// AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1511
1512 /// @todo update m->description and save the global registry (and local
1513 /// registries of portable VMs referring to this medium), this will also
1514 /// require to add the mRegistered flag to data
1515
1516 NOREF(aDescription);
1517
1518 ReturnComNotImplemented();
1519}
1520
1521STDMETHODIMP Medium::COMGETTER(State)(MediumState_T *aState)
1522{
1523 CheckComArgOutPointerValid(aState);
1524
1525 AutoCaller autoCaller(this);
1526 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1527
1528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1529 *aState = m->state;
1530
1531 return S_OK;
1532}
1533
1534STDMETHODIMP Medium::COMGETTER(Variant)(ComSafeArrayOut(MediumVariant_T, aVariant))
1535{
1536 CheckComArgOutSafeArrayPointerValid(aVariant);
1537
1538 AutoCaller autoCaller(this);
1539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1540
1541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 SafeArray<MediumVariant_T> variants(sizeof(MediumVariant_T)*8);
1544
1545 for (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 * Queries the preferred merge direction from this to the other medium, i.e.
4525 * the one which requires the least amount of I/O and therefore time and
4526 * disk consumption.
4527 *
4528 * @returns Status code.
4529 * @retval E_FAIL in case determining the merge direction fails for some reason,
4530 * for example if getting the size of the media fails. There is no
4531 * error set though and the caller is free to continue to find out
4532 * what was going wrong later. Leaves fMergeForward unset.
4533 * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
4534 * An error is set.
4535 * @param pOther The other medium to merge with.
4536 * @param fMergeForward Resulting preferred merge direction (out).
4537 */
4538HRESULT Medium::queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
4539 bool &fMergeForward)
4540{
4541 AssertReturn(pOther != NULL, E_FAIL);
4542 AssertReturn(pOther != this, E_FAIL);
4543
4544 AutoCaller autoCaller(this);
4545 AssertComRCReturnRC(autoCaller.rc());
4546
4547 AutoCaller otherCaller(pOther);
4548 AssertComRCReturnRC(otherCaller.rc());
4549
4550 HRESULT rc = S_OK;
4551 bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
4552
4553 try
4554 {
4555 // locking: we need the tree lock first because we access parent pointers
4556 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4557
4558 /* more sanity checking and figuring out the current merge direction */
4559 ComObjPtr<Medium> pMedium = getParent();
4560 while (!pMedium.isNull() && pMedium != pOther)
4561 pMedium = pMedium->getParent();
4562 if (pMedium == pOther)
4563 fThisParent = false;
4564 else
4565 {
4566 pMedium = pOther->getParent();
4567 while (!pMedium.isNull() && pMedium != this)
4568 pMedium = pMedium->getParent();
4569 if (pMedium == this)
4570 fThisParent = true;
4571 else
4572 {
4573 Utf8Str tgtLoc;
4574 {
4575 AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
4576 tgtLoc = pOther->getLocationFull();
4577 }
4578
4579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4580 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4581 tr("Media '%s' and '%s' are unrelated"),
4582 m->strLocationFull.c_str(), tgtLoc.c_str());
4583 }
4584 }
4585
4586 /*
4587 * Figure out the preferred merge direction. The current way is to
4588 * get the current sizes of file based images and select the merge
4589 * direction depending on the size.
4590 *
4591 * Can't use the VD API to get current size here as the media might
4592 * be write locked by a running VM. Resort to RTFileQuerySize().
4593 */
4594 int vrc = VINF_SUCCESS;
4595 uint64_t cbMediumThis = 0;
4596 uint64_t cbMediumOther = 0;
4597
4598 if (isMediumFormatFile() && pOther->isMediumFormatFile())
4599 {
4600 vrc = RTFileQuerySize(this->getLocationFull().c_str(), &cbMediumThis);
4601 if (RT_SUCCESS(vrc))
4602 {
4603 vrc = RTFileQuerySize(pOther->getLocationFull().c_str(),
4604 &cbMediumOther);
4605 }
4606
4607 if (RT_FAILURE(vrc))
4608 rc = E_FAIL;
4609 else
4610 {
4611 /*
4612 * Check which merge direction might be more optimal.
4613 * This method is not bullet proof of course as there might
4614 * be overlapping blocks in the images so the file size is
4615 * not the best indicator but it is good enough for our purpose
4616 * and everything else is too complicated, especially when the
4617 * media are used by a running VM.
4618 */
4619 bool fMergeIntoThis = cbMediumThis > cbMediumOther;
4620 fMergeForward = fMergeIntoThis ^ fThisParent;
4621 }
4622 }
4623 }
4624 catch (HRESULT aRC) { rc = aRC; }
4625
4626 return rc;
4627}
4628
4629/**
4630 * Prepares this (source) medium, target medium and all intermediate media
4631 * for the merge operation.
4632 *
4633 * This method is to be called prior to calling the #mergeTo() to perform
4634 * necessary consistency checks and place involved media to appropriate
4635 * states. If #mergeTo() is not called or fails, the state modifications
4636 * performed by this method must be undone by #cancelMergeTo().
4637 *
4638 * See #mergeTo() for more information about merging.
4639 *
4640 * @param pTarget Target medium.
4641 * @param aMachineId Allowed machine attachment. NULL means do not check.
4642 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
4643 * do not check.
4644 * @param fLockMedia Flag whether to lock the medium lock list or not.
4645 * If set to false and the medium lock list locking fails
4646 * later you must call #cancelMergeTo().
4647 * @param fMergeForward Resulting merge direction (out).
4648 * @param pParentForTarget New parent for target medium after merge (out).
4649 * @param aChildrenToReparent List of children of the source which will have
4650 * to be reparented to the target after merge (out).
4651 * @param aMediumLockList Medium locking information (out).
4652 *
4653 * @note Locks medium tree for reading. Locks this object, aTarget and all
4654 * intermediate media for writing.
4655 */
4656HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
4657 const Guid *aMachineId,
4658 const Guid *aSnapshotId,
4659 bool fLockMedia,
4660 bool &fMergeForward,
4661 ComObjPtr<Medium> &pParentForTarget,
4662 MediaList &aChildrenToReparent,
4663 MediumLockList * &aMediumLockList)
4664{
4665 AssertReturn(pTarget != NULL, E_FAIL);
4666 AssertReturn(pTarget != this, E_FAIL);
4667
4668 AutoCaller autoCaller(this);
4669 AssertComRCReturnRC(autoCaller.rc());
4670
4671 AutoCaller targetCaller(pTarget);
4672 AssertComRCReturnRC(targetCaller.rc());
4673
4674 HRESULT rc = S_OK;
4675 fMergeForward = false;
4676 pParentForTarget.setNull();
4677 aChildrenToReparent.clear();
4678 Assert(aMediumLockList == NULL);
4679 aMediumLockList = NULL;
4680
4681 try
4682 {
4683 // locking: we need the tree lock first because we access parent pointers
4684 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4685
4686 /* more sanity checking and figuring out the merge direction */
4687 ComObjPtr<Medium> pMedium = getParent();
4688 while (!pMedium.isNull() && pMedium != pTarget)
4689 pMedium = pMedium->getParent();
4690 if (pMedium == pTarget)
4691 fMergeForward = false;
4692 else
4693 {
4694 pMedium = pTarget->getParent();
4695 while (!pMedium.isNull() && pMedium != this)
4696 pMedium = pMedium->getParent();
4697 if (pMedium == this)
4698 fMergeForward = true;
4699 else
4700 {
4701 Utf8Str tgtLoc;
4702 {
4703 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4704 tgtLoc = pTarget->getLocationFull();
4705 }
4706
4707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4708 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4709 tr("Media '%s' and '%s' are unrelated"),
4710 m->strLocationFull.c_str(), tgtLoc.c_str());
4711 }
4712 }
4713
4714 /* Build the lock list. */
4715 aMediumLockList = new MediumLockList();
4716 treeLock.release();
4717 if (fMergeForward)
4718 rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
4719 true /* fMediumLockWrite */,
4720 NULL,
4721 *aMediumLockList);
4722 else
4723 rc = createMediumLockList(true /* fFailIfInaccessible */,
4724 false /* fMediumLockWrite */,
4725 NULL,
4726 *aMediumLockList);
4727 treeLock.acquire();
4728 if (FAILED(rc))
4729 throw rc;
4730
4731 /* Sanity checking, must be after lock list creation as it depends on
4732 * valid medium states. The medium objects must be accessible. Only
4733 * do this if immediate locking is requested, otherwise it fails when
4734 * we construct a medium lock list for an already running VM. Snapshot
4735 * deletion uses this to simplify its life. */
4736 if (fLockMedia)
4737 {
4738 {
4739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4740 if (m->state != MediumState_Created)
4741 throw setStateError();
4742 }
4743 {
4744 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4745 if (pTarget->m->state != MediumState_Created)
4746 throw pTarget->setStateError();
4747 }
4748 }
4749
4750 /* check medium attachment and other sanity conditions */
4751 if (fMergeForward)
4752 {
4753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4754 if (getChildren().size() > 1)
4755 {
4756 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4757 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4758 m->strLocationFull.c_str(), getChildren().size());
4759 }
4760 /* One backreference is only allowed if the machine ID is not empty
4761 * and it matches the machine the medium is attached to (including
4762 * the snapshot ID if not empty). */
4763 if ( m->backRefs.size() != 0
4764 && ( !aMachineId
4765 || m->backRefs.size() != 1
4766 || aMachineId->isZero()
4767 || *getFirstMachineBackrefId() != *aMachineId
4768 || ( (!aSnapshotId || !aSnapshotId->isZero())
4769 && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
4770 throw setError(VBOX_E_OBJECT_IN_USE,
4771 tr("Medium '%s' is attached to %d virtual machines"),
4772 m->strLocationFull.c_str(), m->backRefs.size());
4773 if (m->type == MediumType_Immutable)
4774 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4775 tr("Medium '%s' is immutable"),
4776 m->strLocationFull.c_str());
4777 if (m->type == MediumType_MultiAttach)
4778 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4779 tr("Medium '%s' is multi-attach"),
4780 m->strLocationFull.c_str());
4781 }
4782 else
4783 {
4784 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4785 if (pTarget->getChildren().size() > 1)
4786 {
4787 throw setError(VBOX_E_OBJECT_IN_USE,
4788 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4789 pTarget->m->strLocationFull.c_str(),
4790 pTarget->getChildren().size());
4791 }
4792 if (pTarget->m->type == MediumType_Immutable)
4793 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4794 tr("Medium '%s' is immutable"),
4795 pTarget->m->strLocationFull.c_str());
4796 if (pTarget->m->type == MediumType_MultiAttach)
4797 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4798 tr("Medium '%s' is multi-attach"),
4799 pTarget->m->strLocationFull.c_str());
4800 }
4801 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
4802 ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
4803 for (pLast = pLastIntermediate;
4804 !pLast.isNull() && pLast != pTarget && pLast != this;
4805 pLast = pLast->getParent())
4806 {
4807 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4808 if (pLast->getChildren().size() > 1)
4809 {
4810 throw setError(VBOX_E_OBJECT_IN_USE,
4811 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4812 pLast->m->strLocationFull.c_str(),
4813 pLast->getChildren().size());
4814 }
4815 if (pLast->m->backRefs.size() != 0)
4816 throw setError(VBOX_E_OBJECT_IN_USE,
4817 tr("Medium '%s' is attached to %d virtual machines"),
4818 pLast->m->strLocationFull.c_str(),
4819 pLast->m->backRefs.size());
4820
4821 }
4822
4823 /* Update medium states appropriately */
4824 {
4825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4826
4827 if (m->state == MediumState_Created)
4828 {
4829 rc = markForDeletion();
4830 if (FAILED(rc))
4831 throw rc;
4832 }
4833 else
4834 {
4835 if (fLockMedia)
4836 throw setStateError();
4837 else if ( m->state == MediumState_LockedWrite
4838 || m->state == MediumState_LockedRead)
4839 {
4840 /* Either mark it for deletion in locked state or allow
4841 * others to have done so. */
4842 if (m->preLockState == MediumState_Created)
4843 markLockedForDeletion();
4844 else if (m->preLockState != MediumState_Deleting)
4845 throw setStateError();
4846 }
4847 else
4848 throw setStateError();
4849 }
4850 }
4851
4852 if (fMergeForward)
4853 {
4854 /* we will need parent to reparent target */
4855 pParentForTarget = getParent();
4856 }
4857 else
4858 {
4859 /* we will need to reparent children of the source */
4860 for (MediaList::const_iterator it = getChildren().begin();
4861 it != getChildren().end();
4862 ++it)
4863 {
4864 pMedium = *it;
4865 if (fLockMedia)
4866 {
4867 rc = pMedium->LockWrite(NULL);
4868 if (FAILED(rc))
4869 throw rc;
4870 }
4871
4872 aChildrenToReparent.push_back(pMedium);
4873 }
4874 }
4875 for (pLast = pLastIntermediate;
4876 !pLast.isNull() && pLast != pTarget && pLast != this;
4877 pLast = pLast->getParent())
4878 {
4879 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4880 if (pLast->m->state == MediumState_Created)
4881 {
4882 rc = pLast->markForDeletion();
4883 if (FAILED(rc))
4884 throw rc;
4885 }
4886 else
4887 throw pLast->setStateError();
4888 }
4889
4890 /* Tweak the lock list in the backward merge case, as the target
4891 * isn't marked to be locked for writing yet. */
4892 if (!fMergeForward)
4893 {
4894 MediumLockList::Base::iterator lockListBegin =
4895 aMediumLockList->GetBegin();
4896 MediumLockList::Base::iterator lockListEnd =
4897 aMediumLockList->GetEnd();
4898 lockListEnd--;
4899 for (MediumLockList::Base::iterator it = lockListBegin;
4900 it != lockListEnd;
4901 ++it)
4902 {
4903 MediumLock &mediumLock = *it;
4904 if (mediumLock.GetMedium() == pTarget)
4905 {
4906 HRESULT rc2 = mediumLock.UpdateLock(true);
4907 AssertComRC(rc2);
4908 break;
4909 }
4910 }
4911 }
4912
4913 if (fLockMedia)
4914 {
4915 treeLock.release();
4916 rc = aMediumLockList->Lock();
4917 treeLock.acquire();
4918 if (FAILED(rc))
4919 {
4920 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4921 throw setError(rc,
4922 tr("Failed to lock media when merging to '%s'"),
4923 pTarget->getLocationFull().c_str());
4924 }
4925 }
4926 }
4927 catch (HRESULT aRC) { rc = aRC; }
4928
4929 if (FAILED(rc))
4930 {
4931 delete aMediumLockList;
4932 aMediumLockList = NULL;
4933 }
4934
4935 return rc;
4936}
4937
4938/**
4939 * Merges this medium to the specified medium which must be either its
4940 * direct ancestor or descendant.
4941 *
4942 * Given this medium is SOURCE and the specified medium is TARGET, we will
4943 * get two variants of the merge operation:
4944 *
4945 * forward merge
4946 * ------------------------->
4947 * [Extra] <- SOURCE <- Intermediate <- TARGET
4948 * Any Del Del LockWr
4949 *
4950 *
4951 * backward merge
4952 * <-------------------------
4953 * TARGET <- Intermediate <- SOURCE <- [Extra]
4954 * LockWr Del Del LockWr
4955 *
4956 * Each diagram shows the involved media on the media chain where
4957 * SOURCE and TARGET belong. Under each medium there is a state value which
4958 * the medium must have at a time of the mergeTo() call.
4959 *
4960 * The media in the square braces may be absent (e.g. when the forward
4961 * operation takes place and SOURCE is the base medium, or when the backward
4962 * merge operation takes place and TARGET is the last child in the chain) but if
4963 * they present they are involved too as shown.
4964 *
4965 * Neither the source medium nor intermediate media may be attached to
4966 * any VM directly or in the snapshot, otherwise this method will assert.
4967 *
4968 * The #prepareMergeTo() method must be called prior to this method to place all
4969 * involved to necessary states and perform other consistency checks.
4970 *
4971 * If @a aWait is @c true then this method will perform the operation on the
4972 * calling thread and will not return to the caller until the operation is
4973 * completed. When this method succeeds, all intermediate medium objects in
4974 * the chain will be uninitialized, the state of the target medium (and all
4975 * involved extra media) will be restored. @a aMediumLockList will not be
4976 * deleted, whether the operation is successful or not. The caller has to do
4977 * this if appropriate. Note that this (source) medium is not uninitialized
4978 * because of possible AutoCaller instances held by the caller of this method
4979 * on the current thread. It's therefore the responsibility of the caller to
4980 * call Medium::uninit() after releasing all callers.
4981 *
4982 * If @a aWait is @c false then this method will create a thread to perform the
4983 * operation asynchronously and will return immediately. If the operation
4984 * succeeds, the thread will uninitialize the source medium object and all
4985 * intermediate medium objects in the chain, reset the state of the target
4986 * medium (and all involved extra media) and delete @a aMediumLockList.
4987 * If the operation fails, the thread will only reset the states of all
4988 * involved media and delete @a aMediumLockList.
4989 *
4990 * When this method fails (regardless of the @a aWait mode), it is a caller's
4991 * responsibility to undo state changes and delete @a aMediumLockList using
4992 * #cancelMergeTo().
4993 *
4994 * If @a aProgress is not NULL but the object it points to is @c null then a new
4995 * progress object will be created and assigned to @a *aProgress on success,
4996 * otherwise the existing progress object is used. If Progress is NULL, then no
4997 * progress object is created/used at all. Note that @a aProgress cannot be
4998 * NULL when @a aWait is @c false (this method will assert in this case).
4999 *
5000 * @param pTarget Target medium.
5001 * @param fMergeForward Merge direction.
5002 * @param pParentForTarget New parent for target medium after merge.
5003 * @param aChildrenToReparent List of children of the source which will have
5004 * to be reparented to the target after merge.
5005 * @param aMediumLockList Medium locking information.
5006 * @param aProgress Where to find/store a Progress object to track operation
5007 * completion.
5008 * @param aWait @c true if this method should block instead of creating
5009 * an asynchronous thread.
5010 *
5011 * @note Locks the tree lock for writing. Locks the media from the chain
5012 * for writing.
5013 */
5014HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
5015 bool fMergeForward,
5016 const ComObjPtr<Medium> &pParentForTarget,
5017 const MediaList &aChildrenToReparent,
5018 MediumLockList *aMediumLockList,
5019 ComObjPtr <Progress> *aProgress,
5020 bool aWait)
5021{
5022 AssertReturn(pTarget != NULL, E_FAIL);
5023 AssertReturn(pTarget != this, E_FAIL);
5024 AssertReturn(aMediumLockList != NULL, E_FAIL);
5025 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5026
5027 AutoCaller autoCaller(this);
5028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5029
5030 AutoCaller targetCaller(pTarget);
5031 AssertComRCReturnRC(targetCaller.rc());
5032
5033 HRESULT rc = S_OK;
5034 ComObjPtr <Progress> pProgress;
5035 Medium::Task *pTask = NULL;
5036
5037 try
5038 {
5039 if (aProgress != NULL)
5040 {
5041 /* use the existing progress object... */
5042 pProgress = *aProgress;
5043
5044 /* ...but create a new one if it is null */
5045 if (pProgress.isNull())
5046 {
5047 Utf8Str tgtName;
5048 {
5049 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5050 tgtName = pTarget->getName();
5051 }
5052
5053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5054
5055 pProgress.createObject();
5056 rc = pProgress->init(m->pVirtualBox,
5057 static_cast<IMedium*>(this),
5058 BstrFmt(tr("Merging medium '%s' to '%s'"),
5059 getName().c_str(),
5060 tgtName.c_str()).raw(),
5061 TRUE /* aCancelable */);
5062 if (FAILED(rc))
5063 throw rc;
5064 }
5065 }
5066
5067 /* setup task object to carry out the operation sync/async */
5068 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
5069 pParentForTarget, aChildrenToReparent,
5070 pProgress, aMediumLockList,
5071 aWait /* fKeepMediumLockList */);
5072 rc = pTask->rc();
5073 AssertComRC(rc);
5074 if (FAILED(rc))
5075 throw rc;
5076 }
5077 catch (HRESULT aRC) { rc = aRC; }
5078
5079 if (SUCCEEDED(rc))
5080 {
5081 if (aWait)
5082 rc = runNow(pTask);
5083 else
5084 rc = startThread(pTask);
5085
5086 if (SUCCEEDED(rc) && aProgress != NULL)
5087 *aProgress = pProgress;
5088 }
5089 else if (pTask != NULL)
5090 delete pTask;
5091
5092 return rc;
5093}
5094
5095/**
5096 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
5097 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
5098 * the medium objects in @a aChildrenToReparent.
5099 *
5100 * @param aChildrenToReparent List of children of the source which will have
5101 * to be reparented to the target after merge.
5102 * @param aMediumLockList Medium locking information.
5103 *
5104 * @note Locks the media from the chain for writing.
5105 */
5106void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
5107 MediumLockList *aMediumLockList)
5108{
5109 AutoCaller autoCaller(this);
5110 AssertComRCReturnVoid(autoCaller.rc());
5111
5112 AssertReturnVoid(aMediumLockList != NULL);
5113
5114 /* Revert media marked for deletion to previous state. */
5115 HRESULT rc;
5116 MediumLockList::Base::const_iterator mediumListBegin =
5117 aMediumLockList->GetBegin();
5118 MediumLockList::Base::const_iterator mediumListEnd =
5119 aMediumLockList->GetEnd();
5120 for (MediumLockList::Base::const_iterator it = mediumListBegin;
5121 it != mediumListEnd;
5122 ++it)
5123 {
5124 const MediumLock &mediumLock = *it;
5125 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5126 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5127
5128 if (pMedium->m->state == MediumState_Deleting)
5129 {
5130 rc = pMedium->unmarkForDeletion();
5131 AssertComRC(rc);
5132 }
5133 }
5134
5135 /* the destructor will do the work */
5136 delete aMediumLockList;
5137
5138 /* unlock the children which had to be reparented */
5139 for (MediaList::const_iterator it = aChildrenToReparent.begin();
5140 it != aChildrenToReparent.end();
5141 ++it)
5142 {
5143 const ComObjPtr<Medium> &pMedium = *it;
5144
5145 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5146 pMedium->UnlockWrite(NULL);
5147 }
5148}
5149
5150/**
5151 * Fix the parent UUID of all children to point to this medium as their
5152 * parent.
5153 */
5154HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
5155{
5156 Assert(!isWriteLockOnCurrentThread());
5157 Assert(!m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5158 MediumLockList mediumLockList;
5159 HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
5160 false /* fMediumLockWrite */,
5161 this,
5162 mediumLockList);
5163 AssertComRCReturnRC(rc);
5164
5165 try
5166 {
5167 PVBOXHDD hdd;
5168 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
5169 ComAssertRCThrow(vrc, E_FAIL);
5170
5171 try
5172 {
5173 MediumLockList::Base::iterator lockListBegin =
5174 mediumLockList.GetBegin();
5175 MediumLockList::Base::iterator lockListEnd =
5176 mediumLockList.GetEnd();
5177 for (MediumLockList::Base::iterator it = lockListBegin;
5178 it != lockListEnd;
5179 ++it)
5180 {
5181 MediumLock &mediumLock = *it;
5182 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5183 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5184
5185 // open the medium
5186 vrc = VDOpen(hdd,
5187 pMedium->m->strFormat.c_str(),
5188 pMedium->m->strLocationFull.c_str(),
5189 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
5190 pMedium->m->vdImageIfaces);
5191 if (RT_FAILURE(vrc))
5192 throw vrc;
5193 }
5194
5195 for (MediaList::const_iterator it = childrenToReparent.begin();
5196 it != childrenToReparent.end();
5197 ++it)
5198 {
5199 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
5200 vrc = VDOpen(hdd,
5201 (*it)->m->strFormat.c_str(),
5202 (*it)->m->strLocationFull.c_str(),
5203 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
5204 (*it)->m->vdImageIfaces);
5205 if (RT_FAILURE(vrc))
5206 throw vrc;
5207
5208 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
5209 if (RT_FAILURE(vrc))
5210 throw vrc;
5211
5212 vrc = VDClose(hdd, false /* fDelete */);
5213 if (RT_FAILURE(vrc))
5214 throw vrc;
5215
5216 (*it)->UnlockWrite(NULL);
5217 }
5218 }
5219 catch (HRESULT aRC) { rc = aRC; }
5220 catch (int aVRC)
5221 {
5222 rc = setError(E_FAIL,
5223 tr("Could not update medium UUID references to parent '%s' (%s)"),
5224 m->strLocationFull.c_str(),
5225 vdError(aVRC).c_str());
5226 }
5227
5228 VDDestroy(hdd);
5229 }
5230 catch (HRESULT aRC) { rc = aRC; }
5231
5232 return rc;
5233}
5234
5235/**
5236 * Used by IAppliance to export disk images.
5237 *
5238 * @param aFilename Filename to create (UTF8).
5239 * @param aFormat Medium format for creating @a aFilename.
5240 * @param aVariant Which exact image format variant to use
5241 * for the destination image.
5242 * @param aVDImageIOCallbacks Pointer to the callback table for a
5243 * VDINTERFACEIO interface. May be NULL.
5244 * @param aVDImageIOUser Opaque data for the callbacks.
5245 * @param aProgress Progress object to use.
5246 * @return
5247 * @note The source format is defined by the Medium instance.
5248 */
5249HRESULT Medium::exportFile(const char *aFilename,
5250 const ComObjPtr<MediumFormat> &aFormat,
5251 MediumVariant_T aVariant,
5252 PVDINTERFACEIO aVDImageIOIf, void *aVDImageIOUser,
5253 const ComObjPtr<Progress> &aProgress)
5254{
5255 AssertPtrReturn(aFilename, E_INVALIDARG);
5256 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
5257 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
5258
5259 AutoCaller autoCaller(this);
5260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5261
5262 HRESULT rc = S_OK;
5263 Medium::Task *pTask = NULL;
5264
5265 try
5266 {
5267 // This needs no extra locks besides what is done in the called methods.
5268
5269 /* Build the source lock list. */
5270 MediumLockList *pSourceMediumLockList(new MediumLockList());
5271 rc = createMediumLockList(true /* fFailIfInaccessible */,
5272 false /* fMediumLockWrite */,
5273 NULL,
5274 *pSourceMediumLockList);
5275 if (FAILED(rc))
5276 {
5277 delete pSourceMediumLockList;
5278 throw rc;
5279 }
5280
5281 rc = pSourceMediumLockList->Lock();
5282 if (FAILED(rc))
5283 {
5284 delete pSourceMediumLockList;
5285 throw setError(rc,
5286 tr("Failed to lock source media '%s'"),
5287 getLocationFull().c_str());
5288 }
5289
5290 /* setup task object to carry out the operation asynchronously */
5291 pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
5292 aVariant, aVDImageIOIf,
5293 aVDImageIOUser, pSourceMediumLockList);
5294 rc = pTask->rc();
5295 AssertComRC(rc);
5296 if (FAILED(rc))
5297 throw rc;
5298 }
5299 catch (HRESULT aRC) { rc = aRC; }
5300
5301 if (SUCCEEDED(rc))
5302 rc = startThread(pTask);
5303 else if (pTask != NULL)
5304 delete pTask;
5305
5306 return rc;
5307}
5308
5309/**
5310 * Used by IAppliance to import disk images.
5311 *
5312 * @param aFilename Filename to read (UTF8).
5313 * @param aFormat Medium format for reading @a aFilename.
5314 * @param aVariant Which exact image format variant to use
5315 * for the destination image.
5316 * @param aVDImageIOCallbacks Pointer to the callback table for a
5317 * VDINTERFACEIO interface. May be NULL.
5318 * @param aVDImageIOUser Opaque data for the callbacks.
5319 * @param aParent Parent medium. May be NULL.
5320 * @param aProgress Progress object to use.
5321 * @return
5322 * @note The destination format is defined by the Medium instance.
5323 */
5324HRESULT Medium::importFile(const char *aFilename,
5325 const ComObjPtr<MediumFormat> &aFormat,
5326 MediumVariant_T aVariant,
5327 PVDINTERFACEIO aVDImageIOIf, void *aVDImageIOUser,
5328 const ComObjPtr<Medium> &aParent,
5329 const ComObjPtr<Progress> &aProgress)
5330{
5331 AssertPtrReturn(aFilename, E_INVALIDARG);
5332 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
5333 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
5334
5335 AutoCaller autoCaller(this);
5336 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5337
5338 HRESULT rc = S_OK;
5339 Medium::Task *pTask = NULL;
5340
5341 try
5342 {
5343 // locking: we need the tree lock first because we access parent pointers
5344 // and we need to write-lock the media involved
5345 uint32_t cHandles = 2;
5346 LockHandle* pHandles[3] = { &m->pVirtualBox->getMediaTreeLockHandle(),
5347 this->lockHandle() };
5348 /* Only add parent to the lock if it is not null */
5349 if (!aParent.isNull())
5350 pHandles[cHandles++] = aParent->lockHandle();
5351 AutoWriteLock alock(cHandles,
5352 pHandles
5353 COMMA_LOCKVAL_SRC_POS);
5354
5355 if ( m->state != MediumState_NotCreated
5356 && m->state != MediumState_Created)
5357 throw setStateError();
5358
5359 /* Build the target lock list. */
5360 MediumLockList *pTargetMediumLockList(new MediumLockList());
5361 alock.release();
5362 rc = createMediumLockList(true /* fFailIfInaccessible */,
5363 true /* fMediumLockWrite */,
5364 aParent,
5365 *pTargetMediumLockList);
5366 alock.acquire();
5367 if (FAILED(rc))
5368 {
5369 delete pTargetMediumLockList;
5370 throw rc;
5371 }
5372
5373 alock.release();
5374 rc = pTargetMediumLockList->Lock();
5375 alock.acquire();
5376 if (FAILED(rc))
5377 {
5378 delete pTargetMediumLockList;
5379 throw setError(rc,
5380 tr("Failed to lock target media '%s'"),
5381 getLocationFull().c_str());
5382 }
5383
5384 /* setup task object to carry out the operation asynchronously */
5385 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,
5386 aVariant, aVDImageIOIf,
5387 aVDImageIOUser, aParent,
5388 pTargetMediumLockList);
5389 rc = pTask->rc();
5390 AssertComRC(rc);
5391 if (FAILED(rc))
5392 throw rc;
5393
5394 if (m->state == MediumState_NotCreated)
5395 m->state = MediumState_Creating;
5396 }
5397 catch (HRESULT aRC) { rc = aRC; }
5398
5399 if (SUCCEEDED(rc))
5400 rc = startThread(pTask);
5401 else if (pTask != NULL)
5402 delete pTask;
5403
5404 return rc;
5405}
5406
5407/**
5408 * Internal version of the public CloneTo API which allows to enable certain
5409 * optimizations to improve speed during VM cloning.
5410 *
5411 * @param aTarget Target medium
5412 * @param aVariant Which exact image format variant to use
5413 * for the destination image.
5414 * @param aParent Parent medium. May be NULL.
5415 * @param aProgress Progress object to use.
5416 * @param idxSrcImageSame The last image in the source chain which has the
5417 * same content as the given image in the destination
5418 * chain. Use UINT32_MAX to disable this optimization.
5419 * @param idxDstImageSame The last image in the destination chain which has the
5420 * same content as the given image in the source chain.
5421 * Use UINT32_MAX to disable this optimization.
5422 * @return
5423 */
5424HRESULT Medium::cloneToEx(const ComObjPtr<Medium> &aTarget, ULONG aVariant,
5425 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
5426 uint32_t idxSrcImageSame, uint32_t idxDstImageSame)
5427{
5428 CheckComArgNotNull(aTarget);
5429 CheckComArgOutPointerValid(aProgress);
5430 ComAssertRet(aTarget != this, E_INVALIDARG);
5431
5432 AutoCaller autoCaller(this);
5433 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5434
5435 HRESULT rc = S_OK;
5436 ComObjPtr<Progress> pProgress;
5437 Medium::Task *pTask = NULL;
5438
5439 try
5440 {
5441 // locking: we need the tree lock first because we access parent pointers
5442 // and we need to write-lock the media involved
5443 uint32_t cHandles = 3;
5444 LockHandle* pHandles[4] = { &m->pVirtualBox->getMediaTreeLockHandle(),
5445 this->lockHandle(),
5446 aTarget->lockHandle() };
5447 /* Only add parent to the lock if it is not null */
5448 if (!aParent.isNull())
5449 pHandles[cHandles++] = aParent->lockHandle();
5450 AutoWriteLock alock(cHandles,
5451 pHandles
5452 COMMA_LOCKVAL_SRC_POS);
5453
5454 if ( aTarget->m->state != MediumState_NotCreated
5455 && aTarget->m->state != MediumState_Created)
5456 throw aTarget->setStateError();
5457
5458 /* Build the source lock list. */
5459 MediumLockList *pSourceMediumLockList(new MediumLockList());
5460 alock.release();
5461 rc = createMediumLockList(true /* fFailIfInaccessible */,
5462 false /* fMediumLockWrite */,
5463 NULL,
5464 *pSourceMediumLockList);
5465 alock.acquire();
5466 if (FAILED(rc))
5467 {
5468 delete pSourceMediumLockList;
5469 throw rc;
5470 }
5471
5472 /* Build the target lock list (including the to-be parent chain). */
5473 MediumLockList *pTargetMediumLockList(new MediumLockList());
5474 alock.release();
5475 rc = aTarget->createMediumLockList(true /* fFailIfInaccessible */,
5476 true /* fMediumLockWrite */,
5477 aParent,
5478 *pTargetMediumLockList);
5479 alock.acquire();
5480 if (FAILED(rc))
5481 {
5482 delete pSourceMediumLockList;
5483 delete pTargetMediumLockList;
5484 throw rc;
5485 }
5486
5487 alock.release();
5488 rc = pSourceMediumLockList->Lock();
5489 alock.acquire();
5490 if (FAILED(rc))
5491 {
5492 delete pSourceMediumLockList;
5493 delete pTargetMediumLockList;
5494 throw setError(rc,
5495 tr("Failed to lock source media '%s'"),
5496 getLocationFull().c_str());
5497 }
5498 alock.release();
5499 rc = pTargetMediumLockList->Lock();
5500 alock.acquire();
5501 if (FAILED(rc))
5502 {
5503 delete pSourceMediumLockList;
5504 delete pTargetMediumLockList;
5505 throw setError(rc,
5506 tr("Failed to lock target media '%s'"),
5507 aTarget->getLocationFull().c_str());
5508 }
5509
5510 pProgress.createObject();
5511 rc = pProgress->init(m->pVirtualBox,
5512 static_cast <IMedium *>(this),
5513 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
5514 TRUE /* aCancelable */);
5515 if (FAILED(rc))
5516 {
5517 delete pSourceMediumLockList;
5518 delete pTargetMediumLockList;
5519 throw rc;
5520 }
5521
5522 /* setup task object to carry out the operation asynchronously */
5523 pTask = new Medium::CloneTask(this, pProgress, aTarget,
5524 (MediumVariant_T)aVariant,
5525 aParent, idxSrcImageSame,
5526 idxDstImageSame, pSourceMediumLockList,
5527 pTargetMediumLockList);
5528 rc = pTask->rc();
5529 AssertComRC(rc);
5530 if (FAILED(rc))
5531 throw rc;
5532
5533 if (aTarget->m->state == MediumState_NotCreated)
5534 aTarget->m->state = MediumState_Creating;
5535 }
5536 catch (HRESULT aRC) { rc = aRC; }
5537
5538 if (SUCCEEDED(rc))
5539 {
5540 rc = startThread(pTask);
5541
5542 if (SUCCEEDED(rc))
5543 pProgress.queryInterfaceTo(aProgress);
5544 }
5545 else if (pTask != NULL)
5546 delete pTask;
5547
5548 return rc;
5549}
5550
5551////////////////////////////////////////////////////////////////////////////////
5552//
5553// Private methods
5554//
5555////////////////////////////////////////////////////////////////////////////////
5556
5557/**
5558 * Queries information from the medium.
5559 *
5560 * As a result of this call, the accessibility state and data members such as
5561 * size and description will be updated with the current information.
5562 *
5563 * @note This method may block during a system I/O call that checks storage
5564 * accessibility.
5565 *
5566 * @note Caller MUST NOT hold the media tree or medium lock.
5567 *
5568 * @note Locks mParent for reading. Locks this object for writing.
5569 *
5570 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
5571 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs())
5572 * @return
5573 */
5574HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
5575{
5576 Assert(!isWriteLockOnCurrentThread());
5577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5578
5579 if ( m->state != MediumState_Created
5580 && m->state != MediumState_Inaccessible
5581 && m->state != MediumState_LockedRead)
5582 return E_FAIL;
5583
5584 HRESULT rc = S_OK;
5585
5586 int vrc = VINF_SUCCESS;
5587
5588 /* check if a blocking queryInfo() call is in progress on some other thread,
5589 * and wait for it to finish if so instead of querying data ourselves */
5590 if (m->queryInfoRunning)
5591 {
5592 Assert( m->state == MediumState_LockedRead
5593 || m->state == MediumState_LockedWrite);
5594
5595 while (m->queryInfoRunning)
5596 {
5597 alock.release();
5598 {
5599 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5600 }
5601 alock.acquire();
5602 }
5603
5604 return S_OK;
5605 }
5606
5607 bool success = false;
5608 Utf8Str lastAccessError;
5609
5610 /* are we dealing with a new medium constructed using the existing
5611 * location? */
5612 bool isImport = m->id.isZero();
5613 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
5614
5615 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
5616 * media because that would prevent necessary modifications
5617 * when opening media of some third-party formats for the first
5618 * time in VirtualBox (such as VMDK for which VDOpen() needs to
5619 * generate an UUID if it is missing) */
5620 if ( m->hddOpenMode == OpenReadOnly
5621 || m->type == MediumType_Readonly
5622 || (!isImport && !fSetImageId && !fSetParentId)
5623 )
5624 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5625
5626 /* Open shareable medium with the appropriate flags */
5627 if (m->type == MediumType_Shareable)
5628 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
5629
5630 /* Lock the medium, which makes the behavior much more consistent */
5631 alock.release();
5632 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
5633 rc = LockRead(NULL);
5634 else
5635 rc = LockWrite(NULL);
5636 if (FAILED(rc)) return rc;
5637 alock.acquire();
5638
5639 /* Copies of the input state fields which are not read-only,
5640 * as we're dropping the lock. CAUTION: be extremely careful what
5641 * you do with the contents of this medium object, as you will
5642 * create races if there are concurrent changes. */
5643 Utf8Str format(m->strFormat);
5644 Utf8Str location(m->strLocationFull);
5645 ComObjPtr<MediumFormat> formatObj = m->formatObj;
5646
5647 /* "Output" values which can't be set because the lock isn't held
5648 * at the time the values are determined. */
5649 Guid mediumId = m->id;
5650 uint64_t mediumSize = 0;
5651 uint64_t mediumLogicalSize = 0;
5652
5653 /* Flag whether a base image has a non-zero parent UUID and thus
5654 * need repairing after it was closed again. */
5655 bool fRepairImageZeroParentUuid = false;
5656
5657 /* release the object lock before a lengthy operation, and take the
5658 * opportunity to have a media tree lock, too, which isn't held initially */
5659 m->queryInfoRunning = true;
5660 alock.release();
5661 Assert(!isWriteLockOnCurrentThread());
5662 Assert(!m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5663 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5664 treeLock.release();
5665
5666 /* Note that taking the queryInfoSem after leaving the object lock above
5667 * can lead to short spinning of the loops waiting for queryInfo() to
5668 * complete. This is unavoidable since the other order causes a lock order
5669 * violation: here it would be requesting the object lock (at the beginning
5670 * of the method), then queryInfoSem, and below the other way round. */
5671 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5672
5673 try
5674 {
5675 /* skip accessibility checks for host drives */
5676 if (m->hostDrive)
5677 {
5678 success = true;
5679 throw S_OK;
5680 }
5681
5682 PVBOXHDD hdd;
5683 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
5684 ComAssertRCThrow(vrc, E_FAIL);
5685
5686 try
5687 {
5688 /** @todo This kind of opening of media is assuming that diff
5689 * media can be opened as base media. Should be documented that
5690 * it must work for all medium format backends. */
5691 vrc = VDOpen(hdd,
5692 format.c_str(),
5693 location.c_str(),
5694 uOpenFlags | m->uOpenFlagsDef,
5695 m->vdImageIfaces);
5696 if (RT_FAILURE(vrc))
5697 {
5698 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
5699 location.c_str(), vdError(vrc).c_str());
5700 throw S_OK;
5701 }
5702
5703 if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
5704 {
5705 /* Modify the UUIDs if necessary. The associated fields are
5706 * not modified by other code, so no need to copy. */
5707 if (fSetImageId)
5708 {
5709 alock.acquire();
5710 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
5711 alock.release();
5712 ComAssertRCThrow(vrc, E_FAIL);
5713 mediumId = m->uuidImage;
5714 }
5715 if (fSetParentId)
5716 {
5717 alock.acquire();
5718 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
5719 alock.release();
5720 ComAssertRCThrow(vrc, E_FAIL);
5721 }
5722 /* zap the information, these are no long-term members */
5723 alock.acquire();
5724 unconst(m->uuidImage).clear();
5725 unconst(m->uuidParentImage).clear();
5726 alock.release();
5727
5728 /* check the UUID */
5729 RTUUID uuid;
5730 vrc = VDGetUuid(hdd, 0, &uuid);
5731 ComAssertRCThrow(vrc, E_FAIL);
5732
5733 if (isImport)
5734 {
5735 mediumId = uuid;
5736
5737 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
5738 // only when importing a VDMK that has no UUID, create one in memory
5739 mediumId.create();
5740 }
5741 else
5742 {
5743 Assert(!mediumId.isZero());
5744
5745 if (mediumId != uuid)
5746 {
5747 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
5748 lastAccessError = Utf8StrFmt(
5749 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
5750 &uuid,
5751 location.c_str(),
5752 mediumId.raw(),
5753 m->pVirtualBox->settingsFilePath().c_str());
5754 throw S_OK;
5755 }
5756 }
5757 }
5758 else
5759 {
5760 /* the backend does not support storing UUIDs within the
5761 * underlying storage so use what we store in XML */
5762
5763 if (fSetImageId)
5764 {
5765 /* set the UUID if an API client wants to change it */
5766 alock.acquire();
5767 mediumId = m->uuidImage;
5768 alock.release();
5769 }
5770 else if (isImport)
5771 {
5772 /* generate an UUID for an imported UUID-less medium */
5773 mediumId.create();
5774 }
5775 }
5776
5777 /* set the image uuid before the below parent uuid handling code
5778 * might place it somewhere in the media tree, so that the medium
5779 * UUID is valid at this point */
5780 alock.acquire();
5781 if (isImport || fSetImageId)
5782 unconst(m->id) = mediumId;
5783 alock.release();
5784
5785 /* get the medium variant */
5786 unsigned uImageFlags;
5787 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5788 ComAssertRCThrow(vrc, E_FAIL);
5789 alock.acquire();
5790 m->variant = (MediumVariant_T)uImageFlags;
5791 alock.release();
5792
5793 /* check/get the parent uuid and update corresponding state */
5794 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
5795 {
5796 RTUUID parentId;
5797 vrc = VDGetParentUuid(hdd, 0, &parentId);
5798 ComAssertRCThrow(vrc, E_FAIL);
5799
5800 /* streamOptimized VMDK images are only accepted as base
5801 * images, as this allows automatic repair of OVF appliances.
5802 * Since such images don't support random writes they will not
5803 * be created for diff images. Only an overly smart user might
5804 * manually create this case. Too bad for him. */
5805 if ( (isImport || fSetParentId)
5806 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
5807 {
5808 /* the parent must be known to us. Note that we freely
5809 * call locking methods of mVirtualBox and parent, as all
5810 * relevant locks must be already held. There may be no
5811 * concurrent access to the just opened medium on other
5812 * threads yet (and init() will fail if this method reports
5813 * MediumState_Inaccessible) */
5814
5815 ComObjPtr<Medium> pParent;
5816 if (RTUuidIsNull(&parentId))
5817 rc = VBOX_E_OBJECT_NOT_FOUND;
5818 else
5819 rc = m->pVirtualBox->findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
5820 if (FAILED(rc))
5821 {
5822 if (fSetImageId && !fSetParentId)
5823 {
5824 /* If the image UUID gets changed for an existing
5825 * image then the parent UUID can be stale. In such
5826 * cases clear the parent information. The parent
5827 * information may/will be re-set later if the
5828 * API client wants to adjust a complete medium
5829 * hierarchy one by one. */
5830 rc = S_OK;
5831 alock.acquire();
5832 RTUuidClear(&parentId);
5833 vrc = VDSetParentUuid(hdd, 0, &parentId);
5834 alock.release();
5835 ComAssertRCThrow(vrc, E_FAIL);
5836 }
5837 else
5838 {
5839 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
5840 &parentId, location.c_str(),
5841 m->pVirtualBox->settingsFilePath().c_str());
5842 throw S_OK;
5843 }
5844 }
5845
5846 /* we set mParent & children() */
5847 treeLock.acquire();
5848
5849 if (m->pParent)
5850 deparent();
5851 setParent(pParent);
5852
5853 treeLock.release();
5854 }
5855 else
5856 {
5857 /* we access mParent */
5858 treeLock.acquire();
5859
5860 /* check that parent UUIDs match. Note that there's no need
5861 * for the parent's AutoCaller (our lifetime is bound to
5862 * it) */
5863
5864 if (m->pParent.isNull())
5865 {
5866 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
5867 * and 3.1.0-3.1.8 there are base images out there
5868 * which have a non-zero parent UUID. No point in
5869 * complaining about them, instead automatically
5870 * repair the problem. Later we can bring back the
5871 * error message, but we should wait until really
5872 * most users have repaired their images, either with
5873 * VBoxFixHdd or this way. */
5874#if 1
5875 fRepairImageZeroParentUuid = true;
5876#else /* 0 */
5877 lastAccessError = Utf8StrFmt(
5878 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
5879 location.c_str(),
5880 m->pVirtualBox->settingsFilePath().c_str());
5881 treeLock.release();
5882 throw S_OK;
5883#endif /* 0 */
5884 }
5885
5886 {
5887 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
5888 if ( !fRepairImageZeroParentUuid
5889 && m->pParent->getState() != MediumState_Inaccessible
5890 && m->pParent->getId() != parentId)
5891 {
5892 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
5893 lastAccessError = Utf8StrFmt(
5894 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
5895 &parentId, location.c_str(),
5896 m->pParent->getId().raw(),
5897 m->pVirtualBox->settingsFilePath().c_str());
5898 parentLock.release();
5899 treeLock.release();
5900 throw S_OK;
5901 }
5902 }
5903
5904 /// @todo NEWMEDIA what to do if the parent is not
5905 /// accessible while the diff is? Probably nothing. The
5906 /// real code will detect the mismatch anyway.
5907
5908 treeLock.release();
5909 }
5910 }
5911
5912 mediumSize = VDGetFileSize(hdd, 0);
5913 mediumLogicalSize = VDGetSize(hdd, 0);
5914
5915 success = true;
5916 }
5917 catch (HRESULT aRC)
5918 {
5919 rc = aRC;
5920 }
5921
5922 vrc = VDDestroy(hdd);
5923 if (RT_FAILURE(vrc))
5924 {
5925 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
5926 location.c_str(), vdError(vrc).c_str());
5927 success = false;
5928 throw S_OK;
5929 }
5930 }
5931 catch (HRESULT aRC)
5932 {
5933 rc = aRC;
5934 }
5935
5936 treeLock.acquire();
5937 alock.acquire();
5938
5939 if (success)
5940 {
5941 m->size = mediumSize;
5942 m->logicalSize = mediumLogicalSize;
5943 m->strLastAccessError.setNull();
5944 }
5945 else
5946 {
5947 m->strLastAccessError = lastAccessError;
5948 LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
5949 location.c_str(), m->strLastAccessError.c_str(),
5950 rc, vrc));
5951 }
5952
5953 /* unblock anyone waiting for the queryInfo results */
5954 qlock.release();
5955 m->queryInfoRunning = false;
5956
5957 /* Set the proper state according to the result of the check */
5958 if (success)
5959 m->preLockState = MediumState_Created;
5960 else
5961 m->preLockState = MediumState_Inaccessible;
5962
5963 HRESULT rc2;
5964 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
5965 rc2 = UnlockRead(NULL);
5966 else
5967 rc2 = UnlockWrite(NULL);
5968 if (SUCCEEDED(rc) && FAILED(rc2))
5969 rc = rc2;
5970 if (FAILED(rc)) return rc;
5971
5972 /* If this is a base image which incorrectly has a parent UUID set,
5973 * repair the image now by zeroing the parent UUID. This is only done
5974 * when we have structural information from a config file, on import
5975 * this is not possible. If someone would accidentally call openMedium
5976 * with a diff image before the base is registered this would destroy
5977 * the diff. Not acceptable. */
5978 if (fRepairImageZeroParentUuid)
5979 {
5980 rc = LockWrite(NULL);
5981 if (FAILED(rc)) return rc;
5982
5983 alock.release();
5984
5985 try
5986 {
5987 PVBOXHDD hdd;
5988 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
5989 ComAssertRCThrow(vrc, E_FAIL);
5990
5991 try
5992 {
5993 vrc = VDOpen(hdd,
5994 format.c_str(),
5995 location.c_str(),
5996 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
5997 m->vdImageIfaces);
5998 if (RT_FAILURE(vrc))
5999 throw S_OK;
6000
6001 RTUUID zeroParentUuid;
6002 RTUuidClear(&zeroParentUuid);
6003 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
6004 ComAssertRCThrow(vrc, E_FAIL);
6005 }
6006 catch (HRESULT aRC)
6007 {
6008 rc = aRC;
6009 }
6010
6011 VDDestroy(hdd);
6012 }
6013 catch (HRESULT aRC)
6014 {
6015 rc = aRC;
6016 }
6017
6018 rc = UnlockWrite(NULL);
6019 if (SUCCEEDED(rc) && FAILED(rc2))
6020 rc = rc2;
6021 if (FAILED(rc)) return rc;
6022 }
6023
6024 return rc;
6025}
6026
6027/**
6028 * Performs extra checks if the medium can be closed and returns S_OK in
6029 * this case. Otherwise, returns a respective error message. Called by
6030 * Close() under the medium tree lock and the medium lock.
6031 *
6032 * @note Also reused by Medium::Reset().
6033 *
6034 * @note Caller must hold the media tree write lock!
6035 */
6036HRESULT Medium::canClose()
6037{
6038 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6039
6040 if (getChildren().size() != 0)
6041 return setError(VBOX_E_OBJECT_IN_USE,
6042 tr("Cannot close medium '%s' because it has %d child media"),
6043 m->strLocationFull.c_str(), getChildren().size());
6044
6045 return S_OK;
6046}
6047
6048/**
6049 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
6050 *
6051 * @note Caller must have locked the media tree lock for writing!
6052 */
6053HRESULT Medium::unregisterWithVirtualBox()
6054{
6055 /* Note that we need to de-associate ourselves from the parent to let
6056 * unregisterMedium() properly save the registry */
6057
6058 /* we modify mParent and access children */
6059 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6060
6061 Medium *pParentBackup = m->pParent;
6062 AssertReturn(getChildren().size() == 0, E_FAIL);
6063 if (m->pParent)
6064 deparent();
6065
6066 HRESULT rc = m->pVirtualBox->unregisterMedium(this);
6067 if (FAILED(rc))
6068 {
6069 if (pParentBackup)
6070 {
6071 // re-associate with the parent as we are still relatives in the registry
6072 m->pParent = pParentBackup;
6073 m->pParent->m->llChildren.push_back(this);
6074 }
6075 }
6076
6077 return rc;
6078}
6079
6080/**
6081 * Like SetProperty but do not trigger a settings store. Only for internal use!
6082 */
6083HRESULT Medium::setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
6084{
6085 AutoCaller autoCaller(this);
6086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6087
6088 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
6089
6090 switch (m->state)
6091 {
6092 case MediumState_Created:
6093 case MediumState_Inaccessible:
6094 break;
6095 default:
6096 return setStateError();
6097 }
6098
6099 m->mapProperties[aName] = aValue;
6100
6101 return S_OK;
6102}
6103
6104/**
6105 * Sets the extended error info according to the current media state.
6106 *
6107 * @note Must be called from under this object's write or read lock.
6108 */
6109HRESULT Medium::setStateError()
6110{
6111 HRESULT rc = E_FAIL;
6112
6113 switch (m->state)
6114 {
6115 case MediumState_NotCreated:
6116 {
6117 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6118 tr("Storage for the medium '%s' is not created"),
6119 m->strLocationFull.c_str());
6120 break;
6121 }
6122 case MediumState_Created:
6123 {
6124 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6125 tr("Storage for the medium '%s' is already created"),
6126 m->strLocationFull.c_str());
6127 break;
6128 }
6129 case MediumState_LockedRead:
6130 {
6131 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6132 tr("Medium '%s' is locked for reading by another task"),
6133 m->strLocationFull.c_str());
6134 break;
6135 }
6136 case MediumState_LockedWrite:
6137 {
6138 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6139 tr("Medium '%s' is locked for writing by another task"),
6140 m->strLocationFull.c_str());
6141 break;
6142 }
6143 case MediumState_Inaccessible:
6144 {
6145 /* be in sync with Console::powerUpThread() */
6146 if (!m->strLastAccessError.isEmpty())
6147 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6148 tr("Medium '%s' is not accessible. %s"),
6149 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
6150 else
6151 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6152 tr("Medium '%s' is not accessible"),
6153 m->strLocationFull.c_str());
6154 break;
6155 }
6156 case MediumState_Creating:
6157 {
6158 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6159 tr("Storage for the medium '%s' is being created"),
6160 m->strLocationFull.c_str());
6161 break;
6162 }
6163 case MediumState_Deleting:
6164 {
6165 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6166 tr("Storage for the medium '%s' is being deleted"),
6167 m->strLocationFull.c_str());
6168 break;
6169 }
6170 default:
6171 {
6172 AssertFailed();
6173 break;
6174 }
6175 }
6176
6177 return rc;
6178}
6179
6180/**
6181 * Sets the value of m->strLocationFull. The given location must be a fully
6182 * qualified path; relative paths are not supported here.
6183 *
6184 * As a special exception, if the specified location is a file path that ends with '/'
6185 * then the file name part will be generated by this method automatically in the format
6186 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
6187 * and assign to this medium, and <ext> is the default extension for this
6188 * medium's storage format. Note that this procedure requires the media state to
6189 * be NotCreated and will return a failure otherwise.
6190 *
6191 * @param aLocation Location of the storage unit. If the location is a FS-path,
6192 * then it can be relative to the VirtualBox home directory.
6193 * @param aFormat Optional fallback format if it is an import and the format
6194 * cannot be determined.
6195 *
6196 * @note Must be called from under this object's write lock.
6197 */
6198HRESULT Medium::setLocation(const Utf8Str &aLocation,
6199 const Utf8Str &aFormat /* = Utf8Str::Empty */)
6200{
6201 AssertReturn(!aLocation.isEmpty(), E_FAIL);
6202
6203 AutoCaller autoCaller(this);
6204 AssertComRCReturnRC(autoCaller.rc());
6205
6206 /* formatObj may be null only when initializing from an existing path and
6207 * no format is known yet */
6208 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
6209 || ( autoCaller.state() == InInit
6210 && m->state != MediumState_NotCreated
6211 && m->id.isZero()
6212 && m->strFormat.isEmpty()
6213 && m->formatObj.isNull()),
6214 E_FAIL);
6215
6216 /* are we dealing with a new medium constructed using the existing
6217 * location? */
6218 bool isImport = m->strFormat.isEmpty();
6219
6220 if ( isImport
6221 || ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
6222 && !m->hostDrive))
6223 {
6224 Guid id;
6225
6226 Utf8Str locationFull(aLocation);
6227
6228 if (m->state == MediumState_NotCreated)
6229 {
6230 /* must be a file (formatObj must be already known) */
6231 Assert(m->formatObj->getCapabilities() & MediumFormatCapabilities_File);
6232
6233 if (RTPathFilename(aLocation.c_str()) == NULL)
6234 {
6235 /* no file name is given (either an empty string or ends with a
6236 * slash), generate a new UUID + file name if the state allows
6237 * this */
6238
6239 ComAssertMsgRet(!m->formatObj->getFileExtensions().empty(),
6240 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
6241 E_FAIL);
6242
6243 Utf8Str strExt = m->formatObj->getFileExtensions().front();
6244 ComAssertMsgRet(!strExt.isEmpty(),
6245 ("Default extension must not be empty\n"),
6246 E_FAIL);
6247
6248 id.create();
6249
6250 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
6251 aLocation.c_str(), id.raw(), strExt.c_str());
6252 }
6253 }
6254
6255 // we must always have full paths now (if it refers to a file)
6256 if ( ( m->formatObj.isNull()
6257 || m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
6258 && !RTPathStartsWithRoot(locationFull.c_str()))
6259 return setError(VBOX_E_FILE_ERROR,
6260 tr("The given path '%s' is not fully qualified"),
6261 locationFull.c_str());
6262
6263 /* detect the backend from the storage unit if importing */
6264 if (isImport)
6265 {
6266 VDTYPE enmType = VDTYPE_INVALID;
6267 char *backendName = NULL;
6268
6269 int vrc = VINF_SUCCESS;
6270
6271 /* is it a file? */
6272 {
6273 RTFILE file;
6274 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
6275 if (RT_SUCCESS(vrc))
6276 RTFileClose(file);
6277 }
6278 if (RT_SUCCESS(vrc))
6279 {
6280 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
6281 locationFull.c_str(), &backendName, &enmType);
6282 }
6283 else if ( vrc != VERR_FILE_NOT_FOUND
6284 && vrc != VERR_PATH_NOT_FOUND
6285 && vrc != VERR_ACCESS_DENIED
6286 && locationFull != aLocation)
6287 {
6288 /* assume it's not a file, restore the original location */
6289 locationFull = aLocation;
6290 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
6291 locationFull.c_str(), &backendName, &enmType);
6292 }
6293
6294 if (RT_FAILURE(vrc))
6295 {
6296 if (vrc == VERR_ACCESS_DENIED)
6297 return setError(VBOX_E_FILE_ERROR,
6298 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
6299 locationFull.c_str(), vrc);
6300 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
6301 return setError(VBOX_E_FILE_ERROR,
6302 tr("Could not find file for the medium '%s' (%Rrc)"),
6303 locationFull.c_str(), vrc);
6304 else if (aFormat.isEmpty())
6305 return setError(VBOX_E_IPRT_ERROR,
6306 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
6307 locationFull.c_str(), vrc);
6308 else
6309 {
6310 HRESULT rc = setFormat(aFormat);
6311 /* setFormat() must not fail since we've just used the backend so
6312 * the format object must be there */
6313 AssertComRCReturnRC(rc);
6314 }
6315 }
6316 else if ( enmType == VDTYPE_INVALID
6317 || m->devType != convertToDeviceType(enmType))
6318 {
6319 /*
6320 * The user tried to use a image as a device which is not supported
6321 * by the backend.
6322 */
6323 return setError(E_FAIL,
6324 tr("The medium '%s' can't be used as the requested device type"),
6325 locationFull.c_str());
6326 }
6327 else
6328 {
6329 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
6330
6331 HRESULT rc = setFormat(backendName);
6332 RTStrFree(backendName);
6333
6334 /* setFormat() must not fail since we've just used the backend so
6335 * the format object must be there */
6336 AssertComRCReturnRC(rc);
6337 }
6338 }
6339
6340 m->strLocationFull = locationFull;
6341
6342 /* is it still a file? */
6343 if ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
6344 && (m->state == MediumState_NotCreated)
6345 )
6346 /* assign a new UUID (this UUID will be used when calling
6347 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
6348 * also do that if we didn't generate it to make sure it is
6349 * either generated by us or reset to null */
6350 unconst(m->id) = id;
6351 }
6352 else
6353 m->strLocationFull = aLocation;
6354
6355 return S_OK;
6356}
6357
6358/**
6359 * Checks that the format ID is valid and sets it on success.
6360 *
6361 * Note that this method will caller-reference the format object on success!
6362 * This reference must be released somewhere to let the MediumFormat object be
6363 * uninitialized.
6364 *
6365 * @note Must be called from under this object's write lock.
6366 */
6367HRESULT Medium::setFormat(const Utf8Str &aFormat)
6368{
6369 /* get the format object first */
6370 {
6371 SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
6372 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
6373
6374 unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
6375 if (m->formatObj.isNull())
6376 return setError(E_INVALIDARG,
6377 tr("Invalid medium storage format '%s'"),
6378 aFormat.c_str());
6379
6380 /* reference the format permanently to prevent its unexpected
6381 * uninitialization */
6382 HRESULT rc = m->formatObj->addCaller();
6383 AssertComRCReturnRC(rc);
6384
6385 /* get properties (preinsert them as keys in the map). Note that the
6386 * map doesn't grow over the object life time since the set of
6387 * properties is meant to be constant. */
6388
6389 Assert(m->mapProperties.empty());
6390
6391 for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
6392 it != m->formatObj->getProperties().end();
6393 ++it)
6394 {
6395 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
6396 }
6397 }
6398
6399 unconst(m->strFormat) = aFormat;
6400
6401 return S_OK;
6402}
6403
6404/**
6405 * Converts the Medium device type to the VD type.
6406 */
6407VDTYPE Medium::convertDeviceType()
6408{
6409 VDTYPE enmType;
6410
6411 switch (m->devType)
6412 {
6413 case DeviceType_HardDisk:
6414 enmType = VDTYPE_HDD;
6415 break;
6416 case DeviceType_DVD:
6417 enmType = VDTYPE_DVD;
6418 break;
6419 case DeviceType_Floppy:
6420 enmType = VDTYPE_FLOPPY;
6421 break;
6422 default:
6423 ComAssertFailedRet(VDTYPE_INVALID);
6424 }
6425
6426 return enmType;
6427}
6428
6429/**
6430 * Converts from the VD type to the medium type.
6431 */
6432DeviceType_T Medium::convertToDeviceType(VDTYPE enmType)
6433{
6434 DeviceType_T devType;
6435
6436 switch (enmType)
6437 {
6438 case VDTYPE_HDD:
6439 devType = DeviceType_HardDisk;
6440 break;
6441 case VDTYPE_DVD:
6442 devType = DeviceType_DVD;
6443 break;
6444 case VDTYPE_FLOPPY:
6445 devType = DeviceType_Floppy;
6446 break;
6447 default:
6448 ComAssertFailedRet(DeviceType_Null);
6449 }
6450
6451 return devType;
6452}
6453
6454/**
6455 * Returns the last error message collected by the vdErrorCall callback and
6456 * resets it.
6457 *
6458 * The error message is returned prepended with a dot and a space, like this:
6459 * <code>
6460 * ". <error_text> (%Rrc)"
6461 * </code>
6462 * to make it easily appendable to a more general error message. The @c %Rrc
6463 * format string is given @a aVRC as an argument.
6464 *
6465 * If there is no last error message collected by vdErrorCall or if it is a
6466 * null or empty string, then this function returns the following text:
6467 * <code>
6468 * " (%Rrc)"
6469 * </code>
6470 *
6471 * @note Doesn't do any object locking; it is assumed that the caller makes sure
6472 * the callback isn't called by more than one thread at a time.
6473 *
6474 * @param aVRC VBox error code to use when no error message is provided.
6475 */
6476Utf8Str Medium::vdError(int aVRC)
6477{
6478 Utf8Str error;
6479
6480 if (m->vdError.isEmpty())
6481 error = Utf8StrFmt(" (%Rrc)", aVRC);
6482 else
6483 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
6484
6485 m->vdError.setNull();
6486
6487 return error;
6488}
6489
6490/**
6491 * Error message callback.
6492 *
6493 * Puts the reported error message to the m->vdError field.
6494 *
6495 * @note Doesn't do any object locking; it is assumed that the caller makes sure
6496 * the callback isn't called by more than one thread at a time.
6497 *
6498 * @param pvUser The opaque data passed on container creation.
6499 * @param rc The VBox error code.
6500 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
6501 * @param pszFormat Error message format string.
6502 * @param va Error message arguments.
6503 */
6504/*static*/
6505DECLCALLBACK(void) Medium::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
6506 const char *pszFormat, va_list va)
6507{
6508 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
6509
6510 Medium *that = static_cast<Medium*>(pvUser);
6511 AssertReturnVoid(that != NULL);
6512
6513 if (that->m->vdError.isEmpty())
6514 that->m->vdError =
6515 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
6516 else
6517 that->m->vdError =
6518 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
6519 Utf8Str(pszFormat, va).c_str(), rc);
6520}
6521
6522/* static */
6523DECLCALLBACK(bool) Medium::vdConfigAreKeysValid(void *pvUser,
6524 const char * /* pszzValid */)
6525{
6526 Medium *that = static_cast<Medium*>(pvUser);
6527 AssertReturn(that != NULL, false);
6528
6529 /* we always return true since the only keys we have are those found in
6530 * VDBACKENDINFO */
6531 return true;
6532}
6533
6534/* static */
6535DECLCALLBACK(int) Medium::vdConfigQuerySize(void *pvUser,
6536 const char *pszName,
6537 size_t *pcbValue)
6538{
6539 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
6540
6541 Medium *that = static_cast<Medium*>(pvUser);
6542 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
6543
6544 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
6545 if (it == that->m->mapProperties.end())
6546 return VERR_CFGM_VALUE_NOT_FOUND;
6547
6548 /* we interpret null values as "no value" in Medium */
6549 if (it->second.isEmpty())
6550 return VERR_CFGM_VALUE_NOT_FOUND;
6551
6552 *pcbValue = it->second.length() + 1 /* include terminator */;
6553
6554 return VINF_SUCCESS;
6555}
6556
6557/* static */
6558DECLCALLBACK(int) Medium::vdConfigQuery(void *pvUser,
6559 const char *pszName,
6560 char *pszValue,
6561 size_t cchValue)
6562{
6563 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
6564
6565 Medium *that = static_cast<Medium*>(pvUser);
6566 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
6567
6568 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
6569 if (it == that->m->mapProperties.end())
6570 return VERR_CFGM_VALUE_NOT_FOUND;
6571
6572 /* we interpret null values as "no value" in Medium */
6573 if (it->second.isEmpty())
6574 return VERR_CFGM_VALUE_NOT_FOUND;
6575
6576 const Utf8Str &value = it->second;
6577 if (value.length() >= cchValue)
6578 return VERR_CFGM_NOT_ENOUGH_SPACE;
6579
6580 memcpy(pszValue, value.c_str(), value.length() + 1);
6581
6582 return VINF_SUCCESS;
6583}
6584
6585DECLCALLBACK(int) Medium::vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
6586{
6587 PVDSOCKETINT pSocketInt = NULL;
6588
6589 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
6590 return VERR_NOT_SUPPORTED;
6591
6592 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
6593 if (!pSocketInt)
6594 return VERR_NO_MEMORY;
6595
6596 pSocketInt->hSocket = NIL_RTSOCKET;
6597 *pSock = pSocketInt;
6598 return VINF_SUCCESS;
6599}
6600
6601DECLCALLBACK(int) Medium::vdTcpSocketDestroy(VDSOCKET Sock)
6602{
6603 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6604
6605 if (pSocketInt->hSocket != NIL_RTSOCKET)
6606 RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
6607
6608 RTMemFree(pSocketInt);
6609
6610 return VINF_SUCCESS;
6611}
6612
6613DECLCALLBACK(int) Medium::vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
6614{
6615 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6616
6617 return RTTcpClientConnect(pszAddress, uPort, &pSocketInt->hSocket);
6618}
6619
6620DECLCALLBACK(int) Medium::vdTcpClientClose(VDSOCKET Sock)
6621{
6622 int rc = VINF_SUCCESS;
6623 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6624
6625 rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
6626 pSocketInt->hSocket = NIL_RTSOCKET;
6627 return rc;
6628}
6629
6630DECLCALLBACK(bool) Medium::vdTcpIsClientConnected(VDSOCKET Sock)
6631{
6632 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6633 return pSocketInt->hSocket != NIL_RTSOCKET;
6634}
6635
6636DECLCALLBACK(int) Medium::vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
6637{
6638 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6639 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
6640}
6641
6642DECLCALLBACK(int) Medium::vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
6643{
6644 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6645 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
6646}
6647
6648DECLCALLBACK(int) Medium::vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
6649{
6650 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6651 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
6652}
6653
6654DECLCALLBACK(int) Medium::vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
6655{
6656 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6657 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
6658}
6659
6660DECLCALLBACK(int) Medium::vdTcpFlush(VDSOCKET Sock)
6661{
6662 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6663 return RTTcpFlush(pSocketInt->hSocket);
6664}
6665
6666DECLCALLBACK(int) Medium::vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
6667{
6668 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6669 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
6670}
6671
6672DECLCALLBACK(int) Medium::vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
6673{
6674 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6675 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
6676}
6677
6678DECLCALLBACK(int) Medium::vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
6679{
6680 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6681 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
6682}
6683
6684/**
6685 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
6686 *
6687 * @note When the task is executed by this method, IProgress::notifyComplete()
6688 * is automatically called for the progress object associated with this
6689 * task when the task is finished to signal the operation completion for
6690 * other threads asynchronously waiting for it.
6691 */
6692HRESULT Medium::startThread(Medium::Task *pTask)
6693{
6694#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
6695 /* Extreme paranoia: The calling thread should not hold the medium
6696 * tree lock or any medium lock. Since there is no separate lock class
6697 * for medium objects be even more strict: no other object locks. */
6698 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
6699 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
6700#endif
6701
6702 /// @todo use a more descriptive task name
6703 int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
6704 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
6705 "Medium::Task");
6706 if (RT_FAILURE(vrc))
6707 {
6708 delete pTask;
6709 return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n", vrc);
6710 }
6711
6712 return S_OK;
6713}
6714
6715/**
6716 * Runs Medium::Task::handler() on the current thread instead of creating
6717 * a new one.
6718 *
6719 * This call implies that it is made on another temporary thread created for
6720 * some asynchronous task. Avoid calling it from a normal thread since the task
6721 * operations are potentially lengthy and will block the calling thread in this
6722 * case.
6723 *
6724 * @note When the task is executed by this method, IProgress::notifyComplete()
6725 * is not called for the progress object associated with this task when
6726 * the task is finished. Instead, the result of the operation is returned
6727 * by this method directly and it's the caller's responsibility to
6728 * complete the progress object in this case.
6729 */
6730HRESULT Medium::runNow(Medium::Task *pTask)
6731{
6732#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
6733 /* Extreme paranoia: The calling thread should not hold the medium
6734 * tree lock or any medium lock. Since there is no separate lock class
6735 * for medium objects be even more strict: no other object locks. */
6736 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
6737 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
6738#endif
6739
6740 /* NIL_RTTHREAD indicates synchronous call. */
6741 return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
6742}
6743
6744/**
6745 * Implementation code for the "create base" task.
6746 *
6747 * This only gets started from Medium::CreateBaseStorage() and always runs
6748 * asynchronously. As a result, we always save the VirtualBox.xml file when
6749 * we're done here.
6750 *
6751 * @param task
6752 * @return
6753 */
6754HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
6755{
6756 HRESULT rc = S_OK;
6757
6758 /* these parameters we need after creation */
6759 uint64_t size = 0, logicalSize = 0;
6760 MediumVariant_T variant = MediumVariant_Standard;
6761 bool fGenerateUuid = false;
6762
6763 try
6764 {
6765 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6766
6767 /* The object may request a specific UUID (through a special form of
6768 * the setLocation() argument). Otherwise we have to generate it */
6769 Guid id = m->id;
6770
6771 fGenerateUuid = id.isZero();
6772 if (fGenerateUuid)
6773 {
6774 id.create();
6775 /* VirtualBox::registerMedium() will need UUID */
6776 unconst(m->id) = id;
6777 }
6778
6779 Utf8Str format(m->strFormat);
6780 Utf8Str location(m->strLocationFull);
6781 uint64_t capabilities = m->formatObj->getCapabilities();
6782 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
6783 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
6784 Assert(m->state == MediumState_Creating);
6785
6786 PVBOXHDD hdd;
6787 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6788 ComAssertRCThrow(vrc, E_FAIL);
6789
6790 /* unlock before the potentially lengthy operation */
6791 thisLock.release();
6792
6793 try
6794 {
6795 /* ensure the directory exists */
6796 if (capabilities & MediumFormatCapabilities_File)
6797 {
6798 rc = VirtualBox::ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
6799 if (FAILED(rc))
6800 throw rc;
6801 }
6802
6803 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
6804
6805 vrc = VDCreateBase(hdd,
6806 format.c_str(),
6807 location.c_str(),
6808 task.mSize,
6809 task.mVariant & ~MediumVariant_NoCreateDir,
6810 NULL,
6811 &geo,
6812 &geo,
6813 id.raw(),
6814 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
6815 m->vdImageIfaces,
6816 task.mVDOperationIfaces);
6817 if (RT_FAILURE(vrc))
6818 throw setError(VBOX_E_FILE_ERROR,
6819 tr("Could not create the medium storage unit '%s'%s"),
6820 location.c_str(), vdError(vrc).c_str());
6821
6822 size = VDGetFileSize(hdd, 0);
6823 logicalSize = VDGetSize(hdd, 0);
6824 unsigned uImageFlags;
6825 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6826 if (RT_SUCCESS(vrc))
6827 variant = (MediumVariant_T)uImageFlags;
6828 }
6829 catch (HRESULT aRC) { rc = aRC; }
6830
6831 VDDestroy(hdd);
6832 }
6833 catch (HRESULT aRC) { rc = aRC; }
6834
6835 if (SUCCEEDED(rc))
6836 {
6837 /* register with mVirtualBox as the last step and move to
6838 * Created state only on success (leaving an orphan file is
6839 * better than breaking media registry consistency) */
6840 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6841 ComObjPtr<Medium> pMedium;
6842 rc = m->pVirtualBox->registerMedium(this, &pMedium, DeviceType_HardDisk);
6843 Assert(this == pMedium);
6844 }
6845
6846 // re-acquire the lock before changing state
6847 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6848
6849 if (SUCCEEDED(rc))
6850 {
6851 m->state = MediumState_Created;
6852
6853 m->size = size;
6854 m->logicalSize = logicalSize;
6855 m->variant = variant;
6856
6857 thisLock.release();
6858 markRegistriesModified();
6859 if (task.isAsync())
6860 {
6861 // in asynchronous mode, save settings now
6862 m->pVirtualBox->saveModifiedRegistries();
6863 }
6864 }
6865 else
6866 {
6867 /* back to NotCreated on failure */
6868 m->state = MediumState_NotCreated;
6869
6870 /* reset UUID to prevent it from being reused next time */
6871 if (fGenerateUuid)
6872 unconst(m->id).clear();
6873 }
6874
6875 return rc;
6876}
6877
6878/**
6879 * Implementation code for the "create diff" task.
6880 *
6881 * This task always gets started from Medium::createDiffStorage() and can run
6882 * synchronously or asynchronously depending on the "wait" parameter passed to
6883 * that function. If we run synchronously, the caller expects the medium
6884 * registry modification to be set before returning; otherwise (in asynchronous
6885 * mode), we save the settings ourselves.
6886 *
6887 * @param task
6888 * @return
6889 */
6890HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
6891{
6892 HRESULT rcTmp = S_OK;
6893
6894 const ComObjPtr<Medium> &pTarget = task.mTarget;
6895
6896 uint64_t size = 0, logicalSize = 0;
6897 MediumVariant_T variant = MediumVariant_Standard;
6898 bool fGenerateUuid = false;
6899
6900 try
6901 {
6902 /* Lock both in {parent,child} order. */
6903 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6904
6905 /* The object may request a specific UUID (through a special form of
6906 * the setLocation() argument). Otherwise we have to generate it */
6907 Guid targetId = pTarget->m->id;
6908
6909 fGenerateUuid = targetId.isZero();
6910 if (fGenerateUuid)
6911 {
6912 targetId.create();
6913 /* VirtualBox::registerMedium() will need UUID */
6914 unconst(pTarget->m->id) = targetId;
6915 }
6916
6917 Guid id = m->id;
6918
6919 Utf8Str targetFormat(pTarget->m->strFormat);
6920 Utf8Str targetLocation(pTarget->m->strLocationFull);
6921 uint64_t capabilities = pTarget->m->formatObj->getCapabilities();
6922 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
6923
6924 Assert(pTarget->m->state == MediumState_Creating);
6925 Assert(m->state == MediumState_LockedRead);
6926
6927 PVBOXHDD hdd;
6928 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6929 ComAssertRCThrow(vrc, E_FAIL);
6930
6931 /* the two media are now protected by their non-default states;
6932 * unlock the media before the potentially lengthy operation */
6933 mediaLock.release();
6934
6935 try
6936 {
6937 /* Open all media in the target chain but the last. */
6938 MediumLockList::Base::const_iterator targetListBegin =
6939 task.mpMediumLockList->GetBegin();
6940 MediumLockList::Base::const_iterator targetListEnd =
6941 task.mpMediumLockList->GetEnd();
6942 for (MediumLockList::Base::const_iterator it = targetListBegin;
6943 it != targetListEnd;
6944 ++it)
6945 {
6946 const MediumLock &mediumLock = *it;
6947 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6948
6949 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6950
6951 /* Skip over the target diff medium */
6952 if (pMedium->m->state == MediumState_Creating)
6953 continue;
6954
6955 /* sanity check */
6956 Assert(pMedium->m->state == MediumState_LockedRead);
6957
6958 /* Open all media in appropriate mode. */
6959 vrc = VDOpen(hdd,
6960 pMedium->m->strFormat.c_str(),
6961 pMedium->m->strLocationFull.c_str(),
6962 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
6963 pMedium->m->vdImageIfaces);
6964 if (RT_FAILURE(vrc))
6965 throw setError(VBOX_E_FILE_ERROR,
6966 tr("Could not open the medium storage unit '%s'%s"),
6967 pMedium->m->strLocationFull.c_str(),
6968 vdError(vrc).c_str());
6969 }
6970
6971 /* ensure the target directory exists */
6972 if (capabilities & MediumFormatCapabilities_File)
6973 {
6974 HRESULT rc = VirtualBox::ensureFilePathExists(targetLocation, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
6975 if (FAILED(rc))
6976 throw rc;
6977 }
6978
6979 vrc = VDCreateDiff(hdd,
6980 targetFormat.c_str(),
6981 targetLocation.c_str(),
6982 (task.mVariant & ~MediumVariant_NoCreateDir) | VD_IMAGE_FLAGS_DIFF,
6983 NULL,
6984 targetId.raw(),
6985 id.raw(),
6986 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
6987 pTarget->m->vdImageIfaces,
6988 task.mVDOperationIfaces);
6989 if (RT_FAILURE(vrc))
6990 throw setError(VBOX_E_FILE_ERROR,
6991 tr("Could not create the differencing medium storage unit '%s'%s"),
6992 targetLocation.c_str(), vdError(vrc).c_str());
6993
6994 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
6995 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
6996 unsigned uImageFlags;
6997 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6998 if (RT_SUCCESS(vrc))
6999 variant = (MediumVariant_T)uImageFlags;
7000 }
7001 catch (HRESULT aRC) { rcTmp = aRC; }
7002
7003 VDDestroy(hdd);
7004 }
7005 catch (HRESULT aRC) { rcTmp = aRC; }
7006
7007 MultiResult mrc(rcTmp);
7008
7009 if (SUCCEEDED(mrc))
7010 {
7011 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7012
7013 Assert(pTarget->m->pParent.isNull());
7014
7015 /* associate the child with the parent */
7016 pTarget->m->pParent = this;
7017 m->llChildren.push_back(pTarget);
7018
7019 /** @todo r=klaus neither target nor base() are locked,
7020 * potential race! */
7021 /* diffs for immutable media are auto-reset by default */
7022 pTarget->m->autoReset = (getBase()->m->type == MediumType_Immutable);
7023
7024 /* register with mVirtualBox as the last step and move to
7025 * Created state only on success (leaving an orphan file is
7026 * better than breaking media registry consistency) */
7027 ComObjPtr<Medium> pMedium;
7028 mrc = m->pVirtualBox->registerMedium(pTarget, &pMedium, DeviceType_HardDisk);
7029 Assert(pTarget == pMedium);
7030
7031 if (FAILED(mrc))
7032 /* break the parent association on failure to register */
7033 deparent();
7034 }
7035
7036 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
7037
7038 if (SUCCEEDED(mrc))
7039 {
7040 pTarget->m->state = MediumState_Created;
7041
7042 pTarget->m->size = size;
7043 pTarget->m->logicalSize = logicalSize;
7044 pTarget->m->variant = variant;
7045 }
7046 else
7047 {
7048 /* back to NotCreated on failure */
7049 pTarget->m->state = MediumState_NotCreated;
7050
7051 pTarget->m->autoReset = false;
7052
7053 /* reset UUID to prevent it from being reused next time */
7054 if (fGenerateUuid)
7055 unconst(pTarget->m->id).clear();
7056 }
7057
7058 // deregister the task registered in createDiffStorage()
7059 Assert(m->numCreateDiffTasks != 0);
7060 --m->numCreateDiffTasks;
7061
7062 mediaLock.release();
7063 markRegistriesModified();
7064 if (task.isAsync())
7065 {
7066 // in asynchronous mode, save settings now
7067 m->pVirtualBox->saveModifiedRegistries();
7068 }
7069
7070 /* Note that in sync mode, it's the caller's responsibility to
7071 * unlock the medium. */
7072
7073 return mrc;
7074}
7075
7076/**
7077 * Implementation code for the "merge" task.
7078 *
7079 * This task always gets started from Medium::mergeTo() and can run
7080 * synchronously or asynchronously depending on the "wait" parameter passed to
7081 * that function. If we run synchronously, the caller expects the medium
7082 * registry modification to be set before returning; otherwise (in asynchronous
7083 * mode), we save the settings ourselves.
7084 *
7085 * @param task
7086 * @return
7087 */
7088HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
7089{
7090 HRESULT rcTmp = S_OK;
7091
7092 const ComObjPtr<Medium> &pTarget = task.mTarget;
7093
7094 try
7095 {
7096 PVBOXHDD hdd;
7097 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7098 ComAssertRCThrow(vrc, E_FAIL);
7099
7100 try
7101 {
7102 // Similar code appears in SessionMachine::onlineMergeMedium, so
7103 // if you make any changes below check whether they are applicable
7104 // in that context as well.
7105
7106 unsigned uTargetIdx = VD_LAST_IMAGE;
7107 unsigned uSourceIdx = VD_LAST_IMAGE;
7108 /* Open all media in the chain. */
7109 MediumLockList::Base::iterator lockListBegin =
7110 task.mpMediumLockList->GetBegin();
7111 MediumLockList::Base::iterator lockListEnd =
7112 task.mpMediumLockList->GetEnd();
7113 unsigned i = 0;
7114 for (MediumLockList::Base::iterator it = lockListBegin;
7115 it != lockListEnd;
7116 ++it)
7117 {
7118 MediumLock &mediumLock = *it;
7119 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7120
7121 if (pMedium == this)
7122 uSourceIdx = i;
7123 else if (pMedium == pTarget)
7124 uTargetIdx = i;
7125
7126 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7127
7128 /*
7129 * complex sanity (sane complexity)
7130 *
7131 * The current medium must be in the Deleting (medium is merged)
7132 * or LockedRead (parent medium) state if it is not the target.
7133 * If it is the target it must be in the LockedWrite state.
7134 */
7135 Assert( ( pMedium != pTarget
7136 && ( pMedium->m->state == MediumState_Deleting
7137 || pMedium->m->state == MediumState_LockedRead))
7138 || ( pMedium == pTarget
7139 && pMedium->m->state == MediumState_LockedWrite));
7140
7141 /*
7142 * Medium must be the target, in the LockedRead state
7143 * or Deleting state where it is not allowed to be attached
7144 * to a virtual machine.
7145 */
7146 Assert( pMedium == pTarget
7147 || pMedium->m->state == MediumState_LockedRead
7148 || ( pMedium->m->backRefs.size() == 0
7149 && pMedium->m->state == MediumState_Deleting));
7150 /* The source medium must be in Deleting state. */
7151 Assert( pMedium != this
7152 || pMedium->m->state == MediumState_Deleting);
7153
7154 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7155
7156 if ( pMedium->m->state == MediumState_LockedRead
7157 || pMedium->m->state == MediumState_Deleting)
7158 uOpenFlags = VD_OPEN_FLAGS_READONLY;
7159 if (pMedium->m->type == MediumType_Shareable)
7160 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7161
7162 /* Open the medium */
7163 vrc = VDOpen(hdd,
7164 pMedium->m->strFormat.c_str(),
7165 pMedium->m->strLocationFull.c_str(),
7166 uOpenFlags | m->uOpenFlagsDef,
7167 pMedium->m->vdImageIfaces);
7168 if (RT_FAILURE(vrc))
7169 throw vrc;
7170
7171 i++;
7172 }
7173
7174 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
7175 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
7176
7177 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
7178 task.mVDOperationIfaces);
7179 if (RT_FAILURE(vrc))
7180 throw vrc;
7181
7182 /* update parent UUIDs */
7183 if (!task.mfMergeForward)
7184 {
7185 /* we need to update UUIDs of all source's children
7186 * which cannot be part of the container at once so
7187 * add each one in there individually */
7188 if (task.mChildrenToReparent.size() > 0)
7189 {
7190 for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
7191 it != task.mChildrenToReparent.end();
7192 ++it)
7193 {
7194 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
7195 vrc = VDOpen(hdd,
7196 (*it)->m->strFormat.c_str(),
7197 (*it)->m->strLocationFull.c_str(),
7198 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7199 (*it)->m->vdImageIfaces);
7200 if (RT_FAILURE(vrc))
7201 throw vrc;
7202
7203 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
7204 pTarget->m->id.raw());
7205 if (RT_FAILURE(vrc))
7206 throw vrc;
7207
7208 vrc = VDClose(hdd, false /* fDelete */);
7209 if (RT_FAILURE(vrc))
7210 throw vrc;
7211
7212 (*it)->UnlockWrite(NULL);
7213 }
7214 }
7215 }
7216 }
7217 catch (HRESULT aRC) { rcTmp = aRC; }
7218 catch (int aVRC)
7219 {
7220 rcTmp = setError(VBOX_E_FILE_ERROR,
7221 tr("Could not merge the medium '%s' to '%s'%s"),
7222 m->strLocationFull.c_str(),
7223 pTarget->m->strLocationFull.c_str(),
7224 vdError(aVRC).c_str());
7225 }
7226
7227 VDDestroy(hdd);
7228 }
7229 catch (HRESULT aRC) { rcTmp = aRC; }
7230
7231 ErrorInfoKeeper eik;
7232 MultiResult mrc(rcTmp);
7233 HRESULT rc2;
7234
7235 if (SUCCEEDED(mrc))
7236 {
7237 /* all media but the target were successfully deleted by
7238 * VDMerge; reparent the last one and uninitialize deleted media. */
7239
7240 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7241
7242 if (task.mfMergeForward)
7243 {
7244 /* first, unregister the target since it may become a base
7245 * medium which needs re-registration */
7246 rc2 = m->pVirtualBox->unregisterMedium(pTarget);
7247 AssertComRC(rc2);
7248
7249 /* then, reparent it and disconnect the deleted branch at
7250 * both ends (chain->parent() is source's parent) */
7251 pTarget->deparent();
7252 pTarget->m->pParent = task.mParentForTarget;
7253 if (pTarget->m->pParent)
7254 {
7255 pTarget->m->pParent->m->llChildren.push_back(pTarget);
7256 deparent();
7257 }
7258
7259 /* then, register again */
7260 ComObjPtr<Medium> pMedium;
7261 rc2 = m->pVirtualBox->registerMedium(pTarget, &pMedium,
7262 DeviceType_HardDisk);
7263 AssertComRC(rc2);
7264 }
7265 else
7266 {
7267 Assert(pTarget->getChildren().size() == 1);
7268 Medium *targetChild = pTarget->getChildren().front();
7269
7270 /* disconnect the deleted branch at the elder end */
7271 targetChild->deparent();
7272
7273 /* reparent source's children and disconnect the deleted
7274 * branch at the younger end */
7275 if (task.mChildrenToReparent.size() > 0)
7276 {
7277 /* obey {parent,child} lock order */
7278 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
7279
7280 for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
7281 it != task.mChildrenToReparent.end();
7282 it++)
7283 {
7284 Medium *pMedium = *it;
7285 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
7286
7287 pMedium->deparent(); // removes pMedium from source
7288 pMedium->setParent(pTarget);
7289 }
7290 }
7291 }
7292
7293 /* unregister and uninitialize all media removed by the merge */
7294 MediumLockList::Base::iterator lockListBegin =
7295 task.mpMediumLockList->GetBegin();
7296 MediumLockList::Base::iterator lockListEnd =
7297 task.mpMediumLockList->GetEnd();
7298 for (MediumLockList::Base::iterator it = lockListBegin;
7299 it != lockListEnd;
7300 )
7301 {
7302 MediumLock &mediumLock = *it;
7303 /* Create a real copy of the medium pointer, as the medium
7304 * lock deletion below would invalidate the referenced object. */
7305 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
7306
7307 /* The target and all media not merged (readonly) are skipped */
7308 if ( pMedium == pTarget
7309 || pMedium->m->state == MediumState_LockedRead)
7310 {
7311 ++it;
7312 continue;
7313 }
7314
7315 rc2 = pMedium->m->pVirtualBox->unregisterMedium(pMedium);
7316 AssertComRC(rc2);
7317
7318 /* now, uninitialize the deleted medium (note that
7319 * due to the Deleting state, uninit() will not touch
7320 * the parent-child relationship so we need to
7321 * uninitialize each disk individually) */
7322
7323 /* note that the operation initiator medium (which is
7324 * normally also the source medium) is a special case
7325 * -- there is one more caller added by Task to it which
7326 * we must release. Also, if we are in sync mode, the
7327 * caller may still hold an AutoCaller instance for it
7328 * and therefore we cannot uninit() it (it's therefore
7329 * the caller's responsibility) */
7330 if (pMedium == this)
7331 {
7332 Assert(getChildren().size() == 0);
7333 Assert(m->backRefs.size() == 0);
7334 task.mMediumCaller.release();
7335 }
7336
7337 /* Delete the medium lock list entry, which also releases the
7338 * caller added by MergeChain before uninit() and updates the
7339 * iterator to point to the right place. */
7340 rc2 = task.mpMediumLockList->RemoveByIterator(it);
7341 AssertComRC(rc2);
7342
7343 if (task.isAsync() || pMedium != this)
7344 pMedium->uninit();
7345 }
7346 }
7347
7348 markRegistriesModified();
7349 if (task.isAsync())
7350 {
7351 // in asynchronous mode, save settings now
7352 eik.restore();
7353 m->pVirtualBox->saveModifiedRegistries();
7354 eik.fetch();
7355 }
7356
7357 if (FAILED(mrc))
7358 {
7359 /* Here we come if either VDMerge() failed (in which case we
7360 * assume that it tried to do everything to make a further
7361 * retry possible -- e.g. not deleted intermediate media
7362 * and so on) or VirtualBox::saveRegistries() failed (where we
7363 * should have the original tree but with intermediate storage
7364 * units deleted by VDMerge()). We have to only restore states
7365 * (through the MergeChain dtor) unless we are run synchronously
7366 * in which case it's the responsibility of the caller as stated
7367 * in the mergeTo() docs. The latter also implies that we
7368 * don't own the merge chain, so release it in this case. */
7369 if (task.isAsync())
7370 {
7371 Assert(task.mChildrenToReparent.size() == 0);
7372 cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
7373 }
7374 }
7375
7376 return mrc;
7377}
7378
7379/**
7380 * Implementation code for the "clone" task.
7381 *
7382 * This only gets started from Medium::CloneTo() and always runs asynchronously.
7383 * As a result, we always save the VirtualBox.xml file when we're done here.
7384 *
7385 * @param task
7386 * @return
7387 */
7388HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
7389{
7390 HRESULT rcTmp = S_OK;
7391
7392 const ComObjPtr<Medium> &pTarget = task.mTarget;
7393 const ComObjPtr<Medium> &pParent = task.mParent;
7394
7395 bool fCreatingTarget = false;
7396
7397 uint64_t size = 0, logicalSize = 0;
7398 MediumVariant_T variant = MediumVariant_Standard;
7399 bool fGenerateUuid = false;
7400
7401 try
7402 {
7403 /* Lock all in {parent,child} order. The lock is also used as a
7404 * signal from the task initiator (which releases it only after
7405 * RTThreadCreate()) that we can start the job. */
7406 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
7407
7408 fCreatingTarget = pTarget->m->state == MediumState_Creating;
7409
7410 /* The object may request a specific UUID (through a special form of
7411 * the setLocation() argument). Otherwise we have to generate it */
7412 Guid targetId = pTarget->m->id;
7413
7414 fGenerateUuid = targetId.isZero();
7415 if (fGenerateUuid)
7416 {
7417 targetId.create();
7418 /* VirtualBox::registerMedium() will need UUID */
7419 unconst(pTarget->m->id) = targetId;
7420 }
7421
7422 PVBOXHDD hdd;
7423 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7424 ComAssertRCThrow(vrc, E_FAIL);
7425
7426 try
7427 {
7428 /* Open all media in the source chain. */
7429 MediumLockList::Base::const_iterator sourceListBegin =
7430 task.mpSourceMediumLockList->GetBegin();
7431 MediumLockList::Base::const_iterator sourceListEnd =
7432 task.mpSourceMediumLockList->GetEnd();
7433 for (MediumLockList::Base::const_iterator it = sourceListBegin;
7434 it != sourceListEnd;
7435 ++it)
7436 {
7437 const MediumLock &mediumLock = *it;
7438 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7439 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7440
7441 /* sanity check */
7442 Assert(pMedium->m->state == MediumState_LockedRead);
7443
7444 /** Open all media in read-only mode. */
7445 vrc = VDOpen(hdd,
7446 pMedium->m->strFormat.c_str(),
7447 pMedium->m->strLocationFull.c_str(),
7448 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
7449 pMedium->m->vdImageIfaces);
7450 if (RT_FAILURE(vrc))
7451 throw setError(VBOX_E_FILE_ERROR,
7452 tr("Could not open the medium storage unit '%s'%s"),
7453 pMedium->m->strLocationFull.c_str(),
7454 vdError(vrc).c_str());
7455 }
7456
7457 Utf8Str targetFormat(pTarget->m->strFormat);
7458 Utf8Str targetLocation(pTarget->m->strLocationFull);
7459 uint64_t capabilities = pTarget->m->formatObj->getCapabilities();
7460
7461 Assert( pTarget->m->state == MediumState_Creating
7462 || pTarget->m->state == MediumState_LockedWrite);
7463 Assert(m->state == MediumState_LockedRead);
7464 Assert( pParent.isNull()
7465 || pParent->m->state == MediumState_LockedRead);
7466
7467 /* unlock before the potentially lengthy operation */
7468 thisLock.release();
7469
7470 /* ensure the target directory exists */
7471 if (capabilities & MediumFormatCapabilities_File)
7472 {
7473 HRESULT rc = VirtualBox::ensureFilePathExists(targetLocation, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
7474 if (FAILED(rc))
7475 throw rc;
7476 }
7477
7478 PVBOXHDD targetHdd;
7479 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
7480 ComAssertRCThrow(vrc, E_FAIL);
7481
7482 try
7483 {
7484 /* Open all media in the target chain. */
7485 MediumLockList::Base::const_iterator targetListBegin =
7486 task.mpTargetMediumLockList->GetBegin();
7487 MediumLockList::Base::const_iterator targetListEnd =
7488 task.mpTargetMediumLockList->GetEnd();
7489 for (MediumLockList::Base::const_iterator it = targetListBegin;
7490 it != targetListEnd;
7491 ++it)
7492 {
7493 const MediumLock &mediumLock = *it;
7494 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7495
7496 /* If the target medium is not created yet there's no
7497 * reason to open it. */
7498 if (pMedium == pTarget && fCreatingTarget)
7499 continue;
7500
7501 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7502
7503 /* sanity check */
7504 Assert( pMedium->m->state == MediumState_LockedRead
7505 || pMedium->m->state == MediumState_LockedWrite);
7506
7507 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7508 if (pMedium->m->state != MediumState_LockedWrite)
7509 uOpenFlags = VD_OPEN_FLAGS_READONLY;
7510 if (pMedium->m->type == MediumType_Shareable)
7511 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7512
7513 /* Open all media in appropriate mode. */
7514 vrc = VDOpen(targetHdd,
7515 pMedium->m->strFormat.c_str(),
7516 pMedium->m->strLocationFull.c_str(),
7517 uOpenFlags | m->uOpenFlagsDef,
7518 pMedium->m->vdImageIfaces);
7519 if (RT_FAILURE(vrc))
7520 throw setError(VBOX_E_FILE_ERROR,
7521 tr("Could not open the medium storage unit '%s'%s"),
7522 pMedium->m->strLocationFull.c_str(),
7523 vdError(vrc).c_str());
7524 }
7525
7526 /** @todo r=klaus target isn't locked, race getting the state */
7527 if (task.midxSrcImageSame == UINT32_MAX)
7528 {
7529 vrc = VDCopy(hdd,
7530 VD_LAST_IMAGE,
7531 targetHdd,
7532 targetFormat.c_str(),
7533 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
7534 false /* fMoveByRename */,
7535 0 /* cbSize */,
7536 task.mVariant & ~MediumVariant_NoCreateDir,
7537 targetId.raw(),
7538 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7539 NULL /* pVDIfsOperation */,
7540 pTarget->m->vdImageIfaces,
7541 task.mVDOperationIfaces);
7542 }
7543 else
7544 {
7545 vrc = VDCopyEx(hdd,
7546 VD_LAST_IMAGE,
7547 targetHdd,
7548 targetFormat.c_str(),
7549 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
7550 false /* fMoveByRename */,
7551 0 /* cbSize */,
7552 task.midxSrcImageSame,
7553 task.midxDstImageSame,
7554 task.mVariant & ~MediumVariant_NoCreateDir,
7555 targetId.raw(),
7556 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7557 NULL /* pVDIfsOperation */,
7558 pTarget->m->vdImageIfaces,
7559 task.mVDOperationIfaces);
7560 }
7561 if (RT_FAILURE(vrc))
7562 throw setError(VBOX_E_FILE_ERROR,
7563 tr("Could not create the clone medium '%s'%s"),
7564 targetLocation.c_str(), vdError(vrc).c_str());
7565
7566 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
7567 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
7568 unsigned uImageFlags;
7569 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
7570 if (RT_SUCCESS(vrc))
7571 variant = (MediumVariant_T)uImageFlags;
7572 }
7573 catch (HRESULT aRC) { rcTmp = aRC; }
7574
7575 VDDestroy(targetHdd);
7576 }
7577 catch (HRESULT aRC) { rcTmp = aRC; }
7578
7579 VDDestroy(hdd);
7580 }
7581 catch (HRESULT aRC) { rcTmp = aRC; }
7582
7583 ErrorInfoKeeper eik;
7584 MultiResult mrc(rcTmp);
7585
7586 /* Only do the parent changes for newly created media. */
7587 if (SUCCEEDED(mrc) && fCreatingTarget)
7588 {
7589 /* we set mParent & children() */
7590 AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7591
7592 Assert(pTarget->m->pParent.isNull());
7593
7594 if (pParent)
7595 {
7596 /* associate the clone with the parent and deassociate
7597 * from VirtualBox */
7598 pTarget->m->pParent = pParent;
7599 pParent->m->llChildren.push_back(pTarget);
7600
7601 /* register with mVirtualBox as the last step and move to
7602 * Created state only on success (leaving an orphan file is
7603 * better than breaking media registry consistency) */
7604 eik.restore();
7605 ComObjPtr<Medium> pMedium;
7606 mrc = pParent->m->pVirtualBox->registerMedium(pTarget, &pMedium,
7607 DeviceType_HardDisk);
7608 Assert( FAILED(mrc)
7609 || pTarget == pMedium);
7610 eik.fetch();
7611
7612 if (FAILED(mrc))
7613 /* break parent association on failure to register */
7614 pTarget->deparent(); // removes target from parent
7615 }
7616 else
7617 {
7618 /* just register */
7619 eik.restore();
7620 ComObjPtr<Medium> pMedium;
7621 mrc = m->pVirtualBox->registerMedium(pTarget, &pMedium,
7622 DeviceType_HardDisk);
7623 Assert( FAILED(mrc)
7624 || pTarget == pMedium);
7625 eik.fetch();
7626 }
7627 }
7628
7629 if (fCreatingTarget)
7630 {
7631 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
7632
7633 if (SUCCEEDED(mrc))
7634 {
7635 pTarget->m->state = MediumState_Created;
7636
7637 pTarget->m->size = size;
7638 pTarget->m->logicalSize = logicalSize;
7639 pTarget->m->variant = variant;
7640 }
7641 else
7642 {
7643 /* back to NotCreated on failure */
7644 pTarget->m->state = MediumState_NotCreated;
7645
7646 /* reset UUID to prevent it from being reused next time */
7647 if (fGenerateUuid)
7648 unconst(pTarget->m->id).clear();
7649 }
7650 }
7651
7652 // now, at the end of this task (always asynchronous), save the settings
7653 if (SUCCEEDED(mrc))
7654 {
7655 // save the settings
7656 markRegistriesModified();
7657 /* collect multiple errors */
7658 eik.restore();
7659 m->pVirtualBox->saveModifiedRegistries();
7660 eik.fetch();
7661 }
7662
7663 /* Everything is explicitly unlocked when the task exits,
7664 * as the task destruction also destroys the source chain. */
7665
7666 /* Make sure the source chain is released early. It could happen
7667 * that we get a deadlock in Appliance::Import when Medium::Close
7668 * is called & the source chain is released at the same time. */
7669 task.mpSourceMediumLockList->Clear();
7670
7671 return mrc;
7672}
7673
7674/**
7675 * Implementation code for the "delete" task.
7676 *
7677 * This task always gets started from Medium::deleteStorage() and can run
7678 * synchronously or asynchronously depending on the "wait" parameter passed to
7679 * that function.
7680 *
7681 * @param task
7682 * @return
7683 */
7684HRESULT Medium::taskDeleteHandler(Medium::DeleteTask &task)
7685{
7686 NOREF(task);
7687 HRESULT rc = S_OK;
7688
7689 try
7690 {
7691 /* The lock is also used as a signal from the task initiator (which
7692 * releases it only after RTThreadCreate()) that we can start the job */
7693 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7694
7695 PVBOXHDD hdd;
7696 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7697 ComAssertRCThrow(vrc, E_FAIL);
7698
7699 Utf8Str format(m->strFormat);
7700 Utf8Str location(m->strLocationFull);
7701
7702 /* unlock before the potentially lengthy operation */
7703 Assert(m->state == MediumState_Deleting);
7704 thisLock.release();
7705
7706 try
7707 {
7708 vrc = VDOpen(hdd,
7709 format.c_str(),
7710 location.c_str(),
7711 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7712 m->vdImageIfaces);
7713 if (RT_SUCCESS(vrc))
7714 vrc = VDClose(hdd, true /* fDelete */);
7715
7716 if (RT_FAILURE(vrc))
7717 throw setError(VBOX_E_FILE_ERROR,
7718 tr("Could not delete the medium storage unit '%s'%s"),
7719 location.c_str(), vdError(vrc).c_str());
7720
7721 }
7722 catch (HRESULT aRC) { rc = aRC; }
7723
7724 VDDestroy(hdd);
7725 }
7726 catch (HRESULT aRC) { rc = aRC; }
7727
7728 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7729
7730 /* go to the NotCreated state even on failure since the storage
7731 * may have been already partially deleted and cannot be used any
7732 * more. One will be able to manually re-open the storage if really
7733 * needed to re-register it. */
7734 m->state = MediumState_NotCreated;
7735
7736 /* Reset UUID to prevent Create* from reusing it again */
7737 unconst(m->id).clear();
7738
7739 return rc;
7740}
7741
7742/**
7743 * Implementation code for the "reset" task.
7744 *
7745 * This always gets started asynchronously from Medium::Reset().
7746 *
7747 * @param task
7748 * @return
7749 */
7750HRESULT Medium::taskResetHandler(Medium::ResetTask &task)
7751{
7752 HRESULT rc = S_OK;
7753
7754 uint64_t size = 0, logicalSize = 0;
7755 MediumVariant_T variant = MediumVariant_Standard;
7756
7757 try
7758 {
7759 /* The lock is also used as a signal from the task initiator (which
7760 * releases it only after RTThreadCreate()) that we can start the job */
7761 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7762
7763 /// @todo Below we use a pair of delete/create operations to reset
7764 /// the diff contents but the most efficient way will of course be
7765 /// to add a VDResetDiff() API call
7766
7767 PVBOXHDD hdd;
7768 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7769 ComAssertRCThrow(vrc, E_FAIL);
7770
7771 Guid id = m->id;
7772 Utf8Str format(m->strFormat);
7773 Utf8Str location(m->strLocationFull);
7774
7775 Medium *pParent = m->pParent;
7776 Guid parentId = pParent->m->id;
7777 Utf8Str parentFormat(pParent->m->strFormat);
7778 Utf8Str parentLocation(pParent->m->strLocationFull);
7779
7780 Assert(m->state == MediumState_LockedWrite);
7781
7782 /* unlock before the potentially lengthy operation */
7783 thisLock.release();
7784
7785 try
7786 {
7787 /* Open all media in the target chain but the last. */
7788 MediumLockList::Base::const_iterator targetListBegin =
7789 task.mpMediumLockList->GetBegin();
7790 MediumLockList::Base::const_iterator targetListEnd =
7791 task.mpMediumLockList->GetEnd();
7792 for (MediumLockList::Base::const_iterator it = targetListBegin;
7793 it != targetListEnd;
7794 ++it)
7795 {
7796 const MediumLock &mediumLock = *it;
7797 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7798
7799 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7800
7801 /* sanity check, "this" is checked above */
7802 Assert( pMedium == this
7803 || pMedium->m->state == MediumState_LockedRead);
7804
7805 /* Open all media in appropriate mode. */
7806 vrc = VDOpen(hdd,
7807 pMedium->m->strFormat.c_str(),
7808 pMedium->m->strLocationFull.c_str(),
7809 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
7810 pMedium->m->vdImageIfaces);
7811 if (RT_FAILURE(vrc))
7812 throw setError(VBOX_E_FILE_ERROR,
7813 tr("Could not open the medium storage unit '%s'%s"),
7814 pMedium->m->strLocationFull.c_str(),
7815 vdError(vrc).c_str());
7816
7817 /* Done when we hit the media which should be reset */
7818 if (pMedium == this)
7819 break;
7820 }
7821
7822 /* first, delete the storage unit */
7823 vrc = VDClose(hdd, true /* fDelete */);
7824 if (RT_FAILURE(vrc))
7825 throw setError(VBOX_E_FILE_ERROR,
7826 tr("Could not delete the medium storage unit '%s'%s"),
7827 location.c_str(), vdError(vrc).c_str());
7828
7829 /* next, create it again */
7830 vrc = VDOpen(hdd,
7831 parentFormat.c_str(),
7832 parentLocation.c_str(),
7833 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7834 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 parentLocation.c_str(), vdError(vrc).c_str());
7839
7840 vrc = VDCreateDiff(hdd,
7841 format.c_str(),
7842 location.c_str(),
7843 /// @todo use the same medium variant as before
7844 VD_IMAGE_FLAGS_NONE,
7845 NULL,
7846 id.raw(),
7847 parentId.raw(),
7848 VD_OPEN_FLAGS_NORMAL,
7849 m->vdImageIfaces,
7850 task.mVDOperationIfaces);
7851 if (RT_FAILURE(vrc))
7852 throw setError(VBOX_E_FILE_ERROR,
7853 tr("Could not create the differencing medium storage unit '%s'%s"),
7854 location.c_str(), vdError(vrc).c_str());
7855
7856 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
7857 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
7858 unsigned uImageFlags;
7859 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7860 if (RT_SUCCESS(vrc))
7861 variant = (MediumVariant_T)uImageFlags;
7862 }
7863 catch (HRESULT aRC) { rc = aRC; }
7864
7865 VDDestroy(hdd);
7866 }
7867 catch (HRESULT aRC) { rc = aRC; }
7868
7869 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7870
7871 m->size = size;
7872 m->logicalSize = logicalSize;
7873 m->variant = variant;
7874
7875 if (task.isAsync())
7876 {
7877 /* unlock ourselves when done */
7878 HRESULT rc2 = UnlockWrite(NULL);
7879 AssertComRC(rc2);
7880 }
7881
7882 /* Note that in sync mode, it's the caller's responsibility to
7883 * unlock the medium. */
7884
7885 return rc;
7886}
7887
7888/**
7889 * Implementation code for the "compact" task.
7890 *
7891 * @param task
7892 * @return
7893 */
7894HRESULT Medium::taskCompactHandler(Medium::CompactTask &task)
7895{
7896 HRESULT rc = S_OK;
7897
7898 /* Lock all in {parent,child} order. The lock is also used as a
7899 * signal from the task initiator (which releases it only after
7900 * RTThreadCreate()) that we can start the job. */
7901 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7902
7903 try
7904 {
7905 PVBOXHDD hdd;
7906 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7907 ComAssertRCThrow(vrc, E_FAIL);
7908
7909 try
7910 {
7911 /* Open all media in the chain. */
7912 MediumLockList::Base::const_iterator mediumListBegin =
7913 task.mpMediumLockList->GetBegin();
7914 MediumLockList::Base::const_iterator mediumListEnd =
7915 task.mpMediumLockList->GetEnd();
7916 MediumLockList::Base::const_iterator mediumListLast =
7917 mediumListEnd;
7918 mediumListLast--;
7919 for (MediumLockList::Base::const_iterator it = mediumListBegin;
7920 it != mediumListEnd;
7921 ++it)
7922 {
7923 const MediumLock &mediumLock = *it;
7924 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7925 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7926
7927 /* sanity check */
7928 if (it == mediumListLast)
7929 Assert(pMedium->m->state == MediumState_LockedWrite);
7930 else
7931 Assert(pMedium->m->state == MediumState_LockedRead);
7932
7933 /* Open all media but last in read-only mode. Do not handle
7934 * shareable media, as compaction and sharing are mutually
7935 * exclusive. */
7936 vrc = VDOpen(hdd,
7937 pMedium->m->strFormat.c_str(),
7938 pMedium->m->strLocationFull.c_str(),
7939 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
7940 pMedium->m->vdImageIfaces);
7941 if (RT_FAILURE(vrc))
7942 throw setError(VBOX_E_FILE_ERROR,
7943 tr("Could not open the medium storage unit '%s'%s"),
7944 pMedium->m->strLocationFull.c_str(),
7945 vdError(vrc).c_str());
7946 }
7947
7948 Assert(m->state == MediumState_LockedWrite);
7949
7950 Utf8Str location(m->strLocationFull);
7951
7952 /* unlock before the potentially lengthy operation */
7953 thisLock.release();
7954
7955 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
7956 if (RT_FAILURE(vrc))
7957 {
7958 if (vrc == VERR_NOT_SUPPORTED)
7959 throw setError(VBOX_E_NOT_SUPPORTED,
7960 tr("Compacting is not yet supported for medium '%s'"),
7961 location.c_str());
7962 else if (vrc == VERR_NOT_IMPLEMENTED)
7963 throw setError(E_NOTIMPL,
7964 tr("Compacting is not implemented, medium '%s'"),
7965 location.c_str());
7966 else
7967 throw setError(VBOX_E_FILE_ERROR,
7968 tr("Could not compact medium '%s'%s"),
7969 location.c_str(),
7970 vdError(vrc).c_str());
7971 }
7972 }
7973 catch (HRESULT aRC) { rc = aRC; }
7974
7975 VDDestroy(hdd);
7976 }
7977 catch (HRESULT aRC) { rc = aRC; }
7978
7979 /* Everything is explicitly unlocked when the task exits,
7980 * as the task destruction also destroys the media chain. */
7981
7982 return rc;
7983}
7984
7985/**
7986 * Implementation code for the "resize" task.
7987 *
7988 * @param task
7989 * @return
7990 */
7991HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task)
7992{
7993 HRESULT rc = S_OK;
7994
7995 /* Lock all in {parent,child} order. The lock is also used as a
7996 * signal from the task initiator (which releases it only after
7997 * RTThreadCreate()) that we can start the job. */
7998 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7999
8000 try
8001 {
8002 PVBOXHDD hdd;
8003 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
8004 ComAssertRCThrow(vrc, E_FAIL);
8005
8006 try
8007 {
8008 /* Open all media in the chain. */
8009 MediumLockList::Base::const_iterator mediumListBegin =
8010 task.mpMediumLockList->GetBegin();
8011 MediumLockList::Base::const_iterator mediumListEnd =
8012 task.mpMediumLockList->GetEnd();
8013 MediumLockList::Base::const_iterator mediumListLast =
8014 mediumListEnd;
8015 mediumListLast--;
8016 for (MediumLockList::Base::const_iterator it = mediumListBegin;
8017 it != mediumListEnd;
8018 ++it)
8019 {
8020 const MediumLock &mediumLock = *it;
8021 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8022 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8023
8024 /* sanity check */
8025 if (it == mediumListLast)
8026 Assert(pMedium->m->state == MediumState_LockedWrite);
8027 else
8028 Assert(pMedium->m->state == MediumState_LockedRead);
8029
8030 /* Open all media but last in read-only mode. Do not handle
8031 * shareable media, as compaction and sharing are mutually
8032 * exclusive. */
8033 vrc = VDOpen(hdd,
8034 pMedium->m->strFormat.c_str(),
8035 pMedium->m->strLocationFull.c_str(),
8036 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
8037 pMedium->m->vdImageIfaces);
8038 if (RT_FAILURE(vrc))
8039 throw setError(VBOX_E_FILE_ERROR,
8040 tr("Could not open the medium storage unit '%s'%s"),
8041 pMedium->m->strLocationFull.c_str(),
8042 vdError(vrc).c_str());
8043 }
8044
8045 Assert(m->state == MediumState_LockedWrite);
8046
8047 Utf8Str location(m->strLocationFull);
8048
8049 /* unlock before the potentially lengthy operation */
8050 thisLock.release();
8051
8052 VDGEOMETRY geo = {0, 0, 0}; /* auto */
8053 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
8054 if (RT_FAILURE(vrc))
8055 {
8056 if (vrc == VERR_NOT_SUPPORTED)
8057 throw setError(VBOX_E_NOT_SUPPORTED,
8058 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
8059 task.mSize, location.c_str());
8060 else if (vrc == VERR_NOT_IMPLEMENTED)
8061 throw setError(E_NOTIMPL,
8062 tr("Resiting is not implemented, medium '%s'"),
8063 location.c_str());
8064 else
8065 throw setError(VBOX_E_FILE_ERROR,
8066 tr("Could not resize medium '%s'%s"),
8067 location.c_str(),
8068 vdError(vrc).c_str());
8069 }
8070 }
8071 catch (HRESULT aRC) { rc = aRC; }
8072
8073 VDDestroy(hdd);
8074 }
8075 catch (HRESULT aRC) { rc = aRC; }
8076
8077 /* Everything is explicitly unlocked when the task exits,
8078 * as the task destruction also destroys the media chain. */
8079
8080 return rc;
8081}
8082
8083/**
8084 * Implementation code for the "export" task.
8085 *
8086 * This only gets started from Medium::exportFile() and always runs
8087 * asynchronously. It doesn't touch anything configuration related, so
8088 * we never save the VirtualBox.xml file here.
8089 *
8090 * @param task
8091 * @return
8092 */
8093HRESULT Medium::taskExportHandler(Medium::ExportTask &task)
8094{
8095 HRESULT rc = S_OK;
8096
8097 try
8098 {
8099 /* Lock all in {parent,child} order. The lock is also used as a
8100 * signal from the task initiator (which releases it only after
8101 * RTThreadCreate()) that we can start the job. */
8102 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8103
8104 PVBOXHDD hdd;
8105 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
8106 ComAssertRCThrow(vrc, E_FAIL);
8107
8108 try
8109 {
8110 /* Open all media in the source chain. */
8111 MediumLockList::Base::const_iterator sourceListBegin =
8112 task.mpSourceMediumLockList->GetBegin();
8113 MediumLockList::Base::const_iterator sourceListEnd =
8114 task.mpSourceMediumLockList->GetEnd();
8115 for (MediumLockList::Base::const_iterator it = sourceListBegin;
8116 it != sourceListEnd;
8117 ++it)
8118 {
8119 const MediumLock &mediumLock = *it;
8120 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8121 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8122
8123 /* sanity check */
8124 Assert(pMedium->m->state == MediumState_LockedRead);
8125
8126 /* Open all media in read-only mode. */
8127 vrc = VDOpen(hdd,
8128 pMedium->m->strFormat.c_str(),
8129 pMedium->m->strLocationFull.c_str(),
8130 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
8131 pMedium->m->vdImageIfaces);
8132 if (RT_FAILURE(vrc))
8133 throw setError(VBOX_E_FILE_ERROR,
8134 tr("Could not open the medium storage unit '%s'%s"),
8135 pMedium->m->strLocationFull.c_str(),
8136 vdError(vrc).c_str());
8137 }
8138
8139 Utf8Str targetFormat(task.mFormat->getId());
8140 Utf8Str targetLocation(task.mFilename);
8141 uint64_t capabilities = task.mFormat->getCapabilities();
8142
8143 Assert(m->state == MediumState_LockedRead);
8144
8145 /* unlock before the potentially lengthy operation */
8146 thisLock.release();
8147
8148 /* ensure the target directory exists */
8149 if (capabilities & MediumFormatCapabilities_File)
8150 {
8151 rc = VirtualBox::ensureFilePathExists(targetLocation, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8152 if (FAILED(rc))
8153 throw rc;
8154 }
8155
8156 PVBOXHDD targetHdd;
8157 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
8158 ComAssertRCThrow(vrc, E_FAIL);
8159
8160 try
8161 {
8162 vrc = VDCopy(hdd,
8163 VD_LAST_IMAGE,
8164 targetHdd,
8165 targetFormat.c_str(),
8166 targetLocation.c_str(),
8167 false /* fMoveByRename */,
8168 0 /* cbSize */,
8169 task.mVariant & ~MediumVariant_NoCreateDir,
8170 NULL /* pDstUuid */,
8171 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
8172 NULL /* pVDIfsOperation */,
8173 task.mVDImageIfaces,
8174 task.mVDOperationIfaces);
8175 if (RT_FAILURE(vrc))
8176 throw setError(VBOX_E_FILE_ERROR,
8177 tr("Could not create the clone medium '%s'%s"),
8178 targetLocation.c_str(), vdError(vrc).c_str());
8179 }
8180 catch (HRESULT aRC) { rc = aRC; }
8181
8182 VDDestroy(targetHdd);
8183 }
8184 catch (HRESULT aRC) { rc = aRC; }
8185
8186 VDDestroy(hdd);
8187 }
8188 catch (HRESULT aRC) { rc = aRC; }
8189
8190 /* Everything is explicitly unlocked when the task exits,
8191 * as the task destruction also destroys the source chain. */
8192
8193 /* Make sure the source chain is released early, otherwise it can
8194 * lead to deadlocks with concurrent IAppliance activities. */
8195 task.mpSourceMediumLockList->Clear();
8196
8197 return rc;
8198}
8199
8200/**
8201 * Implementation code for the "import" task.
8202 *
8203 * This only gets started from Medium::importFile() and always runs
8204 * asynchronously. It potentially touches the media registry, so we
8205 * always save the VirtualBox.xml file when we're done here.
8206 *
8207 * @param task
8208 * @return
8209 */
8210HRESULT Medium::taskImportHandler(Medium::ImportTask &task)
8211{
8212 HRESULT rcTmp = S_OK;
8213
8214 const ComObjPtr<Medium> &pParent = task.mParent;
8215
8216 bool fCreatingTarget = false;
8217
8218 uint64_t size = 0, logicalSize = 0;
8219 MediumVariant_T variant = MediumVariant_Standard;
8220 bool fGenerateUuid = false;
8221
8222 try
8223 {
8224 /* Lock all in {parent,child} order. The lock is also used as a
8225 * signal from the task initiator (which releases it only after
8226 * RTThreadCreate()) that we can start the job. */
8227 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
8228
8229 fCreatingTarget = m->state == MediumState_Creating;
8230
8231 /* The object may request a specific UUID (through a special form of
8232 * the setLocation() argument). Otherwise we have to generate it */
8233 Guid targetId = m->id;
8234
8235 fGenerateUuid = targetId.isZero();
8236 if (fGenerateUuid)
8237 {
8238 targetId.create();
8239 /* VirtualBox::registerMedium() will need UUID */
8240 unconst(m->id) = targetId;
8241 }
8242
8243
8244 PVBOXHDD hdd;
8245 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
8246 ComAssertRCThrow(vrc, E_FAIL);
8247
8248 try
8249 {
8250 /* Open source medium. */
8251 vrc = VDOpen(hdd,
8252 task.mFormat->getId().c_str(),
8253 task.mFilename.c_str(),
8254 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
8255 task.mVDImageIfaces);
8256 if (RT_FAILURE(vrc))
8257 throw setError(VBOX_E_FILE_ERROR,
8258 tr("Could not open the medium storage unit '%s'%s"),
8259 task.mFilename.c_str(),
8260 vdError(vrc).c_str());
8261
8262 Utf8Str targetFormat(m->strFormat);
8263 Utf8Str targetLocation(m->strLocationFull);
8264 uint64_t capabilities = task.mFormat->getCapabilities();
8265
8266 Assert( m->state == MediumState_Creating
8267 || m->state == MediumState_LockedWrite);
8268 Assert( pParent.isNull()
8269 || pParent->m->state == MediumState_LockedRead);
8270
8271 /* unlock before the potentially lengthy operation */
8272 thisLock.release();
8273
8274 /* ensure the target directory exists */
8275 if (capabilities & MediumFormatCapabilities_File)
8276 {
8277 HRESULT rc = VirtualBox::ensureFilePathExists(targetLocation, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8278 if (FAILED(rc))
8279 throw rc;
8280 }
8281
8282 PVBOXHDD targetHdd;
8283 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
8284 ComAssertRCThrow(vrc, E_FAIL);
8285
8286 try
8287 {
8288 /* Open all media in the target chain. */
8289 MediumLockList::Base::const_iterator targetListBegin =
8290 task.mpTargetMediumLockList->GetBegin();
8291 MediumLockList::Base::const_iterator targetListEnd =
8292 task.mpTargetMediumLockList->GetEnd();
8293 for (MediumLockList::Base::const_iterator it = targetListBegin;
8294 it != targetListEnd;
8295 ++it)
8296 {
8297 const MediumLock &mediumLock = *it;
8298 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8299
8300 /* If the target medium is not created yet there's no
8301 * reason to open it. */
8302 if (pMedium == this && fCreatingTarget)
8303 continue;
8304
8305 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8306
8307 /* sanity check */
8308 Assert( pMedium->m->state == MediumState_LockedRead
8309 || pMedium->m->state == MediumState_LockedWrite);
8310
8311 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8312 if (pMedium->m->state != MediumState_LockedWrite)
8313 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8314 if (pMedium->m->type == MediumType_Shareable)
8315 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8316
8317 /* Open all media in appropriate mode. */
8318 vrc = VDOpen(targetHdd,
8319 pMedium->m->strFormat.c_str(),
8320 pMedium->m->strLocationFull.c_str(),
8321 uOpenFlags | m->uOpenFlagsDef,
8322 pMedium->m->vdImageIfaces);
8323 if (RT_FAILURE(vrc))
8324 throw setError(VBOX_E_FILE_ERROR,
8325 tr("Could not open the medium storage unit '%s'%s"),
8326 pMedium->m->strLocationFull.c_str(),
8327 vdError(vrc).c_str());
8328 }
8329
8330 /** @todo r=klaus target isn't locked, race getting the state */
8331 vrc = VDCopy(hdd,
8332 VD_LAST_IMAGE,
8333 targetHdd,
8334 targetFormat.c_str(),
8335 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
8336 false /* fMoveByRename */,
8337 0 /* cbSize */,
8338 task.mVariant & ~MediumVariant_NoCreateDir,
8339 targetId.raw(),
8340 VD_OPEN_FLAGS_NORMAL,
8341 NULL /* pVDIfsOperation */,
8342 m->vdImageIfaces,
8343 task.mVDOperationIfaces);
8344 if (RT_FAILURE(vrc))
8345 throw setError(VBOX_E_FILE_ERROR,
8346 tr("Could not create the clone medium '%s'%s"),
8347 targetLocation.c_str(), vdError(vrc).c_str());
8348
8349 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
8350 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
8351 unsigned uImageFlags;
8352 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
8353 if (RT_SUCCESS(vrc))
8354 variant = (MediumVariant_T)uImageFlags;
8355 }
8356 catch (HRESULT aRC) { rcTmp = aRC; }
8357
8358 VDDestroy(targetHdd);
8359 }
8360 catch (HRESULT aRC) { rcTmp = aRC; }
8361
8362 VDDestroy(hdd);
8363 }
8364 catch (HRESULT aRC) { rcTmp = aRC; }
8365
8366 ErrorInfoKeeper eik;
8367 MultiResult mrc(rcTmp);
8368
8369 /* Only do the parent changes for newly created media. */
8370 if (SUCCEEDED(mrc) && fCreatingTarget)
8371 {
8372 /* we set mParent & children() */
8373 AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8374
8375 Assert(m->pParent.isNull());
8376
8377 if (pParent)
8378 {
8379 /* associate the clone with the parent and deassociate
8380 * from VirtualBox */
8381 m->pParent = pParent;
8382 pParent->m->llChildren.push_back(this);
8383
8384 /* register with mVirtualBox as the last step and move to
8385 * Created state only on success (leaving an orphan file is
8386 * better than breaking media registry consistency) */
8387 eik.restore();
8388 ComObjPtr<Medium> pMedium;
8389 mrc = pParent->m->pVirtualBox->registerMedium(this, &pMedium,
8390 DeviceType_HardDisk);
8391 Assert(this == pMedium);
8392 eik.fetch();
8393
8394 if (FAILED(mrc))
8395 /* break parent association on failure to register */
8396 this->deparent(); // removes target from parent
8397 }
8398 else
8399 {
8400 /* just register */
8401 eik.restore();
8402 ComObjPtr<Medium> pMedium;
8403 mrc = m->pVirtualBox->registerMedium(this, &pMedium, DeviceType_HardDisk);
8404 Assert(this == pMedium);
8405 eik.fetch();
8406 }
8407 }
8408
8409 if (fCreatingTarget)
8410 {
8411 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
8412
8413 if (SUCCEEDED(mrc))
8414 {
8415 m->state = MediumState_Created;
8416
8417 m->size = size;
8418 m->logicalSize = logicalSize;
8419 m->variant = variant;
8420 }
8421 else
8422 {
8423 /* back to NotCreated on failure */
8424 m->state = MediumState_NotCreated;
8425
8426 /* reset UUID to prevent it from being reused next time */
8427 if (fGenerateUuid)
8428 unconst(m->id).clear();
8429 }
8430 }
8431
8432 // now, at the end of this task (always asynchronous), save the settings
8433 {
8434 // save the settings
8435 markRegistriesModified();
8436 /* collect multiple errors */
8437 eik.restore();
8438 m->pVirtualBox->saveModifiedRegistries();
8439 eik.fetch();
8440 }
8441
8442 /* Everything is explicitly unlocked when the task exits,
8443 * as the task destruction also destroys the target chain. */
8444
8445 /* Make sure the target chain is released early, otherwise it can
8446 * lead to deadlocks with concurrent IAppliance activities. */
8447 task.mpTargetMediumLockList->Clear();
8448
8449 return mrc;
8450}
8451
8452/* 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