VirtualBox

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

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

Main: fix broken snapshots when disk images are shared between several machines

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