VirtualBox

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

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

Main: document the reasoning behind the host drive UUIDs

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

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