VirtualBox

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

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

Main/Medium: Add field for default open flags and add VD_OPEN_FLAGS_IGNORE_FLUSH for now

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

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