VirtualBox

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

Last change on this file since 35757 was 35638, checked in by vboxsync, 14 years ago

Main. QT/FE: fix long standing COM issue

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

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