VirtualBox

source: vbox/trunk/src/VBox/Main/MediumImpl.cpp@ 34575

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

Main/Medium: prevent VirtualBox object refcount reaching zero while a task is running

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