VirtualBox

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

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

Main: further fix broken snapshots when disk images are shared between several machines: recurse into diff images as well

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 256.8 KB
Line 
1/* $Id: MediumImpl.cpp 35982 2011-02-15 16:02:28Z 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 * If fRecurse == true, then additionally the media tree lock must be held for reading.
3082 *
3083 * @param id
3084 * @param fRecurse If true, recurses into child media to make sure the whole tree has registries in sync.
3085 * @return true if the registry was added; false if the given id was already on the list.
3086 */
3087bool Medium::addRegistry(const Guid& id, bool fRecurse)
3088{
3089 // hard disks cannot be in more than one registry
3090 if ( m->devType == DeviceType_HardDisk
3091 && m->llRegistryIDs.size() > 0
3092 )
3093 return false;
3094
3095 // no need to add the UUID twice
3096 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3097 it != m->llRegistryIDs.end();
3098 ++it)
3099 {
3100 if ((*it) == id)
3101 return false;
3102 }
3103
3104 addRegistryImpl(id, fRecurse);
3105
3106 return true;
3107}
3108
3109/**
3110 * Private implementation for addRegistry() so we can recurse more efficiently.
3111 * @param id
3112 * @param fRecurse
3113 */
3114void Medium::addRegistryImpl(const Guid& id, bool fRecurse)
3115{
3116 m->llRegistryIDs.push_back(id);
3117
3118 if (fRecurse)
3119 {
3120 for (MediaList::iterator it = m->llChildren.begin();
3121 it != m->llChildren.end();
3122 ++it)
3123 {
3124 Medium *pChild = *it;
3125 // recurse!
3126 pChild->addRegistryImpl(id, fRecurse);
3127 }
3128 }
3129}
3130
3131/**
3132 * Removes the given UUID from the list of media registry UUIDs. Returns true
3133 * if found or false if not.
3134 *
3135 * Must have caller + locking!
3136 *
3137 * If fRecurse == true, then additionally the media tree lock must be held for reading.
3138 *
3139 * @param id
3140 * @param fRecurse If true, recurses into child media to make sure the whole tree has registries in sync.
3141 * @return
3142 */
3143bool Medium::removeRegistry(const Guid& id, bool fRecurse)
3144{
3145 for (GuidList::iterator it = m->llRegistryIDs.begin();
3146 it != m->llRegistryIDs.end();
3147 ++it)
3148 {
3149 if ((*it) == id)
3150 {
3151 m->llRegistryIDs.erase(it);
3152
3153 if (fRecurse)
3154 {
3155 for (MediaList::iterator it = m->llChildren.begin();
3156 it != m->llChildren.end();
3157 ++it)
3158 {
3159 Medium *pChild = *it;
3160 pChild->removeRegistry(id, true);
3161 }
3162 }
3163
3164 return true;
3165 }
3166 }
3167
3168 return false;
3169}
3170
3171/**
3172 * Returns true if id is in the list of media registries for this medium.
3173 *
3174 * Must have caller + locking!
3175 *
3176 * @param id
3177 * @return
3178 */
3179bool Medium::isInRegistry(const Guid& id)
3180{
3181 AutoCaller autoCaller(this);
3182 if (FAILED(autoCaller.rc())) return false;
3183
3184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3185
3186 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3187 it != m->llRegistryIDs.end();
3188 ++it)
3189 {
3190 if (*it == id)
3191 return true;
3192 }
3193
3194 return false;
3195}
3196
3197/**
3198 * Internal method to return the medium's first registry machine (i.e. the machine in whose
3199 * machine XML this medium is listed).
3200 *
3201 * Every attached medium must now (4.0) reside in at least one media registry, which is identified
3202 * by a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
3203 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
3204 * object if the machine is old and still needs the global registry in VirtualBox.xml.
3205 *
3206 * By definition, hard disks may only be in one media registry, in which all its children
3207 * will be stored as well. Otherwise we run into problems with having keep multiple registries
3208 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
3209 * case, only VM2's registry is used for the disk in question.)
3210 *
3211 * If there is no medium registry, particularly if the medium has not been attached yet, this
3212 * does not modify uuid and returns false.
3213 *
3214 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
3215 * the user.
3216 *
3217 * Must have caller + locking!
3218 *
3219 * @param uuid Receives first registry machine UUID, if available.
3220 * @return true if uuid was set.
3221 */
3222bool Medium::getFirstRegistryMachineId(Guid &uuid) const
3223{
3224 if (m->llRegistryIDs.size())
3225 {
3226 uuid = m->llRegistryIDs.front();
3227 return true;
3228 }
3229 return false;
3230}
3231
3232/**
3233 * Adds all the IDs of the registries in which this medium is registered to the given list
3234 * of UUIDs, but only if they are not on the list yet.
3235 * @param llRegistryIDs
3236 */
3237HRESULT Medium::addToRegistryIDList(GuidList &llRegistryIDs)
3238{
3239 AutoCaller autoCaller(this);
3240 if (FAILED(autoCaller.rc())) return false;
3241
3242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3243
3244 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3245 it != m->llRegistryIDs.end();
3246 ++it)
3247 {
3248 m->pVirtualBox->addGuidToListUniquely(llRegistryIDs, *it);
3249 }
3250
3251 return S_OK;
3252}
3253
3254/**
3255 * Adds the given machine and optionally the snapshot to the list of the objects
3256 * this medium is attached to.
3257 *
3258 * @param aMachineId Machine ID.
3259 * @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
3260 */
3261HRESULT Medium::addBackReference(const Guid &aMachineId,
3262 const Guid &aSnapshotId /*= Guid::Empty*/)
3263{
3264 AssertReturn(!aMachineId.isEmpty(), E_FAIL);
3265
3266 LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
3267
3268 AutoCaller autoCaller(this);
3269 AssertComRCReturnRC(autoCaller.rc());
3270
3271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3272
3273 switch (m->state)
3274 {
3275 case MediumState_Created:
3276 case MediumState_Inaccessible:
3277 case MediumState_LockedRead:
3278 case MediumState_LockedWrite:
3279 break;
3280
3281 default:
3282 return setStateError();
3283 }
3284
3285 if (m->numCreateDiffTasks > 0)
3286 return setError(VBOX_E_OBJECT_IN_USE,
3287 tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created"),
3288 m->strLocationFull.c_str(),
3289 m->id.raw(),
3290 m->numCreateDiffTasks);
3291
3292 BackRefList::iterator it = std::find_if(m->backRefs.begin(),
3293 m->backRefs.end(),
3294 BackRef::EqualsTo(aMachineId));
3295 if (it == m->backRefs.end())
3296 {
3297 BackRef ref(aMachineId, aSnapshotId);
3298 m->backRefs.push_back(ref);
3299
3300 return S_OK;
3301 }
3302
3303 // if the caller has not supplied a snapshot ID, then we're attaching
3304 // to a machine a medium which represents the machine's current state,
3305 // so set the flag
3306 if (aSnapshotId.isEmpty())
3307 {
3308 /* sanity: no duplicate attachments */
3309 AssertReturn(!it->fInCurState, E_FAIL);
3310 it->fInCurState = true;
3311
3312 return S_OK;
3313 }
3314
3315 // otherwise: a snapshot medium is being attached
3316
3317 /* sanity: no duplicate attachments */
3318 for (GuidList::const_iterator jt = it->llSnapshotIds.begin();
3319 jt != it->llSnapshotIds.end();
3320 ++jt)
3321 {
3322 const Guid &idOldSnapshot = *jt;
3323
3324 if (idOldSnapshot == aSnapshotId)
3325 {
3326#ifdef DEBUG
3327 dumpBackRefs();
3328#endif
3329 return setError(VBOX_E_OBJECT_IN_USE,
3330 tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
3331 m->strLocationFull.c_str(),
3332 m->id.raw(),
3333 aSnapshotId.raw(),
3334 idOldSnapshot.raw());
3335 }
3336 }
3337
3338 it->llSnapshotIds.push_back(aSnapshotId);
3339 it->fInCurState = false;
3340
3341 LogFlowThisFuncLeave();
3342
3343 return S_OK;
3344}
3345
3346/**
3347 * Removes the given machine and optionally the snapshot from the list of the
3348 * objects this medium is attached to.
3349 *
3350 * @param aMachineId Machine ID.
3351 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
3352 * attachment.
3353 */
3354HRESULT Medium::removeBackReference(const Guid &aMachineId,
3355 const Guid &aSnapshotId /*= Guid::Empty*/)
3356{
3357 AssertReturn(!aMachineId.isEmpty(), E_FAIL);
3358
3359 AutoCaller autoCaller(this);
3360 AssertComRCReturnRC(autoCaller.rc());
3361
3362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3363
3364 BackRefList::iterator it =
3365 std::find_if(m->backRefs.begin(), m->backRefs.end(),
3366 BackRef::EqualsTo(aMachineId));
3367 AssertReturn(it != m->backRefs.end(), E_FAIL);
3368
3369 if (aSnapshotId.isEmpty())
3370 {
3371 /* remove the current state attachment */
3372 it->fInCurState = false;
3373 }
3374 else
3375 {
3376 /* remove the snapshot attachment */
3377 GuidList::iterator jt = std::find(it->llSnapshotIds.begin(),
3378 it->llSnapshotIds.end(),
3379 aSnapshotId);
3380
3381 AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
3382 it->llSnapshotIds.erase(jt);
3383 }
3384
3385 /* if the backref becomes empty, remove it */
3386 if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
3387 m->backRefs.erase(it);
3388
3389 return S_OK;
3390}
3391
3392/**
3393 * Internal method to return the medium's list of backrefs. Must have caller + locking!
3394 * @return
3395 */
3396const Guid* Medium::getFirstMachineBackrefId() const
3397{
3398 if (!m->backRefs.size())
3399 return NULL;
3400
3401 return &m->backRefs.front().machineId;
3402}
3403
3404/**
3405 * Internal method which returns a machine that either this medium or one of its children
3406 * is attached to. This is used for finding a replacement media registry when an existing
3407 * media registry is about to be deleted in VirtualBox::unregisterMachine().
3408 *
3409 * Must have caller + locking, *and* caller must hold the media tree lock!
3410 * @return
3411 */
3412const Guid* Medium::getAnyMachineBackref() const
3413{
3414 if (m->backRefs.size())
3415 return &m->backRefs.front().machineId;
3416
3417 for (MediaList::iterator it = m->llChildren.begin();
3418 it != m->llChildren.end();
3419 ++it)
3420 {
3421 Medium *pChild = *it;
3422 // recurse for this child
3423 const Guid* puuid;
3424 if ((puuid = pChild->getAnyMachineBackref()))
3425 return puuid;
3426 }
3427
3428 return NULL;
3429}
3430
3431const Guid* Medium::getFirstMachineBackrefSnapshotId() const
3432{
3433 if (!m->backRefs.size())
3434 return NULL;
3435
3436 const BackRef &ref = m->backRefs.front();
3437 if (!ref.llSnapshotIds.size())
3438 return NULL;
3439
3440 return &ref.llSnapshotIds.front();
3441}
3442
3443#ifdef DEBUG
3444/**
3445 * Debugging helper that gets called after VirtualBox initialization that writes all
3446 * machine backreferences to the debug log.
3447 */
3448void Medium::dumpBackRefs()
3449{
3450 AutoCaller autoCaller(this);
3451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3452
3453 LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
3454
3455 for (BackRefList::iterator it2 = m->backRefs.begin();
3456 it2 != m->backRefs.end();
3457 ++it2)
3458 {
3459 const BackRef &ref = *it2;
3460 LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d)\n", ref.machineId.raw(), ref.fInCurState));
3461
3462 for (GuidList::const_iterator jt2 = it2->llSnapshotIds.begin();
3463 jt2 != it2->llSnapshotIds.end();
3464 ++jt2)
3465 {
3466 const Guid &id = *jt2;
3467 LogFlowThisFunc((" Backref from snapshot {%RTuuid}\n", id.raw()));
3468 }
3469 }
3470}
3471#endif
3472
3473/**
3474 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
3475 * of this media and updates it if necessary to reflect the new location.
3476 *
3477 * @param aOldPath Old path (full).
3478 * @param aNewPath New path (full).
3479 *
3480 * @note Locks this object for writing.
3481 */
3482HRESULT Medium::updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
3483{
3484 AssertReturn(!strOldPath.isEmpty(), E_FAIL);
3485 AssertReturn(!strNewPath.isEmpty(), E_FAIL);
3486
3487 AutoCaller autoCaller(this);
3488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3489
3490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3491
3492 LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
3493
3494 const char *pcszMediumPath = m->strLocationFull.c_str();
3495
3496 if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
3497 {
3498 Utf8Str newPath(strNewPath);
3499 newPath.append(pcszMediumPath + strOldPath.length());
3500 unconst(m->strLocationFull) = newPath;
3501
3502 LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
3503 }
3504
3505 return S_OK;
3506}
3507
3508/**
3509 * Returns the base medium of the media chain this medium is part of.
3510 *
3511 * The base medium is found by walking up the parent-child relationship axis.
3512 * If the medium doesn't have a parent (i.e. it's a base medium), it
3513 * returns itself in response to this method.
3514 *
3515 * @param aLevel Where to store the number of ancestors of this medium
3516 * (zero for the base), may be @c NULL.
3517 *
3518 * @note Locks medium tree for reading.
3519 */
3520ComObjPtr<Medium> Medium::getBase(uint32_t *aLevel /*= NULL*/)
3521{
3522 ComObjPtr<Medium> pBase;
3523 uint32_t level;
3524
3525 AutoCaller autoCaller(this);
3526 AssertReturn(autoCaller.isOk(), pBase);
3527
3528 /* we access mParent */
3529 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3530
3531 pBase = this;
3532 level = 0;
3533
3534 if (m->pParent)
3535 {
3536 for (;;)
3537 {
3538 AutoCaller baseCaller(pBase);
3539 AssertReturn(baseCaller.isOk(), pBase);
3540
3541 if (pBase->m->pParent.isNull())
3542 break;
3543
3544 pBase = pBase->m->pParent;
3545 ++level;
3546 }
3547 }
3548
3549 if (aLevel != NULL)
3550 *aLevel = level;
3551
3552 return pBase;
3553}
3554
3555/**
3556 * Returns @c true if this medium cannot be modified because it has
3557 * dependents (children) or is part of the snapshot. Related to the medium
3558 * type and posterity, not to the current media state.
3559 *
3560 * @note Locks this object and medium tree for reading.
3561 */
3562bool Medium::isReadOnly()
3563{
3564 AutoCaller autoCaller(this);
3565 AssertComRCReturn(autoCaller.rc(), false);
3566
3567 /* we access children */
3568 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3569
3570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3571
3572 switch (m->type)
3573 {
3574 case MediumType_Normal:
3575 {
3576 if (getChildren().size() != 0)
3577 return true;
3578
3579 for (BackRefList::const_iterator it = m->backRefs.begin();
3580 it != m->backRefs.end(); ++it)
3581 if (it->llSnapshotIds.size() != 0)
3582 return true;
3583
3584 return false;
3585 }
3586 case MediumType_Immutable:
3587 case MediumType_MultiAttach:
3588 return true;
3589 case MediumType_Writethrough:
3590 case MediumType_Shareable:
3591 case MediumType_Readonly: /* explicit readonly media has no diffs */
3592 return false;
3593 default:
3594 break;
3595 }
3596
3597 AssertFailedReturn(false);
3598}
3599
3600/**
3601 * Saves medium data by appending a new child node to the given
3602 * parent XML settings node.
3603 *
3604 * @param data Settings struct to be updated.
3605 * @param strHardDiskFolder Folder for which paths should be relative.
3606 *
3607 * @note Locks this object, medium tree and children for reading.
3608 */
3609HRESULT Medium::saveSettings(settings::Medium &data,
3610 const Utf8Str &strHardDiskFolder)
3611{
3612 AutoCaller autoCaller(this);
3613 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3614
3615 /* we access mParent */
3616 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3617
3618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3619
3620 data.uuid = m->id;
3621
3622 // make path relative if needed
3623 if ( !strHardDiskFolder.isEmpty()
3624 && RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
3625 )
3626 data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
3627 else
3628 data.strLocation = m->strLocationFull;
3629 data.strFormat = m->strFormat;
3630
3631 /* optional, only for diffs, default is false */
3632 if (m->pParent)
3633 data.fAutoReset = m->autoReset;
3634 else
3635 data.fAutoReset = false;
3636
3637 /* optional */
3638 data.strDescription = m->strDescription;
3639
3640 /* optional properties */
3641 data.properties.clear();
3642 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
3643 it != m->mapProperties.end();
3644 ++it)
3645 {
3646 /* only save properties that have non-default values */
3647 if (!it->second.isEmpty())
3648 {
3649 const Utf8Str &name = it->first;
3650 const Utf8Str &value = it->second;
3651 data.properties[name] = value;
3652 }
3653 }
3654
3655 /* only for base media */
3656 if (m->pParent.isNull())
3657 data.hdType = m->type;
3658
3659 /* save all children */
3660 for (MediaList::const_iterator it = getChildren().begin();
3661 it != getChildren().end();
3662 ++it)
3663 {
3664 settings::Medium med;
3665 HRESULT rc = (*it)->saveSettings(med, strHardDiskFolder);
3666 AssertComRCReturnRC(rc);
3667 data.llChildren.push_back(med);
3668 }
3669
3670 return S_OK;
3671}
3672
3673/**
3674 * Constructs a medium lock list for this medium. The lock is not taken.
3675 *
3676 * @note Locks the medium tree for reading.
3677 *
3678 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
3679 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
3680 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
3681 * @param fMediumLockWrite Whether to associate a write lock with this medium.
3682 * @param pToBeParent Medium which will become the parent of this medium.
3683 * @param mediumLockList Where to store the resulting list.
3684 */
3685HRESULT Medium::createMediumLockList(bool fFailIfInaccessible,
3686 bool fMediumLockWrite,
3687 Medium *pToBeParent,
3688 MediumLockList &mediumLockList)
3689{
3690 AutoCaller autoCaller(this);
3691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3692
3693 HRESULT rc = S_OK;
3694
3695 /* we access parent medium objects */
3696 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3697
3698 /* paranoid sanity checking if the medium has a to-be parent medium */
3699 if (pToBeParent)
3700 {
3701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3702 ComAssertRet(getParent().isNull(), E_FAIL);
3703 ComAssertRet(getChildren().size() == 0, E_FAIL);
3704 }
3705
3706 ErrorInfoKeeper eik;
3707 MultiResult mrc(S_OK);
3708
3709 ComObjPtr<Medium> pMedium = this;
3710 while (!pMedium.isNull())
3711 {
3712 // need write lock for RefreshState if medium is inaccessible
3713 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
3714
3715 /* Accessibility check must be first, otherwise locking interferes
3716 * with getting the medium state. Lock lists are not created for
3717 * fun, and thus getting the medium status is no luxury. */
3718 MediumState_T mediumState = pMedium->getState();
3719 if (mediumState == MediumState_Inaccessible)
3720 {
3721 rc = pMedium->RefreshState(&mediumState);
3722 if (FAILED(rc)) return rc;
3723
3724 if (mediumState == MediumState_Inaccessible)
3725 {
3726 // ignore inaccessible ISO media and silently return S_OK,
3727 // otherwise VM startup (esp. restore) may fail without good reason
3728 if (!fFailIfInaccessible)
3729 return S_OK;
3730
3731 // otherwise report an error
3732 Bstr error;
3733 rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
3734 if (FAILED(rc)) return rc;
3735
3736 /* collect multiple errors */
3737 eik.restore();
3738 Assert(!error.isEmpty());
3739 mrc = setError(E_FAIL,
3740 "%ls",
3741 error.raw());
3742 // error message will be something like
3743 // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
3744 eik.fetch();
3745 }
3746 }
3747
3748 if (pMedium == this)
3749 mediumLockList.Prepend(pMedium, fMediumLockWrite);
3750 else
3751 mediumLockList.Prepend(pMedium, false);
3752
3753 pMedium = pMedium->getParent();
3754 if (pMedium.isNull() && pToBeParent)
3755 {
3756 pMedium = pToBeParent;
3757 pToBeParent = NULL;
3758 }
3759 }
3760
3761 return mrc;
3762}
3763
3764/**
3765 * Creates a new differencing storage unit using the format of the given target
3766 * medium and the location. Note that @c aTarget must be NotCreated.
3767 *
3768 * The @a aMediumLockList parameter contains the associated medium lock list,
3769 * which must be in locked state. If @a aWait is @c true then the caller is
3770 * responsible for unlocking.
3771 *
3772 * If @a aProgress is not NULL but the object it points to is @c null then a
3773 * new progress object will be created and assigned to @a *aProgress on
3774 * success, otherwise the existing progress object is used. If @a aProgress is
3775 * NULL, then no progress object is created/used at all.
3776 *
3777 * When @a aWait is @c false, this method will create a thread to perform the
3778 * create operation asynchronously and will return immediately. Otherwise, it
3779 * will perform the operation on the calling thread and will not return to the
3780 * caller until the operation is completed. Note that @a aProgress cannot be
3781 * NULL when @a aWait is @c false (this method will assert in this case).
3782 *
3783 * @param aTarget Target medium.
3784 * @param aVariant Precise medium variant to create.
3785 * @param aMediumLockList List of media which should be locked.
3786 * @param aProgress Where to find/store a Progress object to track
3787 * operation completion.
3788 * @param aWait @c true if this method should block instead of
3789 * creating an asynchronous thread.
3790 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs that will receive the registry IDs that need saving.
3791 * This only works in "wait" mode; otherwise saveSettings is called automatically by the thread that
3792 * was created, and this parameter is ignored.
3793 *
3794 * @note Locks this object and @a aTarget for writing.
3795 */
3796HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
3797 MediumVariant_T aVariant,
3798 MediumLockList *aMediumLockList,
3799 ComObjPtr<Progress> *aProgress,
3800 bool aWait,
3801 GuidList *pllRegistriesThatNeedSaving)
3802{
3803 AssertReturn(!aTarget.isNull(), E_FAIL);
3804 AssertReturn(aMediumLockList, E_FAIL);
3805 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
3806
3807 AutoCaller autoCaller(this);
3808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3809
3810 AutoCaller targetCaller(aTarget);
3811 if (FAILED(targetCaller.rc())) return targetCaller.rc();
3812
3813 HRESULT rc = S_OK;
3814 ComObjPtr<Progress> pProgress;
3815 Medium::Task *pTask = NULL;
3816
3817 try
3818 {
3819 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
3820
3821 ComAssertThrow( m->type != MediumType_Writethrough
3822 && m->type != MediumType_Shareable
3823 && m->type != MediumType_Readonly, E_FAIL);
3824 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
3825
3826 if (aTarget->m->state != MediumState_NotCreated)
3827 throw aTarget->setStateError();
3828
3829 /* Check that the medium is not attached to the current state of
3830 * any VM referring to it. */
3831 for (BackRefList::const_iterator it = m->backRefs.begin();
3832 it != m->backRefs.end();
3833 ++it)
3834 {
3835 if (it->fInCurState)
3836 {
3837 /* Note: when a VM snapshot is being taken, all normal media
3838 * attached to the VM in the current state will be, as an
3839 * exception, also associated with the snapshot which is about
3840 * to create (see SnapshotMachine::init()) before deassociating
3841 * them from the current state (which takes place only on
3842 * success in Machine::fixupHardDisks()), so that the size of
3843 * snapshotIds will be 1 in this case. The extra condition is
3844 * used to filter out this legal situation. */
3845 if (it->llSnapshotIds.size() == 0)
3846 throw setError(VBOX_E_INVALID_OBJECT_STATE,
3847 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"),
3848 m->strLocationFull.c_str(), it->machineId.raw());
3849
3850 Assert(it->llSnapshotIds.size() == 1);
3851 }
3852 }
3853
3854 if (aProgress != NULL)
3855 {
3856 /* use the existing progress object... */
3857 pProgress = *aProgress;
3858
3859 /* ...but create a new one if it is null */
3860 if (pProgress.isNull())
3861 {
3862 pProgress.createObject();
3863 rc = pProgress->init(m->pVirtualBox,
3864 static_cast<IMedium*>(this),
3865 BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
3866 TRUE /* aCancelable */);
3867 if (FAILED(rc))
3868 throw rc;
3869 }
3870 }
3871
3872 /* setup task object to carry out the operation sync/async */
3873 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
3874 aMediumLockList,
3875 aWait /* fKeepMediumLockList */);
3876 rc = pTask->rc();
3877 AssertComRC(rc);
3878 if (FAILED(rc))
3879 throw rc;
3880
3881 /* register a task (it will deregister itself when done) */
3882 ++m->numCreateDiffTasks;
3883 Assert(m->numCreateDiffTasks != 0); /* overflow? */
3884
3885 aTarget->m->state = MediumState_Creating;
3886 }
3887 catch (HRESULT aRC) { rc = aRC; }
3888
3889 if (SUCCEEDED(rc))
3890 {
3891 if (aWait)
3892 rc = runNow(pTask, pllRegistriesThatNeedSaving);
3893 else
3894 rc = startThread(pTask);
3895
3896 if (SUCCEEDED(rc) && aProgress != NULL)
3897 *aProgress = pProgress;
3898 }
3899 else if (pTask != NULL)
3900 delete pTask;
3901
3902 return rc;
3903}
3904
3905/**
3906 * Returns a preferred format for differencing media.
3907 */
3908Utf8Str Medium::getPreferredDiffFormat()
3909{
3910 AutoCaller autoCaller(this);
3911 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
3912
3913 /* check that our own format supports diffs */
3914 if (!(m->formatObj->getCapabilities() & MediumFormatCapabilities_Differencing))
3915 {
3916 /* use the default format if not */
3917 Utf8Str tmp;
3918 m->pVirtualBox->getDefaultHardDiskFormat(tmp);
3919 return tmp;
3920 }
3921
3922 /* m->strFormat is const, no need to lock */
3923 return m->strFormat;
3924}
3925
3926/**
3927 * Implementation for the public Medium::Close() with the exception of calling
3928 * VirtualBox::saveSettings(), in case someone wants to call this for several
3929 * media.
3930 *
3931 * After this returns with success, uninit() has been called on the medium, and
3932 * the object is no longer usable ("not ready" state).
3933 *
3934 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs that will receive the registry IDs that need saving.
3935 * @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here
3936 * upon which the Medium instance gets uninitialized.
3937 * @return
3938 */
3939HRESULT Medium::close(GuidList *pllRegistriesThatNeedSaving,
3940 AutoCaller &autoCaller)
3941{
3942 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
3943 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
3944 this->lockHandle()
3945 COMMA_LOCKVAL_SRC_POS);
3946
3947 LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str()));
3948
3949 bool wasCreated = true;
3950
3951 switch (m->state)
3952 {
3953 case MediumState_NotCreated:
3954 wasCreated = false;
3955 break;
3956 case MediumState_Created:
3957 case MediumState_Inaccessible:
3958 break;
3959 default:
3960 return setStateError();
3961 }
3962
3963 if (m->backRefs.size() != 0)
3964 return setError(VBOX_E_OBJECT_IN_USE,
3965 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
3966 m->strLocationFull.c_str(), m->backRefs.size());
3967
3968 // perform extra media-dependent close checks
3969 HRESULT rc = canClose();
3970 if (FAILED(rc)) return rc;
3971
3972 if (wasCreated)
3973 {
3974 // remove from the list of known media before performing actual
3975 // uninitialization (to keep the media registry consistent on
3976 // failure to do so)
3977 rc = unregisterWithVirtualBox(pllRegistriesThatNeedSaving);
3978 if (FAILED(rc)) return rc;
3979 }
3980
3981 // leave the AutoCaller, as otherwise uninit() will simply hang
3982 autoCaller.release();
3983
3984 // Keep the locks held until after uninit, as otherwise the consistency
3985 // of the medium tree cannot be guaranteed.
3986 uninit();
3987
3988 LogFlowFuncLeave();
3989
3990 return rc;
3991}
3992
3993/**
3994 * Deletes the medium storage unit.
3995 *
3996 * If @a aProgress is not NULL but the object it points to is @c null then a new
3997 * progress object will be created and assigned to @a *aProgress on success,
3998 * otherwise the existing progress object is used. If Progress is NULL, then no
3999 * progress object is created/used at all.
4000 *
4001 * When @a aWait is @c false, this method will create a thread to perform the
4002 * delete operation asynchronously and will return immediately. Otherwise, it
4003 * will perform the operation on the calling thread and will not return to the
4004 * caller until the operation is completed. Note that @a aProgress cannot be
4005 * NULL when @a aWait is @c false (this method will assert in this case).
4006 *
4007 * @param aProgress Where to find/store a Progress object to track operation
4008 * completion.
4009 * @param aWait @c true if this method should block instead of creating
4010 * an asynchronous thread.
4011 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
4012 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
4013 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
4014 * and this parameter is ignored.
4015 *
4016 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
4017 * writing.
4018 */
4019HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
4020 bool aWait,
4021 GuidList *pllRegistriesThatNeedSaving)
4022{
4023 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4024
4025 AutoCaller autoCaller(this);
4026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4027
4028 HRESULT rc = S_OK;
4029 ComObjPtr<Progress> pProgress;
4030 Medium::Task *pTask = NULL;
4031
4032 try
4033 {
4034 /* we're accessing the media tree, and canClose() needs it too */
4035 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
4036 this->lockHandle()
4037 COMMA_LOCKVAL_SRC_POS);
4038 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
4039
4040 if ( !(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
4041 | MediumFormatCapabilities_CreateFixed)))
4042 throw setError(VBOX_E_NOT_SUPPORTED,
4043 tr("Medium format '%s' does not support storage deletion"),
4044 m->strFormat.c_str());
4045
4046 /* Note that we are fine with Inaccessible state too: a) for symmetry
4047 * with create calls and b) because it doesn't really harm to try, if
4048 * it is really inaccessible, the delete operation will fail anyway.
4049 * Accepting Inaccessible state is especially important because all
4050 * registered media are initially Inaccessible upon VBoxSVC startup
4051 * until COMGETTER(RefreshState) is called. Accept Deleting state
4052 * because some callers need to put the medium in this state early
4053 * to prevent races. */
4054 switch (m->state)
4055 {
4056 case MediumState_Created:
4057 case MediumState_Deleting:
4058 case MediumState_Inaccessible:
4059 break;
4060 default:
4061 throw setStateError();
4062 }
4063
4064 if (m->backRefs.size() != 0)
4065 {
4066 Utf8Str strMachines;
4067 for (BackRefList::const_iterator it = m->backRefs.begin();
4068 it != m->backRefs.end();
4069 ++it)
4070 {
4071 const BackRef &b = *it;
4072 if (strMachines.length())
4073 strMachines.append(", ");
4074 strMachines.append(b.machineId.toString().c_str());
4075 }
4076#ifdef DEBUG
4077 dumpBackRefs();
4078#endif
4079 throw setError(VBOX_E_OBJECT_IN_USE,
4080 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
4081 m->strLocationFull.c_str(),
4082 m->backRefs.size(),
4083 strMachines.c_str());
4084 }
4085
4086 rc = canClose();
4087 if (FAILED(rc))
4088 throw rc;
4089
4090 /* go to Deleting state, so that the medium is not actually locked */
4091 if (m->state != MediumState_Deleting)
4092 {
4093 rc = markForDeletion();
4094 if (FAILED(rc))
4095 throw rc;
4096 }
4097
4098 /* Build the medium lock list. */
4099 MediumLockList *pMediumLockList(new MediumLockList());
4100 rc = createMediumLockList(true /* fFailIfInaccessible */,
4101 true /* fMediumLockWrite */,
4102 NULL,
4103 *pMediumLockList);
4104 if (FAILED(rc))
4105 {
4106 delete pMediumLockList;
4107 throw rc;
4108 }
4109
4110 rc = pMediumLockList->Lock();
4111 if (FAILED(rc))
4112 {
4113 delete pMediumLockList;
4114 throw setError(rc,
4115 tr("Failed to lock media when deleting '%s'"),
4116 getLocationFull().c_str());
4117 }
4118
4119 /* try to remove from the list of known media before performing
4120 * actual deletion (we favor the consistency of the media registry
4121 * which would have been broken if unregisterWithVirtualBox() failed
4122 * after we successfully deleted the storage) */
4123 rc = unregisterWithVirtualBox(pllRegistriesThatNeedSaving);
4124 if (FAILED(rc))
4125 throw rc;
4126 // no longer need lock
4127 multilock.release();
4128
4129 if (aProgress != NULL)
4130 {
4131 /* use the existing progress object... */
4132 pProgress = *aProgress;
4133
4134 /* ...but create a new one if it is null */
4135 if (pProgress.isNull())
4136 {
4137 pProgress.createObject();
4138 rc = pProgress->init(m->pVirtualBox,
4139 static_cast<IMedium*>(this),
4140 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
4141 FALSE /* aCancelable */);
4142 if (FAILED(rc))
4143 throw rc;
4144 }
4145 }
4146
4147 /* setup task object to carry out the operation sync/async */
4148 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
4149 rc = pTask->rc();
4150 AssertComRC(rc);
4151 if (FAILED(rc))
4152 throw rc;
4153 }
4154 catch (HRESULT aRC) { rc = aRC; }
4155
4156 if (SUCCEEDED(rc))
4157 {
4158 if (aWait)
4159 rc = runNow(pTask, NULL /* pfNeedsGlobalSaveSettings*/);
4160 else
4161 rc = startThread(pTask);
4162
4163 if (SUCCEEDED(rc) && aProgress != NULL)
4164 *aProgress = pProgress;
4165
4166 }
4167 else
4168 {
4169 if (pTask)
4170 delete pTask;
4171
4172 /* Undo deleting state if necessary. */
4173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4174 unmarkForDeletion();
4175 }
4176
4177 return rc;
4178}
4179
4180/**
4181 * Mark a medium for deletion.
4182 *
4183 * @note Caller must hold the write lock on this medium!
4184 */
4185HRESULT Medium::markForDeletion()
4186{
4187 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4188 switch (m->state)
4189 {
4190 case MediumState_Created:
4191 case MediumState_Inaccessible:
4192 m->preLockState = m->state;
4193 m->state = MediumState_Deleting;
4194 return S_OK;
4195 default:
4196 return setStateError();
4197 }
4198}
4199
4200/**
4201 * Removes the "mark for deletion".
4202 *
4203 * @note Caller must hold the write lock on this medium!
4204 */
4205HRESULT Medium::unmarkForDeletion()
4206{
4207 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4208 switch (m->state)
4209 {
4210 case MediumState_Deleting:
4211 m->state = m->preLockState;
4212 return S_OK;
4213 default:
4214 return setStateError();
4215 }
4216}
4217
4218/**
4219 * Mark a medium for deletion which is in locked state.
4220 *
4221 * @note Caller must hold the write lock on this medium!
4222 */
4223HRESULT Medium::markLockedForDeletion()
4224{
4225 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4226 if ( ( m->state == MediumState_LockedRead
4227 || m->state == MediumState_LockedWrite)
4228 && m->preLockState == MediumState_Created)
4229 {
4230 m->preLockState = MediumState_Deleting;
4231 return S_OK;
4232 }
4233 else
4234 return setStateError();
4235}
4236
4237/**
4238 * Removes the "mark for deletion" for a medium in locked state.
4239 *
4240 * @note Caller must hold the write lock on this medium!
4241 */
4242HRESULT Medium::unmarkLockedForDeletion()
4243{
4244 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
4245 if ( ( m->state == MediumState_LockedRead
4246 || m->state == MediumState_LockedWrite)
4247 && m->preLockState == MediumState_Deleting)
4248 {
4249 m->preLockState = MediumState_Created;
4250 return S_OK;
4251 }
4252 else
4253 return setStateError();
4254}
4255
4256/**
4257 * Prepares this (source) medium, target medium and all intermediate media
4258 * for the merge operation.
4259 *
4260 * This method is to be called prior to calling the #mergeTo() to perform
4261 * necessary consistency checks and place involved media to appropriate
4262 * states. If #mergeTo() is not called or fails, the state modifications
4263 * performed by this method must be undone by #cancelMergeTo().
4264 *
4265 * See #mergeTo() for more information about merging.
4266 *
4267 * @param pTarget Target medium.
4268 * @param aMachineId Allowed machine attachment. NULL means do not check.
4269 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
4270 * do not check.
4271 * @param fLockMedia Flag whether to lock the medium lock list or not.
4272 * If set to false and the medium lock list locking fails
4273 * later you must call #cancelMergeTo().
4274 * @param fMergeForward Resulting merge direction (out).
4275 * @param pParentForTarget New parent for target medium after merge (out).
4276 * @param aChildrenToReparent List of children of the source which will have
4277 * to be reparented to the target after merge (out).
4278 * @param aMediumLockList Medium locking information (out).
4279 *
4280 * @note Locks medium tree for reading. Locks this object, aTarget and all
4281 * intermediate media for writing.
4282 */
4283HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
4284 const Guid *aMachineId,
4285 const Guid *aSnapshotId,
4286 bool fLockMedia,
4287 bool &fMergeForward,
4288 ComObjPtr<Medium> &pParentForTarget,
4289 MediaList &aChildrenToReparent,
4290 MediumLockList * &aMediumLockList)
4291{
4292 AssertReturn(pTarget != NULL, E_FAIL);
4293 AssertReturn(pTarget != this, E_FAIL);
4294
4295 AutoCaller autoCaller(this);
4296 AssertComRCReturnRC(autoCaller.rc());
4297
4298 AutoCaller targetCaller(pTarget);
4299 AssertComRCReturnRC(targetCaller.rc());
4300
4301 HRESULT rc = S_OK;
4302 fMergeForward = false;
4303 pParentForTarget.setNull();
4304 aChildrenToReparent.clear();
4305 Assert(aMediumLockList == NULL);
4306 aMediumLockList = NULL;
4307
4308 try
4309 {
4310 // locking: we need the tree lock first because we access parent pointers
4311 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4312
4313 /* more sanity checking and figuring out the merge direction */
4314 ComObjPtr<Medium> pMedium = getParent();
4315 while (!pMedium.isNull() && pMedium != pTarget)
4316 pMedium = pMedium->getParent();
4317 if (pMedium == pTarget)
4318 fMergeForward = false;
4319 else
4320 {
4321 pMedium = pTarget->getParent();
4322 while (!pMedium.isNull() && pMedium != this)
4323 pMedium = pMedium->getParent();
4324 if (pMedium == this)
4325 fMergeForward = true;
4326 else
4327 {
4328 Utf8Str tgtLoc;
4329 {
4330 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4331 tgtLoc = pTarget->getLocationFull();
4332 }
4333
4334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4335 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4336 tr("Media '%s' and '%s' are unrelated"),
4337 m->strLocationFull.c_str(), tgtLoc.c_str());
4338 }
4339 }
4340
4341 /* Build the lock list. */
4342 aMediumLockList = new MediumLockList();
4343 if (fMergeForward)
4344 rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
4345 true /* fMediumLockWrite */,
4346 NULL,
4347 *aMediumLockList);
4348 else
4349 rc = createMediumLockList(true /* fFailIfInaccessible */,
4350 false /* fMediumLockWrite */,
4351 NULL,
4352 *aMediumLockList);
4353 if (FAILED(rc))
4354 throw rc;
4355
4356 /* Sanity checking, must be after lock list creation as it depends on
4357 * valid medium states. The medium objects must be accessible. Only
4358 * do this if immediate locking is requested, otherwise it fails when
4359 * we construct a medium lock list for an already running VM. Snapshot
4360 * deletion uses this to simplify its life. */
4361 if (fLockMedia)
4362 {
4363 {
4364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4365 if (m->state != MediumState_Created)
4366 throw setStateError();
4367 }
4368 {
4369 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4370 if (pTarget->m->state != MediumState_Created)
4371 throw pTarget->setStateError();
4372 }
4373 }
4374
4375 /* check medium attachment and other sanity conditions */
4376 if (fMergeForward)
4377 {
4378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4379 if (getChildren().size() > 1)
4380 {
4381 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4382 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4383 m->strLocationFull.c_str(), getChildren().size());
4384 }
4385 /* One backreference is only allowed if the machine ID is not empty
4386 * and it matches the machine the medium is attached to (including
4387 * the snapshot ID if not empty). */
4388 if ( m->backRefs.size() != 0
4389 && ( !aMachineId
4390 || m->backRefs.size() != 1
4391 || aMachineId->isEmpty()
4392 || *getFirstMachineBackrefId() != *aMachineId
4393 || ( (!aSnapshotId || !aSnapshotId->isEmpty())
4394 && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
4395 throw setError(VBOX_E_OBJECT_IN_USE,
4396 tr("Medium '%s' is attached to %d virtual machines"),
4397 m->strLocationFull.c_str(), m->backRefs.size());
4398 if (m->type == MediumType_Immutable)
4399 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4400 tr("Medium '%s' is immutable"),
4401 m->strLocationFull.c_str());
4402 if (m->type == MediumType_MultiAttach)
4403 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4404 tr("Medium '%s' is multi-attach"),
4405 m->strLocationFull.c_str());
4406 }
4407 else
4408 {
4409 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4410 if (pTarget->getChildren().size() > 1)
4411 {
4412 throw setError(VBOX_E_OBJECT_IN_USE,
4413 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4414 pTarget->m->strLocationFull.c_str(),
4415 pTarget->getChildren().size());
4416 }
4417 if (pTarget->m->type == MediumType_Immutable)
4418 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4419 tr("Medium '%s' is immutable"),
4420 pTarget->m->strLocationFull.c_str());
4421 if (pTarget->m->type == MediumType_MultiAttach)
4422 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4423 tr("Medium '%s' is multi-attach"),
4424 pTarget->m->strLocationFull.c_str());
4425 }
4426 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
4427 ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
4428 for (pLast = pLastIntermediate;
4429 !pLast.isNull() && pLast != pTarget && pLast != this;
4430 pLast = pLast->getParent())
4431 {
4432 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4433 if (pLast->getChildren().size() > 1)
4434 {
4435 throw setError(VBOX_E_OBJECT_IN_USE,
4436 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4437 pLast->m->strLocationFull.c_str(),
4438 pLast->getChildren().size());
4439 }
4440 if (pLast->m->backRefs.size() != 0)
4441 throw setError(VBOX_E_OBJECT_IN_USE,
4442 tr("Medium '%s' is attached to %d virtual machines"),
4443 pLast->m->strLocationFull.c_str(),
4444 pLast->m->backRefs.size());
4445
4446 }
4447
4448 /* Update medium states appropriately */
4449 if (m->state == MediumState_Created)
4450 {
4451 rc = markForDeletion();
4452 if (FAILED(rc))
4453 throw rc;
4454 }
4455 else
4456 {
4457 if (fLockMedia)
4458 throw setStateError();
4459 else if ( m->state == MediumState_LockedWrite
4460 || m->state == MediumState_LockedRead)
4461 {
4462 /* Either mark it for deletion in locked state or allow
4463 * others to have done so. */
4464 if (m->preLockState == MediumState_Created)
4465 markLockedForDeletion();
4466 else if (m->preLockState != MediumState_Deleting)
4467 throw setStateError();
4468 }
4469 else
4470 throw setStateError();
4471 }
4472
4473 if (fMergeForward)
4474 {
4475 /* we will need parent to reparent target */
4476 pParentForTarget = m->pParent;
4477 }
4478 else
4479 {
4480 /* we will need to reparent children of the source */
4481 for (MediaList::const_iterator it = getChildren().begin();
4482 it != getChildren().end();
4483 ++it)
4484 {
4485 pMedium = *it;
4486 if (fLockMedia)
4487 {
4488 rc = pMedium->LockWrite(NULL);
4489 if (FAILED(rc))
4490 throw rc;
4491 }
4492
4493 aChildrenToReparent.push_back(pMedium);
4494 }
4495 }
4496 for (pLast = pLastIntermediate;
4497 !pLast.isNull() && pLast != pTarget && pLast != this;
4498 pLast = pLast->getParent())
4499 {
4500 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4501 if (pLast->m->state == MediumState_Created)
4502 {
4503 rc = pLast->markForDeletion();
4504 if (FAILED(rc))
4505 throw rc;
4506 }
4507 else
4508 throw pLast->setStateError();
4509 }
4510
4511 /* Tweak the lock list in the backward merge case, as the target
4512 * isn't marked to be locked for writing yet. */
4513 if (!fMergeForward)
4514 {
4515 MediumLockList::Base::iterator lockListBegin =
4516 aMediumLockList->GetBegin();
4517 MediumLockList::Base::iterator lockListEnd =
4518 aMediumLockList->GetEnd();
4519 lockListEnd--;
4520 for (MediumLockList::Base::iterator it = lockListBegin;
4521 it != lockListEnd;
4522 ++it)
4523 {
4524 MediumLock &mediumLock = *it;
4525 if (mediumLock.GetMedium() == pTarget)
4526 {
4527 HRESULT rc2 = mediumLock.UpdateLock(true);
4528 AssertComRC(rc2);
4529 break;
4530 }
4531 }
4532 }
4533
4534 if (fLockMedia)
4535 {
4536 rc = aMediumLockList->Lock();
4537 if (FAILED(rc))
4538 {
4539 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4540 throw setError(rc,
4541 tr("Failed to lock media when merging to '%s'"),
4542 pTarget->getLocationFull().c_str());
4543 }
4544 }
4545 }
4546 catch (HRESULT aRC) { rc = aRC; }
4547
4548 if (FAILED(rc))
4549 {
4550 delete aMediumLockList;
4551 aMediumLockList = NULL;
4552 }
4553
4554 return rc;
4555}
4556
4557/**
4558 * Merges this medium to the specified medium which must be either its
4559 * direct ancestor or descendant.
4560 *
4561 * Given this medium is SOURCE and the specified medium is TARGET, we will
4562 * get two variants of the merge operation:
4563 *
4564 * forward merge
4565 * ------------------------->
4566 * [Extra] <- SOURCE <- Intermediate <- TARGET
4567 * Any Del Del LockWr
4568 *
4569 *
4570 * backward merge
4571 * <-------------------------
4572 * TARGET <- Intermediate <- SOURCE <- [Extra]
4573 * LockWr Del Del LockWr
4574 *
4575 * Each diagram shows the involved media on the media chain where
4576 * SOURCE and TARGET belong. Under each medium there is a state value which
4577 * the medium must have at a time of the mergeTo() call.
4578 *
4579 * The media in the square braces may be absent (e.g. when the forward
4580 * operation takes place and SOURCE is the base medium, or when the backward
4581 * merge operation takes place and TARGET is the last child in the chain) but if
4582 * they present they are involved too as shown.
4583 *
4584 * Neither the source medium nor intermediate media may be attached to
4585 * any VM directly or in the snapshot, otherwise this method will assert.
4586 *
4587 * The #prepareMergeTo() method must be called prior to this method to place all
4588 * involved to necessary states and perform other consistency checks.
4589 *
4590 * If @a aWait is @c true then this method will perform the operation on the
4591 * calling thread and will not return to the caller until the operation is
4592 * completed. When this method succeeds, all intermediate medium objects in
4593 * the chain will be uninitialized, the state of the target medium (and all
4594 * involved extra media) will be restored. @a aMediumLockList will not be
4595 * deleted, whether the operation is successful or not. The caller has to do
4596 * this if appropriate. Note that this (source) medium is not uninitialized
4597 * because of possible AutoCaller instances held by the caller of this method
4598 * on the current thread. It's therefore the responsibility of the caller to
4599 * call Medium::uninit() after releasing all callers.
4600 *
4601 * If @a aWait is @c false then this method will create a thread to perform the
4602 * operation asynchronously and will return immediately. If the operation
4603 * succeeds, the thread will uninitialize the source medium object and all
4604 * intermediate medium objects in the chain, reset the state of the target
4605 * medium (and all involved extra media) and delete @a aMediumLockList.
4606 * If the operation fails, the thread will only reset the states of all
4607 * involved media and delete @a aMediumLockList.
4608 *
4609 * When this method fails (regardless of the @a aWait mode), it is a caller's
4610 * responsibility to undo state changes and delete @a aMediumLockList using
4611 * #cancelMergeTo().
4612 *
4613 * If @a aProgress is not NULL but the object it points to is @c null then a new
4614 * progress object will be created and assigned to @a *aProgress on success,
4615 * otherwise the existing progress object is used. If Progress is NULL, then no
4616 * progress object is created/used at all. Note that @a aProgress cannot be
4617 * NULL when @a aWait is @c false (this method will assert in this case).
4618 *
4619 * @param pTarget Target medium.
4620 * @param fMergeForward Merge direction.
4621 * @param pParentForTarget New parent for target medium after merge.
4622 * @param aChildrenToReparent List of children of the source which will have
4623 * to be reparented to the target after merge.
4624 * @param aMediumLockList Medium locking information.
4625 * @param aProgress Where to find/store a Progress object to track operation
4626 * completion.
4627 * @param aWait @c true if this method should block instead of creating
4628 * an asynchronous thread.
4629 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
4630 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
4631 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
4632 * and this parameter is ignored.
4633 *
4634 * @note Locks the tree lock for writing. Locks the media from the chain
4635 * for writing.
4636 */
4637HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
4638 bool fMergeForward,
4639 const ComObjPtr<Medium> &pParentForTarget,
4640 const MediaList &aChildrenToReparent,
4641 MediumLockList *aMediumLockList,
4642 ComObjPtr <Progress> *aProgress,
4643 bool aWait,
4644 GuidList *pllRegistriesThatNeedSaving)
4645{
4646 AssertReturn(pTarget != NULL, E_FAIL);
4647 AssertReturn(pTarget != this, E_FAIL);
4648 AssertReturn(aMediumLockList != NULL, E_FAIL);
4649 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4650
4651 AutoCaller autoCaller(this);
4652 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4653
4654 AutoCaller targetCaller(pTarget);
4655 AssertComRCReturnRC(targetCaller.rc());
4656
4657 HRESULT rc = S_OK;
4658 ComObjPtr <Progress> pProgress;
4659 Medium::Task *pTask = NULL;
4660
4661 try
4662 {
4663 if (aProgress != NULL)
4664 {
4665 /* use the existing progress object... */
4666 pProgress = *aProgress;
4667
4668 /* ...but create a new one if it is null */
4669 if (pProgress.isNull())
4670 {
4671 Utf8Str tgtName;
4672 {
4673 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4674 tgtName = pTarget->getName();
4675 }
4676
4677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4678
4679 pProgress.createObject();
4680 rc = pProgress->init(m->pVirtualBox,
4681 static_cast<IMedium*>(this),
4682 BstrFmt(tr("Merging medium '%s' to '%s'"),
4683 getName().c_str(),
4684 tgtName.c_str()).raw(),
4685 TRUE /* aCancelable */);
4686 if (FAILED(rc))
4687 throw rc;
4688 }
4689 }
4690
4691 /* setup task object to carry out the operation sync/async */
4692 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
4693 pParentForTarget, aChildrenToReparent,
4694 pProgress, aMediumLockList,
4695 aWait /* fKeepMediumLockList */);
4696 rc = pTask->rc();
4697 AssertComRC(rc);
4698 if (FAILED(rc))
4699 throw rc;
4700 }
4701 catch (HRESULT aRC) { rc = aRC; }
4702
4703 if (SUCCEEDED(rc))
4704 {
4705 if (aWait)
4706 rc = runNow(pTask, pllRegistriesThatNeedSaving);
4707 else
4708 rc = startThread(pTask);
4709
4710 if (SUCCEEDED(rc) && aProgress != NULL)
4711 *aProgress = pProgress;
4712 }
4713 else if (pTask != NULL)
4714 delete pTask;
4715
4716 return rc;
4717}
4718
4719/**
4720 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
4721 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
4722 * the medium objects in @a aChildrenToReparent.
4723 *
4724 * @param aChildrenToReparent List of children of the source which will have
4725 * to be reparented to the target after merge.
4726 * @param aMediumLockList Medium locking information.
4727 *
4728 * @note Locks the media from the chain for writing.
4729 */
4730void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
4731 MediumLockList *aMediumLockList)
4732{
4733 AutoCaller autoCaller(this);
4734 AssertComRCReturnVoid(autoCaller.rc());
4735
4736 AssertReturnVoid(aMediumLockList != NULL);
4737
4738 /* Revert media marked for deletion to previous state. */
4739 HRESULT rc;
4740 MediumLockList::Base::const_iterator mediumListBegin =
4741 aMediumLockList->GetBegin();
4742 MediumLockList::Base::const_iterator mediumListEnd =
4743 aMediumLockList->GetEnd();
4744 for (MediumLockList::Base::const_iterator it = mediumListBegin;
4745 it != mediumListEnd;
4746 ++it)
4747 {
4748 const MediumLock &mediumLock = *it;
4749 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4750 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4751
4752 if (pMedium->m->state == MediumState_Deleting)
4753 {
4754 rc = pMedium->unmarkForDeletion();
4755 AssertComRC(rc);
4756 }
4757 }
4758
4759 /* the destructor will do the work */
4760 delete aMediumLockList;
4761
4762 /* unlock the children which had to be reparented */
4763 for (MediaList::const_iterator it = aChildrenToReparent.begin();
4764 it != aChildrenToReparent.end();
4765 ++it)
4766 {
4767 const ComObjPtr<Medium> &pMedium = *it;
4768
4769 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4770 pMedium->UnlockWrite(NULL);
4771 }
4772}
4773
4774/**
4775 * Fix the parent UUID of all children to point to this medium as their
4776 * parent.
4777 */
4778HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
4779{
4780 MediumLockList mediumLockList;
4781 HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
4782 false /* fMediumLockWrite */,
4783 this,
4784 mediumLockList);
4785 AssertComRCReturnRC(rc);
4786
4787 try
4788 {
4789 PVBOXHDD hdd;
4790 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
4791 ComAssertRCThrow(vrc, E_FAIL);
4792
4793 try
4794 {
4795 MediumLockList::Base::iterator lockListBegin =
4796 mediumLockList.GetBegin();
4797 MediumLockList::Base::iterator lockListEnd =
4798 mediumLockList.GetEnd();
4799 for (MediumLockList::Base::iterator it = lockListBegin;
4800 it != lockListEnd;
4801 ++it)
4802 {
4803 MediumLock &mediumLock = *it;
4804 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4805 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4806
4807 // open the medium
4808 vrc = VDOpen(hdd,
4809 pMedium->m->strFormat.c_str(),
4810 pMedium->m->strLocationFull.c_str(),
4811 VD_OPEN_FLAGS_READONLY,
4812 pMedium->m->vdImageIfaces);
4813 if (RT_FAILURE(vrc))
4814 throw vrc;
4815 }
4816
4817 for (MediaList::const_iterator it = childrenToReparent.begin();
4818 it != childrenToReparent.end();
4819 ++it)
4820 {
4821 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
4822 vrc = VDOpen(hdd,
4823 (*it)->m->strFormat.c_str(),
4824 (*it)->m->strLocationFull.c_str(),
4825 VD_OPEN_FLAGS_INFO,
4826 (*it)->m->vdImageIfaces);
4827 if (RT_FAILURE(vrc))
4828 throw vrc;
4829
4830 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
4831 if (RT_FAILURE(vrc))
4832 throw vrc;
4833
4834 vrc = VDClose(hdd, false /* fDelete */);
4835 if (RT_FAILURE(vrc))
4836 throw vrc;
4837
4838 (*it)->UnlockWrite(NULL);
4839 }
4840 }
4841 catch (HRESULT aRC) { rc = aRC; }
4842 catch (int aVRC)
4843 {
4844 throw setError(E_FAIL,
4845 tr("Could not update medium UUID references to parent '%s' (%s)"),
4846 m->strLocationFull.c_str(),
4847 vdError(aVRC).c_str());
4848 }
4849
4850 VDDestroy(hdd);
4851 }
4852 catch (HRESULT aRC) { rc = aRC; }
4853
4854 return rc;
4855}
4856
4857/**
4858 * Used by IAppliance to export disk images.
4859 *
4860 * @param aFilename Filename to create (UTF8).
4861 * @param aFormat Medium format for creating @a aFilename.
4862 * @param aVariant Which exact image format variant to use
4863 * for the destination image.
4864 * @param aVDImageIOCallbacks Pointer to the callback table for a
4865 * VDINTERFACEIO interface. May be NULL.
4866 * @param aVDImageIOUser Opaque data for the callbacks.
4867 * @param aProgress Progress object to use.
4868 * @return
4869 * @note The source format is defined by the Medium instance.
4870 */
4871HRESULT Medium::exportFile(const char *aFilename,
4872 const ComObjPtr<MediumFormat> &aFormat,
4873 MediumVariant_T aVariant,
4874 void *aVDImageIOCallbacks, void *aVDImageIOUser,
4875 const ComObjPtr<Progress> &aProgress)
4876{
4877 AssertPtrReturn(aFilename, E_INVALIDARG);
4878 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
4879 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
4880
4881 AutoCaller autoCaller(this);
4882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4883
4884 HRESULT rc = S_OK;
4885 Medium::Task *pTask = NULL;
4886
4887 try
4888 {
4889 // locking: we need the tree lock first because we access parent pointers
4890 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4891 // and we need to write-lock the media involved
4892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4893
4894 /* Build the source lock list. */
4895 MediumLockList *pSourceMediumLockList(new MediumLockList());
4896 rc = createMediumLockList(true /* fFailIfInaccessible */,
4897 false /* fMediumLockWrite */,
4898 NULL,
4899 *pSourceMediumLockList);
4900 if (FAILED(rc))
4901 {
4902 delete pSourceMediumLockList;
4903 throw rc;
4904 }
4905
4906 rc = pSourceMediumLockList->Lock();
4907 if (FAILED(rc))
4908 {
4909 delete pSourceMediumLockList;
4910 throw setError(rc,
4911 tr("Failed to lock source media '%s'"),
4912 getLocationFull().c_str());
4913 }
4914
4915 /* setup task object to carry out the operation asynchronously */
4916 pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
4917 aVariant, aVDImageIOCallbacks,
4918 aVDImageIOUser, pSourceMediumLockList);
4919 rc = pTask->rc();
4920 AssertComRC(rc);
4921 if (FAILED(rc))
4922 throw rc;
4923 }
4924 catch (HRESULT aRC) { rc = aRC; }
4925
4926 if (SUCCEEDED(rc))
4927 rc = startThread(pTask);
4928 else if (pTask != NULL)
4929 delete pTask;
4930
4931 return rc;
4932}
4933
4934/**
4935 * Used by IAppliance to import disk images.
4936 *
4937 * @param aFilename Filename to read (UTF8).
4938 * @param aFormat Medium format for reading @a aFilename.
4939 * @param aVariant Which exact image format variant to use
4940 * for the destination image.
4941 * @param aVDImageIOCallbacks Pointer to the callback table for a
4942 * VDINTERFACEIO interface. May be NULL.
4943 * @param aVDImageIOUser Opaque data for the callbacks.
4944 * @param aParent Parent medium. May be NULL.
4945 * @param aProgress Progress object to use.
4946 * @return
4947 * @note The destination format is defined by the Medium instance.
4948 */
4949HRESULT Medium::importFile(const char *aFilename,
4950 const ComObjPtr<MediumFormat> &aFormat,
4951 MediumVariant_T aVariant,
4952 void *aVDImageIOCallbacks, void *aVDImageIOUser,
4953 const ComObjPtr<Medium> &aParent,
4954 const ComObjPtr<Progress> &aProgress)
4955{
4956 AssertPtrReturn(aFilename, E_INVALIDARG);
4957 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
4958 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
4959
4960 AutoCaller autoCaller(this);
4961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4962
4963 HRESULT rc = S_OK;
4964 Medium::Task *pTask = NULL;
4965
4966 try
4967 {
4968 // locking: we need the tree lock first because we access parent pointers
4969 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4970 // and we need to write-lock the media involved
4971 AutoMultiWriteLock2 alock(this, aParent COMMA_LOCKVAL_SRC_POS);
4972
4973 if ( m->state != MediumState_NotCreated
4974 && m->state != MediumState_Created)
4975 throw setStateError();
4976
4977 /* Build the target lock list. */
4978 MediumLockList *pTargetMediumLockList(new MediumLockList());
4979 rc = createMediumLockList(true /* fFailIfInaccessible */,
4980 true /* fMediumLockWrite */,
4981 aParent,
4982 *pTargetMediumLockList);
4983 if (FAILED(rc))
4984 {
4985 delete pTargetMediumLockList;
4986 throw rc;
4987 }
4988
4989 rc = pTargetMediumLockList->Lock();
4990 if (FAILED(rc))
4991 {
4992 delete pTargetMediumLockList;
4993 throw setError(rc,
4994 tr("Failed to lock target media '%s'"),
4995 getLocationFull().c_str());
4996 }
4997
4998 /* setup task object to carry out the operation asynchronously */
4999 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,
5000 aVariant, aVDImageIOCallbacks,
5001 aVDImageIOUser, aParent,
5002 pTargetMediumLockList);
5003 rc = pTask->rc();
5004 AssertComRC(rc);
5005 if (FAILED(rc))
5006 throw rc;
5007
5008 if (m->state == MediumState_NotCreated)
5009 m->state = MediumState_Creating;
5010 }
5011 catch (HRESULT aRC) { rc = aRC; }
5012
5013 if (SUCCEEDED(rc))
5014 rc = startThread(pTask);
5015 else if (pTask != NULL)
5016 delete pTask;
5017
5018 return rc;
5019}
5020
5021////////////////////////////////////////////////////////////////////////////////
5022//
5023// Private methods
5024//
5025////////////////////////////////////////////////////////////////////////////////
5026
5027/**
5028 * Queries information from the medium.
5029 *
5030 * As a result of this call, the accessibility state and data members such as
5031 * size and description will be updated with the current information.
5032 *
5033 * @note This method may block during a system I/O call that checks storage
5034 * accessibility.
5035 *
5036 * @note Locks medium tree for reading and writing (for new diff media checked
5037 * for the first time). Locks mParent for reading. Locks this object for
5038 * writing.
5039 *
5040 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
5041 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs())
5042 * @return
5043 */
5044HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
5045{
5046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5047
5048 if ( m->state != MediumState_Created
5049 && m->state != MediumState_Inaccessible
5050 && m->state != MediumState_LockedRead)
5051 return E_FAIL;
5052
5053 HRESULT rc = S_OK;
5054
5055 int vrc = VINF_SUCCESS;
5056
5057 /* check if a blocking queryInfo() call is in progress on some other thread,
5058 * and wait for it to finish if so instead of querying data ourselves */
5059 if (m->queryInfoRunning)
5060 {
5061 Assert( m->state == MediumState_LockedRead
5062 || m->state == MediumState_LockedWrite);
5063
5064 alock.leave();
5065 vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
5066 alock.enter();
5067
5068 AssertRC(vrc);
5069
5070 return S_OK;
5071 }
5072
5073 bool success = false;
5074 Utf8Str lastAccessError;
5075
5076 /* are we dealing with a new medium constructed using the existing
5077 * location? */
5078 bool isImport = m->id.isEmpty();
5079 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
5080
5081 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
5082 * media because that would prevent necessary modifications
5083 * when opening media of some third-party formats for the first
5084 * time in VirtualBox (such as VMDK for which VDOpen() needs to
5085 * generate an UUID if it is missing) */
5086 if ( (m->hddOpenMode == OpenReadOnly)
5087 || m->type == MediumType_Readonly
5088 || !isImport
5089 )
5090 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5091
5092 /* Open shareable medium with the appropriate flags */
5093 if (m->type == MediumType_Shareable)
5094 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
5095
5096 /* Lock the medium, which makes the behavior much more consistent */
5097 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
5098 rc = LockRead(NULL);
5099 else
5100 rc = LockWrite(NULL);
5101 if (FAILED(rc)) return rc;
5102
5103 /* Copies of the input state fields which are not read-only,
5104 * as we're dropping the lock. CAUTION: be extremely careful what
5105 * you do with the contents of this medium object, as you will
5106 * create races if there are concurrent changes. */
5107 Utf8Str format(m->strFormat);
5108 Utf8Str location(m->strLocationFull);
5109 ComObjPtr<MediumFormat> formatObj = m->formatObj;
5110
5111 /* "Output" values which can't be set because the lock isn't held
5112 * at the time the values are determined. */
5113 Guid mediumId = m->id;
5114 uint64_t mediumSize = 0;
5115 uint64_t mediumLogicalSize = 0;
5116
5117 /* Flag whether a base image has a non-zero parent UUID and thus
5118 * need repairing after it was closed again. */
5119 bool fRepairImageZeroParentUuid = false;
5120
5121 /* leave the lock before a lengthy operation */
5122 vrc = RTSemEventMultiReset(m->queryInfoSem);
5123 AssertRCReturn(vrc, E_FAIL);
5124 m->queryInfoRunning = true;
5125 alock.leave();
5126
5127 try
5128 {
5129 /* skip accessibility checks for host drives */
5130 if (m->hostDrive)
5131 {
5132 success = true;
5133 throw S_OK;
5134 }
5135
5136 PVBOXHDD hdd;
5137 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
5138 ComAssertRCThrow(vrc, E_FAIL);
5139
5140 try
5141 {
5142 /** @todo This kind of opening of media is assuming that diff
5143 * media can be opened as base media. Should be documented that
5144 * it must work for all medium format backends. */
5145 vrc = VDOpen(hdd,
5146 format.c_str(),
5147 location.c_str(),
5148 uOpenFlags,
5149 m->vdImageIfaces);
5150 if (RT_FAILURE(vrc))
5151 {
5152 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
5153 location.c_str(), vdError(vrc).c_str());
5154 throw S_OK;
5155 }
5156
5157 if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
5158 {
5159 /* Modify the UUIDs if necessary. The associated fields are
5160 * not modified by other code, so no need to copy. */
5161 if (fSetImageId)
5162 {
5163 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
5164 ComAssertRCThrow(vrc, E_FAIL);
5165 }
5166 if (fSetParentId)
5167 {
5168 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
5169 ComAssertRCThrow(vrc, E_FAIL);
5170 }
5171 /* zap the information, these are no long-term members */
5172 unconst(m->uuidImage).clear();
5173 unconst(m->uuidParentImage).clear();
5174
5175 /* check the UUID */
5176 RTUUID uuid;
5177 vrc = VDGetUuid(hdd, 0, &uuid);
5178 ComAssertRCThrow(vrc, E_FAIL);
5179
5180 if (isImport)
5181 {
5182 mediumId = uuid;
5183
5184 if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
5185 // only when importing a VDMK that has no UUID, create one in memory
5186 mediumId.create();
5187 }
5188 else
5189 {
5190 Assert(!mediumId.isEmpty());
5191
5192 if (mediumId != uuid)
5193 {
5194 lastAccessError = Utf8StrFmt(
5195 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
5196 &uuid,
5197 location.c_str(),
5198 mediumId.raw(),
5199 m->pVirtualBox->settingsFilePath().c_str());
5200 throw S_OK;
5201 }
5202 }
5203 }
5204 else
5205 {
5206 /* the backend does not support storing UUIDs within the
5207 * underlying storage so use what we store in XML */
5208
5209 /* generate an UUID for an imported UUID-less medium */
5210 if (isImport)
5211 {
5212 if (fSetImageId)
5213 mediumId = m->uuidImage;
5214 else
5215 mediumId.create();
5216 }
5217 }
5218
5219 /* get the medium variant */
5220 unsigned uImageFlags;
5221 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5222 ComAssertRCThrow(vrc, E_FAIL);
5223 m->variant = (MediumVariant_T)uImageFlags;
5224
5225 /* check/get the parent uuid and update corresponding state */
5226 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
5227 {
5228 RTUUID parentId;
5229 vrc = VDGetParentUuid(hdd, 0, &parentId);
5230 ComAssertRCThrow(vrc, E_FAIL);
5231
5232 /* streamOptimized VMDK images are only accepted as base
5233 * images, as this allows automatic repair of OVF appliances.
5234 * Since such images don't support random writes they will not
5235 * be created for diff images. Only an overly smart user might
5236 * manually create this case. Too bad for him. */
5237 if ( isImport
5238 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
5239 {
5240 /* the parent must be known to us. Note that we freely
5241 * call locking methods of mVirtualBox and parent, as all
5242 * relevant locks must be already held. There may be no
5243 * concurrent access to the just opened medium on other
5244 * threads yet (and init() will fail if this method reports
5245 * MediumState_Inaccessible) */
5246
5247 Guid id = parentId;
5248 ComObjPtr<Medium> pParent;
5249 rc = m->pVirtualBox->findHardDiskById(id, false /* aSetError */, &pParent);
5250 if (FAILED(rc))
5251 {
5252 lastAccessError = Utf8StrFmt(
5253 tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
5254 &parentId, location.c_str(),
5255 m->pVirtualBox->settingsFilePath().c_str());
5256 throw S_OK;
5257 }
5258
5259 /* we set mParent & children() */
5260 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5261
5262 Assert(m->pParent.isNull());
5263 m->pParent = pParent;
5264 m->pParent->m->llChildren.push_back(this);
5265 }
5266 else
5267 {
5268 /* we access mParent */
5269 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5270
5271 /* check that parent UUIDs match. Note that there's no need
5272 * for the parent's AutoCaller (our lifetime is bound to
5273 * it) */
5274
5275 if (m->pParent.isNull())
5276 {
5277 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
5278 * and 3.1.0-3.1.8 there are base images out there
5279 * which have a non-zero parent UUID. No point in
5280 * complaining about them, instead automatically
5281 * repair the problem. Later we can bring back the
5282 * error message, but we should wait until really
5283 * most users have repaired their images, either with
5284 * VBoxFixHdd or this way. */
5285#if 1
5286 fRepairImageZeroParentUuid = true;
5287#else /* 0 */
5288 lastAccessError = Utf8StrFmt(
5289 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
5290 location.c_str(),
5291 m->pVirtualBox->settingsFilePath().c_str());
5292 throw S_OK;
5293#endif /* 0 */
5294 }
5295
5296 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
5297 if ( !fRepairImageZeroParentUuid
5298 && m->pParent->getState() != MediumState_Inaccessible
5299 && m->pParent->getId() != parentId)
5300 {
5301 lastAccessError = Utf8StrFmt(
5302 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
5303 &parentId, location.c_str(),
5304 m->pParent->getId().raw(),
5305 m->pVirtualBox->settingsFilePath().c_str());
5306 throw S_OK;
5307 }
5308
5309 /// @todo NEWMEDIA what to do if the parent is not
5310 /// accessible while the diff is? Probably nothing. The
5311 /// real code will detect the mismatch anyway.
5312 }
5313 }
5314
5315 mediumSize = VDGetFileSize(hdd, 0);
5316 mediumLogicalSize = VDGetSize(hdd, 0);
5317
5318 success = true;
5319 }
5320 catch (HRESULT aRC)
5321 {
5322 rc = aRC;
5323 }
5324
5325 VDDestroy(hdd);
5326 }
5327 catch (HRESULT aRC)
5328 {
5329 rc = aRC;
5330 }
5331
5332 alock.enter();
5333
5334 if (isImport)
5335 unconst(m->id) = mediumId;
5336
5337 if (success)
5338 {
5339 m->size = mediumSize;
5340 m->logicalSize = mediumLogicalSize;
5341 m->strLastAccessError.setNull();
5342 }
5343 else
5344 {
5345 m->strLastAccessError = lastAccessError;
5346 LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
5347 location.c_str(), m->strLastAccessError.c_str(),
5348 rc, vrc));
5349 }
5350
5351 /* inform other callers if there are any */
5352 RTSemEventMultiSignal(m->queryInfoSem);
5353 m->queryInfoRunning = false;
5354
5355 /* Set the proper state according to the result of the check */
5356 if (success)
5357 m->preLockState = MediumState_Created;
5358 else
5359 m->preLockState = MediumState_Inaccessible;
5360
5361 HRESULT rc2;
5362 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
5363 rc2 = UnlockRead(NULL);
5364 else
5365 rc2 = UnlockWrite(NULL);
5366 if (SUCCEEDED(rc) && FAILED(rc2))
5367 rc = rc2;
5368 if (FAILED(rc)) return rc;
5369
5370 /* If this is a base image which incorrectly has a parent UUID set,
5371 * repair the image now by zeroing the parent UUID. This is only done
5372 * when we have structural information from a config file, on import
5373 * this is not possible. If someone would accidentally call openMedium
5374 * with a diff image before the base is registered this would destroy
5375 * the diff. Not acceptable. */
5376 if (fRepairImageZeroParentUuid)
5377 {
5378 rc = LockWrite(NULL);
5379 if (FAILED(rc)) return rc;
5380
5381 alock.leave();
5382
5383 try
5384 {
5385 PVBOXHDD hdd;
5386 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
5387 ComAssertRCThrow(vrc, E_FAIL);
5388
5389 try
5390 {
5391 vrc = VDOpen(hdd,
5392 format.c_str(),
5393 location.c_str(),
5394 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
5395 m->vdImageIfaces);
5396 if (RT_FAILURE(vrc))
5397 throw S_OK;
5398
5399 RTUUID zeroParentUuid;
5400 RTUuidClear(&zeroParentUuid);
5401 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
5402 ComAssertRCThrow(vrc, E_FAIL);
5403 }
5404 catch (HRESULT aRC)
5405 {
5406 rc = aRC;
5407 }
5408
5409 VDDestroy(hdd);
5410 }
5411 catch (HRESULT aRC)
5412 {
5413 rc = aRC;
5414 }
5415
5416 alock.enter();
5417
5418 rc = UnlockWrite(NULL);
5419 if (SUCCEEDED(rc) && FAILED(rc2))
5420 rc = rc2;
5421 if (FAILED(rc)) return rc;
5422 }
5423
5424 return rc;
5425}
5426
5427/**
5428 * Performs extra checks if the medium can be closed and returns S_OK in
5429 * this case. Otherwise, returns a respective error message. Called by
5430 * Close() under the medium tree lock and the medium lock.
5431 *
5432 * @note Also reused by Medium::Reset().
5433 *
5434 * @note Caller must hold the media tree write lock!
5435 */
5436HRESULT Medium::canClose()
5437{
5438 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5439
5440 if (getChildren().size() != 0)
5441 return setError(VBOX_E_OBJECT_IN_USE,
5442 tr("Cannot close medium '%s' because it has %d child media"),
5443 m->strLocationFull.c_str(), getChildren().size());
5444
5445 return S_OK;
5446}
5447
5448/**
5449 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
5450 *
5451 * This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending
5452 * on the device type of this medium.
5453 *
5454 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs that will receive the registry IDs that need saving.
5455 *
5456 * @note Caller must have locked the media tree lock for writing!
5457 */
5458HRESULT Medium::unregisterWithVirtualBox(GuidList *pllRegistriesThatNeedSaving)
5459{
5460 /* Note that we need to de-associate ourselves from the parent to let
5461 * unregisterHardDisk() properly save the registry */
5462
5463 /* we modify mParent and access children */
5464 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5465
5466 Medium *pParentBackup = m->pParent;
5467 AssertReturn(getChildren().size() == 0, E_FAIL);
5468 if (m->pParent)
5469 deparent();
5470
5471 HRESULT rc = E_FAIL;
5472 switch (m->devType)
5473 {
5474 case DeviceType_DVD:
5475 case DeviceType_Floppy:
5476 rc = m->pVirtualBox->unregisterImage(this,
5477 m->devType,
5478 pllRegistriesThatNeedSaving);
5479 break;
5480
5481 case DeviceType_HardDisk:
5482 rc = m->pVirtualBox->unregisterHardDisk(this, pllRegistriesThatNeedSaving);
5483 break;
5484
5485 default:
5486 break;
5487 }
5488
5489 if (FAILED(rc))
5490 {
5491 if (pParentBackup)
5492 {
5493 // re-associate with the parent as we are still relatives in the registry
5494 m->pParent = pParentBackup;
5495 m->pParent->m->llChildren.push_back(this);
5496 }
5497 }
5498
5499 return rc;
5500}
5501
5502/**
5503 * Sets the extended error info according to the current media state.
5504 *
5505 * @note Must be called from under this object's write or read lock.
5506 */
5507HRESULT Medium::setStateError()
5508{
5509 HRESULT rc = E_FAIL;
5510
5511 switch (m->state)
5512 {
5513 case MediumState_NotCreated:
5514 {
5515 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5516 tr("Storage for the medium '%s' is not created"),
5517 m->strLocationFull.c_str());
5518 break;
5519 }
5520 case MediumState_Created:
5521 {
5522 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5523 tr("Storage for the medium '%s' is already created"),
5524 m->strLocationFull.c_str());
5525 break;
5526 }
5527 case MediumState_LockedRead:
5528 {
5529 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5530 tr("Medium '%s' is locked for reading by another task"),
5531 m->strLocationFull.c_str());
5532 break;
5533 }
5534 case MediumState_LockedWrite:
5535 {
5536 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5537 tr("Medium '%s' is locked for writing by another task"),
5538 m->strLocationFull.c_str());
5539 break;
5540 }
5541 case MediumState_Inaccessible:
5542 {
5543 /* be in sync with Console::powerUpThread() */
5544 if (!m->strLastAccessError.isEmpty())
5545 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5546 tr("Medium '%s' is not accessible. %s"),
5547 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
5548 else
5549 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5550 tr("Medium '%s' is not accessible"),
5551 m->strLocationFull.c_str());
5552 break;
5553 }
5554 case MediumState_Creating:
5555 {
5556 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5557 tr("Storage for the medium '%s' is being created"),
5558 m->strLocationFull.c_str());
5559 break;
5560 }
5561 case MediumState_Deleting:
5562 {
5563 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5564 tr("Storage for the medium '%s' is being deleted"),
5565 m->strLocationFull.c_str());
5566 break;
5567 }
5568 default:
5569 {
5570 AssertFailed();
5571 break;
5572 }
5573 }
5574
5575 return rc;
5576}
5577
5578/**
5579 * Sets the value of m->strLocationFull. The given location must be a fully
5580 * qualified path; relative paths are not supported here.
5581 *
5582 * As a special exception, if the specified location is a file path that ends with '/'
5583 * then the file name part will be generated by this method automatically in the format
5584 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
5585 * and assign to this medium, and <ext> is the default extension for this
5586 * medium's storage format. Note that this procedure requires the media state to
5587 * be NotCreated and will return a failure otherwise.
5588 *
5589 * @param aLocation Location of the storage unit. If the location is a FS-path,
5590 * then it can be relative to the VirtualBox home directory.
5591 * @param aFormat Optional fallback format if it is an import and the format
5592 * cannot be determined.
5593 *
5594 * @note Must be called from under this object's write lock.
5595 */
5596HRESULT Medium::setLocation(const Utf8Str &aLocation,
5597 const Utf8Str &aFormat /* = Utf8Str::Empty */)
5598{
5599 AssertReturn(!aLocation.isEmpty(), E_FAIL);
5600
5601 AutoCaller autoCaller(this);
5602 AssertComRCReturnRC(autoCaller.rc());
5603
5604 /* formatObj may be null only when initializing from an existing path and
5605 * no format is known yet */
5606 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
5607 || ( autoCaller.state() == InInit
5608 && m->state != MediumState_NotCreated
5609 && m->id.isEmpty()
5610 && m->strFormat.isEmpty()
5611 && m->formatObj.isNull()),
5612 E_FAIL);
5613
5614 /* are we dealing with a new medium constructed using the existing
5615 * location? */
5616 bool isImport = m->strFormat.isEmpty();
5617
5618 if ( isImport
5619 || ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
5620 && !m->hostDrive))
5621 {
5622 Guid id;
5623
5624 Utf8Str locationFull(aLocation);
5625
5626 if (m->state == MediumState_NotCreated)
5627 {
5628 /* must be a file (formatObj must be already known) */
5629 Assert(m->formatObj->getCapabilities() & MediumFormatCapabilities_File);
5630
5631 if (RTPathFilename(aLocation.c_str()) == NULL)
5632 {
5633 /* no file name is given (either an empty string or ends with a
5634 * slash), generate a new UUID + file name if the state allows
5635 * this */
5636
5637 ComAssertMsgRet(!m->formatObj->getFileExtensions().empty(),
5638 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
5639 E_FAIL);
5640
5641 Utf8Str strExt = m->formatObj->getFileExtensions().front();
5642 ComAssertMsgRet(!strExt.isEmpty(),
5643 ("Default extension must not be empty\n"),
5644 E_FAIL);
5645
5646 id.create();
5647
5648 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
5649 aLocation.c_str(), id.raw(), strExt.c_str());
5650 }
5651 }
5652
5653 // we must always have full paths now
5654 if (!RTPathStartsWithRoot(locationFull.c_str()))
5655 return setError(VBOX_E_FILE_ERROR,
5656 tr("The given path '%s' is not fully qualified"),
5657 locationFull.c_str());
5658
5659 /* detect the backend from the storage unit if importing */
5660 if (isImport)
5661 {
5662 VDTYPE enmType = VDTYPE_INVALID;
5663 char *backendName = NULL;
5664
5665 int vrc = VINF_SUCCESS;
5666
5667 /* is it a file? */
5668 {
5669 RTFILE file;
5670 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
5671 if (RT_SUCCESS(vrc))
5672 RTFileClose(file);
5673 }
5674 if (RT_SUCCESS(vrc))
5675 {
5676 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
5677 locationFull.c_str(), &backendName, &enmType);
5678 }
5679 else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
5680 {
5681 /* assume it's not a file, restore the original location */
5682 locationFull = aLocation;
5683 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
5684 locationFull.c_str(), &backendName, &enmType);
5685 }
5686
5687 if (RT_FAILURE(vrc))
5688 {
5689 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
5690 return setError(VBOX_E_FILE_ERROR,
5691 tr("Could not find file for the medium '%s' (%Rrc)"),
5692 locationFull.c_str(), vrc);
5693 else if (aFormat.isEmpty())
5694 return setError(VBOX_E_IPRT_ERROR,
5695 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
5696 locationFull.c_str(), vrc);
5697 else
5698 {
5699 HRESULT rc = setFormat(aFormat);
5700 /* setFormat() must not fail since we've just used the backend so
5701 * the format object must be there */
5702 AssertComRCReturnRC(rc);
5703 }
5704 }
5705 else if ( enmType == VDTYPE_INVALID
5706 || m->devType != convertToDeviceType(enmType))
5707 {
5708 /*
5709 * The user tried to use a image as a device which is not supported
5710 * by the backend.
5711 */
5712 return setError(E_FAIL,
5713 tr("The medium '%s' can't be used as the requested device type"),
5714 locationFull.c_str());
5715 }
5716 else
5717 {
5718 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
5719
5720 HRESULT rc = setFormat(backendName);
5721 RTStrFree(backendName);
5722
5723 /* setFormat() must not fail since we've just used the backend so
5724 * the format object must be there */
5725 AssertComRCReturnRC(rc);
5726 }
5727 }
5728
5729 m->strLocationFull = locationFull;
5730
5731 /* is it still a file? */
5732 if ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
5733 && (m->state == MediumState_NotCreated)
5734 )
5735 /* assign a new UUID (this UUID will be used when calling
5736 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
5737 * also do that if we didn't generate it to make sure it is
5738 * either generated by us or reset to null */
5739 unconst(m->id) = id;
5740 }
5741 else
5742 m->strLocationFull = aLocation;
5743
5744 return S_OK;
5745}
5746
5747/**
5748 * Checks that the format ID is valid and sets it on success.
5749 *
5750 * Note that this method will caller-reference the format object on success!
5751 * This reference must be released somewhere to let the MediumFormat object be
5752 * uninitialized.
5753 *
5754 * @note Must be called from under this object's write lock.
5755 */
5756HRESULT Medium::setFormat(const Utf8Str &aFormat)
5757{
5758 /* get the format object first */
5759 {
5760 SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
5761 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
5762
5763 unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
5764 if (m->formatObj.isNull())
5765 return setError(E_INVALIDARG,
5766 tr("Invalid medium storage format '%s'"),
5767 aFormat.c_str());
5768
5769 /* reference the format permanently to prevent its unexpected
5770 * uninitialization */
5771 HRESULT rc = m->formatObj->addCaller();
5772 AssertComRCReturnRC(rc);
5773
5774 /* get properties (preinsert them as keys in the map). Note that the
5775 * map doesn't grow over the object life time since the set of
5776 * properties is meant to be constant. */
5777
5778 Assert(m->mapProperties.empty());
5779
5780 for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
5781 it != m->formatObj->getProperties().end();
5782 ++it)
5783 {
5784 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
5785 }
5786 }
5787
5788 unconst(m->strFormat) = aFormat;
5789
5790 return S_OK;
5791}
5792
5793/**
5794 * Converts the Medium device type to the VD type.
5795 */
5796VDTYPE Medium::convertDeviceType()
5797{
5798 VDTYPE enmType;
5799
5800 switch (m->devType)
5801 {
5802 case DeviceType_HardDisk:
5803 enmType = VDTYPE_HDD;
5804 break;
5805 case DeviceType_DVD:
5806 enmType = VDTYPE_DVD;
5807 break;
5808 case DeviceType_Floppy:
5809 enmType = VDTYPE_FLOPPY;
5810 break;
5811 default:
5812 ComAssertFailedRet(VDTYPE_INVALID);
5813 }
5814
5815 return enmType;
5816}
5817
5818/**
5819 * Converts from the VD type to the medium type.
5820 */
5821DeviceType_T Medium::convertToDeviceType(VDTYPE enmType)
5822{
5823 DeviceType_T devType;
5824
5825 switch (enmType)
5826 {
5827 case VDTYPE_HDD:
5828 devType = DeviceType_HardDisk;
5829 break;
5830 case VDTYPE_DVD:
5831 devType = DeviceType_DVD;
5832 break;
5833 case VDTYPE_FLOPPY:
5834 devType = DeviceType_Floppy;
5835 break;
5836 default:
5837 ComAssertFailedRet(DeviceType_Null);
5838 }
5839
5840 return devType;
5841}
5842
5843/**
5844 * Returns the last error message collected by the vdErrorCall callback and
5845 * resets it.
5846 *
5847 * The error message is returned prepended with a dot and a space, like this:
5848 * <code>
5849 * ". <error_text> (%Rrc)"
5850 * </code>
5851 * to make it easily appendable to a more general error message. The @c %Rrc
5852 * format string is given @a aVRC as an argument.
5853 *
5854 * If there is no last error message collected by vdErrorCall or if it is a
5855 * null or empty string, then this function returns the following text:
5856 * <code>
5857 * " (%Rrc)"
5858 * </code>
5859 *
5860 * @note Doesn't do any object locking; it is assumed that the caller makes sure
5861 * the callback isn't called by more than one thread at a time.
5862 *
5863 * @param aVRC VBox error code to use when no error message is provided.
5864 */
5865Utf8Str Medium::vdError(int aVRC)
5866{
5867 Utf8Str error;
5868
5869 if (m->vdError.isEmpty())
5870 error = Utf8StrFmt(" (%Rrc)", aVRC);
5871 else
5872 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
5873
5874 m->vdError.setNull();
5875
5876 return error;
5877}
5878
5879/**
5880 * Error message callback.
5881 *
5882 * Puts the reported error message to the m->vdError field.
5883 *
5884 * @note Doesn't do any object locking; it is assumed that the caller makes sure
5885 * the callback isn't called by more than one thread at a time.
5886 *
5887 * @param pvUser The opaque data passed on container creation.
5888 * @param rc The VBox error code.
5889 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
5890 * @param pszFormat Error message format string.
5891 * @param va Error message arguments.
5892 */
5893/*static*/
5894DECLCALLBACK(void) Medium::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
5895 const char *pszFormat, va_list va)
5896{
5897 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
5898
5899 Medium *that = static_cast<Medium*>(pvUser);
5900 AssertReturnVoid(that != NULL);
5901
5902 if (that->m->vdError.isEmpty())
5903 that->m->vdError =
5904 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
5905 else
5906 that->m->vdError =
5907 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
5908 Utf8Str(pszFormat, va).c_str(), rc);
5909}
5910
5911/* static */
5912DECLCALLBACK(bool) Medium::vdConfigAreKeysValid(void *pvUser,
5913 const char * /* pszzValid */)
5914{
5915 Medium *that = static_cast<Medium*>(pvUser);
5916 AssertReturn(that != NULL, false);
5917
5918 /* we always return true since the only keys we have are those found in
5919 * VDBACKENDINFO */
5920 return true;
5921}
5922
5923/* static */
5924DECLCALLBACK(int) Medium::vdConfigQuerySize(void *pvUser,
5925 const char *pszName,
5926 size_t *pcbValue)
5927{
5928 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
5929
5930 Medium *that = static_cast<Medium*>(pvUser);
5931 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
5932
5933 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
5934 if (it == that->m->mapProperties.end())
5935 return VERR_CFGM_VALUE_NOT_FOUND;
5936
5937 /* we interpret null values as "no value" in Medium */
5938 if (it->second.isEmpty())
5939 return VERR_CFGM_VALUE_NOT_FOUND;
5940
5941 *pcbValue = it->second.length() + 1 /* include terminator */;
5942
5943 return VINF_SUCCESS;
5944}
5945
5946/* static */
5947DECLCALLBACK(int) Medium::vdConfigQuery(void *pvUser,
5948 const char *pszName,
5949 char *pszValue,
5950 size_t cchValue)
5951{
5952 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
5953
5954 Medium *that = static_cast<Medium*>(pvUser);
5955 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
5956
5957 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
5958 if (it == that->m->mapProperties.end())
5959 return VERR_CFGM_VALUE_NOT_FOUND;
5960
5961 /* we interpret null values as "no value" in Medium */
5962 if (it->second.isEmpty())
5963 return VERR_CFGM_VALUE_NOT_FOUND;
5964
5965 const Utf8Str &value = it->second;
5966 if (value.length() >= cchValue)
5967 return VERR_CFGM_NOT_ENOUGH_SPACE;
5968
5969 memcpy(pszValue, value.c_str(), value.length() + 1);
5970
5971 return VINF_SUCCESS;
5972}
5973
5974DECLCALLBACK(int) Medium::vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
5975{
5976 PVDSOCKETINT pSocketInt = NULL;
5977
5978 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
5979 return VERR_NOT_SUPPORTED;
5980
5981 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
5982 if (!pSocketInt)
5983 return VERR_NO_MEMORY;
5984
5985 pSocketInt->hSocket = NIL_RTSOCKET;
5986 *pSock = pSocketInt;
5987 return VINF_SUCCESS;
5988}
5989
5990DECLCALLBACK(int) Medium::vdTcpSocketDestroy(VDSOCKET Sock)
5991{
5992 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
5993
5994 if (pSocketInt->hSocket != NIL_RTSOCKET)
5995 RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
5996
5997 RTMemFree(pSocketInt);
5998
5999 return VINF_SUCCESS;
6000}
6001
6002DECLCALLBACK(int) Medium::vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
6003{
6004 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6005
6006 return RTTcpClientConnect(pszAddress, uPort, &pSocketInt->hSocket);
6007}
6008
6009DECLCALLBACK(int) Medium::vdTcpClientClose(VDSOCKET Sock)
6010{
6011 int rc = VINF_SUCCESS;
6012 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6013
6014 rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
6015 pSocketInt->hSocket = NIL_RTSOCKET;
6016 return rc;
6017}
6018
6019DECLCALLBACK(bool) Medium::vdTcpIsClientConnected(VDSOCKET Sock)
6020{
6021 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6022 return pSocketInt->hSocket != NIL_RTSOCKET;
6023}
6024
6025DECLCALLBACK(int) Medium::vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
6026{
6027 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6028 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
6029}
6030
6031DECLCALLBACK(int) Medium::vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
6032{
6033 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6034 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
6035}
6036
6037DECLCALLBACK(int) Medium::vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
6038{
6039 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6040 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
6041}
6042
6043DECLCALLBACK(int) Medium::vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
6044{
6045 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6046 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
6047}
6048
6049DECLCALLBACK(int) Medium::vdTcpFlush(VDSOCKET Sock)
6050{
6051 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6052 return RTTcpFlush(pSocketInt->hSocket);
6053}
6054
6055DECLCALLBACK(int) Medium::vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
6056{
6057 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6058 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
6059}
6060
6061DECLCALLBACK(int) Medium::vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
6062{
6063 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6064 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
6065}
6066
6067DECLCALLBACK(int) Medium::vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
6068{
6069 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6070 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
6071}
6072
6073/**
6074 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
6075 *
6076 * @note When the task is executed by this method, IProgress::notifyComplete()
6077 * is automatically called for the progress object associated with this
6078 * task when the task is finished to signal the operation completion for
6079 * other threads asynchronously waiting for it.
6080 */
6081HRESULT Medium::startThread(Medium::Task *pTask)
6082{
6083#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
6084 /* Extreme paranoia: The calling thread should not hold the medium
6085 * tree lock or any medium lock. Since there is no separate lock class
6086 * for medium objects be even more strict: no other object locks. */
6087 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
6088 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
6089#endif
6090
6091 /// @todo use a more descriptive task name
6092 int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
6093 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
6094 "Medium::Task");
6095 if (RT_FAILURE(vrc))
6096 {
6097 delete pTask;
6098 return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n", vrc);
6099 }
6100
6101 return S_OK;
6102}
6103
6104/**
6105 * Runs Medium::Task::handler() on the current thread instead of creating
6106 * a new one.
6107 *
6108 * This call implies that it is made on another temporary thread created for
6109 * some asynchronous task. Avoid calling it from a normal thread since the task
6110 * operations are potentially lengthy and will block the calling thread in this
6111 * case.
6112 *
6113 * @note When the task is executed by this method, IProgress::notifyComplete()
6114 * is not called for the progress object associated with this task when
6115 * the task is finished. Instead, the result of the operation is returned
6116 * by this method directly and it's the caller's responsibility to
6117 * complete the progress object in this case.
6118 */
6119HRESULT Medium::runNow(Medium::Task *pTask,
6120 GuidList *pllRegistriesThatNeedSaving)
6121{
6122#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
6123 /* Extreme paranoia: The calling thread should not hold the medium
6124 * tree lock or any medium lock. Since there is no separate lock class
6125 * for medium objects be even more strict: no other object locks. */
6126 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
6127 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
6128#endif
6129
6130 pTask->m_pllRegistriesThatNeedSaving = pllRegistriesThatNeedSaving;
6131
6132 /* NIL_RTTHREAD indicates synchronous call. */
6133 return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
6134}
6135
6136/**
6137 * Implementation code for the "create base" task.
6138 *
6139 * This only gets started from Medium::CreateBaseStorage() and always runs
6140 * asynchronously. As a result, we always save the VirtualBox.xml file when
6141 * we're done here.
6142 *
6143 * @param task
6144 * @return
6145 */
6146HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
6147{
6148 HRESULT rc = S_OK;
6149
6150 /* these parameters we need after creation */
6151 uint64_t size = 0, logicalSize = 0;
6152 MediumVariant_T variant = MediumVariant_Standard;
6153 bool fGenerateUuid = false;
6154
6155 try
6156 {
6157 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6158
6159 /* The object may request a specific UUID (through a special form of
6160 * the setLocation() argument). Otherwise we have to generate it */
6161 Guid id = m->id;
6162 fGenerateUuid = id.isEmpty();
6163 if (fGenerateUuid)
6164 {
6165 id.create();
6166 /* VirtualBox::registerHardDisk() will need UUID */
6167 unconst(m->id) = id;
6168 }
6169
6170 Utf8Str format(m->strFormat);
6171 Utf8Str location(m->strLocationFull);
6172 uint64_t capabilities = m->formatObj->getCapabilities();
6173 ComAssertThrow(capabilities & ( VD_CAP_CREATE_FIXED
6174 | VD_CAP_CREATE_DYNAMIC), E_FAIL);
6175 Assert(m->state == MediumState_Creating);
6176
6177 PVBOXHDD hdd;
6178 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6179 ComAssertRCThrow(vrc, E_FAIL);
6180
6181 /* unlock before the potentially lengthy operation */
6182 thisLock.release();
6183
6184 try
6185 {
6186 /* ensure the directory exists */
6187 rc = VirtualBox::ensureFilePathExists(location);
6188 if (FAILED(rc))
6189 throw rc;
6190
6191 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
6192
6193 vrc = VDCreateBase(hdd,
6194 format.c_str(),
6195 location.c_str(),
6196 task.mSize,
6197 task.mVariant,
6198 NULL,
6199 &geo,
6200 &geo,
6201 id.raw(),
6202 VD_OPEN_FLAGS_NORMAL,
6203 m->vdImageIfaces,
6204 task.mVDOperationIfaces);
6205 if (RT_FAILURE(vrc))
6206 throw setError(VBOX_E_FILE_ERROR,
6207 tr("Could not create the medium storage unit '%s'%s"),
6208 location.c_str(), vdError(vrc).c_str());
6209
6210 size = VDGetFileSize(hdd, 0);
6211 logicalSize = VDGetSize(hdd, 0);
6212 unsigned uImageFlags;
6213 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6214 if (RT_SUCCESS(vrc))
6215 variant = (MediumVariant_T)uImageFlags;
6216 }
6217 catch (HRESULT aRC) { rc = aRC; }
6218
6219 VDDestroy(hdd);
6220 }
6221 catch (HRESULT aRC) { rc = aRC; }
6222
6223 if (SUCCEEDED(rc))
6224 {
6225 /* register with mVirtualBox as the last step and move to
6226 * Created state only on success (leaving an orphan file is
6227 * better than breaking media registry consistency) */
6228 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6229 rc = m->pVirtualBox->registerHardDisk(this, NULL /* pllRegistriesThatNeedSaving */);
6230 }
6231
6232 // reenter the lock before changing state
6233 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6234
6235 if (SUCCEEDED(rc))
6236 {
6237 m->state = MediumState_Created;
6238
6239 m->size = size;
6240 m->logicalSize = logicalSize;
6241 m->variant = variant;
6242 }
6243 else
6244 {
6245 /* back to NotCreated on failure */
6246 m->state = MediumState_NotCreated;
6247
6248 /* reset UUID to prevent it from being reused next time */
6249 if (fGenerateUuid)
6250 unconst(m->id).clear();
6251 }
6252
6253 return rc;
6254}
6255
6256/**
6257 * Implementation code for the "create diff" task.
6258 *
6259 * This task always gets started from Medium::createDiffStorage() and can run
6260 * synchronously or asynchronously depending on the "wait" parameter passed to
6261 * that function. If we run synchronously, the caller expects the bool
6262 * *pfNeedsGlobalSaveSettings to be set before returning; otherwise (in asynchronous
6263 * mode), we save the settings ourselves.
6264 *
6265 * @param task
6266 * @return
6267 */
6268HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
6269{
6270 HRESULT rc = S_OK;
6271
6272 const ComObjPtr<Medium> &pTarget = task.mTarget;
6273
6274 uint64_t size = 0, logicalSize = 0;
6275 MediumVariant_T variant = MediumVariant_Standard;
6276 bool fGenerateUuid = false;
6277
6278 GuidList llRegistriesThatNeedSaving; // gets copied to task pointer later in synchronous mode
6279
6280 try
6281 {
6282 /* Lock both in {parent,child} order. */
6283 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6284
6285 /* The object may request a specific UUID (through a special form of
6286 * the setLocation() argument). Otherwise we have to generate it */
6287 Guid targetId = pTarget->m->id;
6288 fGenerateUuid = targetId.isEmpty();
6289 if (fGenerateUuid)
6290 {
6291 targetId.create();
6292 /* VirtualBox::registerHardDisk() will need UUID */
6293 unconst(pTarget->m->id) = targetId;
6294 }
6295
6296 Guid id = m->id;
6297
6298 Utf8Str targetFormat(pTarget->m->strFormat);
6299 Utf8Str targetLocation(pTarget->m->strLocationFull);
6300 uint64_t capabilities = pTarget->m->formatObj->getCapabilities();
6301 ComAssertThrow(capabilities & VD_CAP_CREATE_DYNAMIC, E_FAIL);
6302
6303 Assert(pTarget->m->state == MediumState_Creating);
6304 Assert(m->state == MediumState_LockedRead);
6305
6306 PVBOXHDD hdd;
6307 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6308 ComAssertRCThrow(vrc, E_FAIL);
6309
6310 /* the two media are now protected by their non-default states;
6311 * unlock the media before the potentially lengthy operation */
6312 mediaLock.release();
6313
6314 try
6315 {
6316 /* Open all media in the target chain but the last. */
6317 MediumLockList::Base::const_iterator targetListBegin =
6318 task.mpMediumLockList->GetBegin();
6319 MediumLockList::Base::const_iterator targetListEnd =
6320 task.mpMediumLockList->GetEnd();
6321 for (MediumLockList::Base::const_iterator it = targetListBegin;
6322 it != targetListEnd;
6323 ++it)
6324 {
6325 const MediumLock &mediumLock = *it;
6326 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6327
6328 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6329
6330 /* Skip over the target diff medium */
6331 if (pMedium->m->state == MediumState_Creating)
6332 continue;
6333
6334 /* sanity check */
6335 Assert(pMedium->m->state == MediumState_LockedRead);
6336
6337 /* Open all media in appropriate mode. */
6338 vrc = VDOpen(hdd,
6339 pMedium->m->strFormat.c_str(),
6340 pMedium->m->strLocationFull.c_str(),
6341 VD_OPEN_FLAGS_READONLY,
6342 pMedium->m->vdImageIfaces);
6343 if (RT_FAILURE(vrc))
6344 throw setError(VBOX_E_FILE_ERROR,
6345 tr("Could not open the medium storage unit '%s'%s"),
6346 pMedium->m->strLocationFull.c_str(),
6347 vdError(vrc).c_str());
6348 }
6349
6350 /* ensure the target directory exists */
6351 rc = VirtualBox::ensureFilePathExists(targetLocation);
6352 if (FAILED(rc))
6353 throw rc;
6354
6355 vrc = VDCreateDiff(hdd,
6356 targetFormat.c_str(),
6357 targetLocation.c_str(),
6358 task.mVariant | VD_IMAGE_FLAGS_DIFF,
6359 NULL,
6360 targetId.raw(),
6361 id.raw(),
6362 VD_OPEN_FLAGS_NORMAL,
6363 pTarget->m->vdImageIfaces,
6364 task.mVDOperationIfaces);
6365 if (RT_FAILURE(vrc))
6366 throw setError(VBOX_E_FILE_ERROR,
6367 tr("Could not create the differencing medium storage unit '%s'%s"),
6368 targetLocation.c_str(), vdError(vrc).c_str());
6369
6370 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
6371 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
6372 unsigned uImageFlags;
6373 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6374 if (RT_SUCCESS(vrc))
6375 variant = (MediumVariant_T)uImageFlags;
6376 }
6377 catch (HRESULT aRC) { rc = aRC; }
6378
6379 VDDestroy(hdd);
6380 }
6381 catch (HRESULT aRC) { rc = aRC; }
6382
6383 if (SUCCEEDED(rc))
6384 {
6385 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6386
6387 Assert(pTarget->m->pParent.isNull());
6388
6389 /* associate the child with the parent */
6390 pTarget->m->pParent = this;
6391 m->llChildren.push_back(pTarget);
6392
6393 /** @todo r=klaus neither target nor base() are locked,
6394 * potential race! */
6395 /* diffs for immutable media are auto-reset by default */
6396 pTarget->m->autoReset = (getBase()->m->type == MediumType_Immutable);
6397
6398 /* register with mVirtualBox as the last step and move to
6399 * Created state only on success (leaving an orphan file is
6400 * better than breaking media registry consistency) */
6401 rc = m->pVirtualBox->registerHardDisk(pTarget, &llRegistriesThatNeedSaving);
6402
6403 if (FAILED(rc))
6404 /* break the parent association on failure to register */
6405 deparent();
6406 }
6407
6408 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6409
6410 if (SUCCEEDED(rc))
6411 {
6412 pTarget->m->state = MediumState_Created;
6413
6414 pTarget->m->size = size;
6415 pTarget->m->logicalSize = logicalSize;
6416 pTarget->m->variant = variant;
6417 }
6418 else
6419 {
6420 /* back to NotCreated on failure */
6421 pTarget->m->state = MediumState_NotCreated;
6422
6423 pTarget->m->autoReset = false;
6424
6425 /* reset UUID to prevent it from being reused next time */
6426 if (fGenerateUuid)
6427 unconst(pTarget->m->id).clear();
6428 }
6429
6430 // deregister the task registered in createDiffStorage()
6431 Assert(m->numCreateDiffTasks != 0);
6432 --m->numCreateDiffTasks;
6433
6434 if (task.isAsync())
6435 {
6436 mediaLock.release();
6437 m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
6438 }
6439 else
6440 // synchronous mode: report save settings result to caller
6441 if (task.m_pllRegistriesThatNeedSaving)
6442 *task.m_pllRegistriesThatNeedSaving = llRegistriesThatNeedSaving;
6443
6444 /* Note that in sync mode, it's the caller's responsibility to
6445 * unlock the medium. */
6446
6447 return rc;
6448}
6449
6450/**
6451 * Implementation code for the "merge" task.
6452 *
6453 * This task always gets started from Medium::mergeTo() and can run
6454 * synchronously or asynchronously depending on the "wait" parameter passed to
6455 * that function. If we run synchronously, the caller expects the bool
6456 * *pfNeedsGlobalSaveSettings to be set before returning; otherwise (in asynchronous
6457 * mode), we save the settings ourselves.
6458 *
6459 * @param task
6460 * @return
6461 */
6462HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
6463{
6464 HRESULT rc = S_OK;
6465
6466 const ComObjPtr<Medium> &pTarget = task.mTarget;
6467
6468 try
6469 {
6470 PVBOXHDD hdd;
6471 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6472 ComAssertRCThrow(vrc, E_FAIL);
6473
6474 try
6475 {
6476 // Similar code appears in SessionMachine::onlineMergeMedium, so
6477 // if you make any changes below check whether they are applicable
6478 // in that context as well.
6479
6480 unsigned uTargetIdx = VD_LAST_IMAGE;
6481 unsigned uSourceIdx = VD_LAST_IMAGE;
6482 /* Open all media in the chain. */
6483 MediumLockList::Base::iterator lockListBegin =
6484 task.mpMediumLockList->GetBegin();
6485 MediumLockList::Base::iterator lockListEnd =
6486 task.mpMediumLockList->GetEnd();
6487 unsigned i = 0;
6488 for (MediumLockList::Base::iterator it = lockListBegin;
6489 it != lockListEnd;
6490 ++it)
6491 {
6492 MediumLock &mediumLock = *it;
6493 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6494
6495 if (pMedium == this)
6496 uSourceIdx = i;
6497 else if (pMedium == pTarget)
6498 uTargetIdx = i;
6499
6500 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6501
6502 /*
6503 * complex sanity (sane complexity)
6504 *
6505 * The current medium must be in the Deleting (medium is merged)
6506 * or LockedRead (parent medium) state if it is not the target.
6507 * If it is the target it must be in the LockedWrite state.
6508 */
6509 Assert( ( pMedium != pTarget
6510 && ( pMedium->m->state == MediumState_Deleting
6511 || pMedium->m->state == MediumState_LockedRead))
6512 || ( pMedium == pTarget
6513 && pMedium->m->state == MediumState_LockedWrite));
6514
6515 /*
6516 * Medium must be the target, in the LockedRead state
6517 * or Deleting state where it is not allowed to be attached
6518 * to a virtual machine.
6519 */
6520 Assert( pMedium == pTarget
6521 || pMedium->m->state == MediumState_LockedRead
6522 || ( pMedium->m->backRefs.size() == 0
6523 && pMedium->m->state == MediumState_Deleting));
6524 /* The source medium must be in Deleting state. */
6525 Assert( pMedium != this
6526 || pMedium->m->state == MediumState_Deleting);
6527
6528 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
6529
6530 if ( pMedium->m->state == MediumState_LockedRead
6531 || pMedium->m->state == MediumState_Deleting)
6532 uOpenFlags = VD_OPEN_FLAGS_READONLY;
6533 if (pMedium->m->type == MediumType_Shareable)
6534 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6535
6536 /* Open the medium */
6537 vrc = VDOpen(hdd,
6538 pMedium->m->strFormat.c_str(),
6539 pMedium->m->strLocationFull.c_str(),
6540 uOpenFlags,
6541 pMedium->m->vdImageIfaces);
6542 if (RT_FAILURE(vrc))
6543 throw vrc;
6544
6545 i++;
6546 }
6547
6548 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
6549 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
6550
6551 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
6552 task.mVDOperationIfaces);
6553 if (RT_FAILURE(vrc))
6554 throw vrc;
6555
6556 /* update parent UUIDs */
6557 if (!task.mfMergeForward)
6558 {
6559 /* we need to update UUIDs of all source's children
6560 * which cannot be part of the container at once so
6561 * add each one in there individually */
6562 if (task.mChildrenToReparent.size() > 0)
6563 {
6564 for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
6565 it != task.mChildrenToReparent.end();
6566 ++it)
6567 {
6568 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
6569 vrc = VDOpen(hdd,
6570 (*it)->m->strFormat.c_str(),
6571 (*it)->m->strLocationFull.c_str(),
6572 VD_OPEN_FLAGS_INFO,
6573 (*it)->m->vdImageIfaces);
6574 if (RT_FAILURE(vrc))
6575 throw vrc;
6576
6577 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
6578 pTarget->m->id.raw());
6579 if (RT_FAILURE(vrc))
6580 throw vrc;
6581
6582 vrc = VDClose(hdd, false /* fDelete */);
6583 if (RT_FAILURE(vrc))
6584 throw vrc;
6585
6586 (*it)->UnlockWrite(NULL);
6587 }
6588 }
6589 }
6590 }
6591 catch (HRESULT aRC) { rc = aRC; }
6592 catch (int aVRC)
6593 {
6594 throw setError(VBOX_E_FILE_ERROR,
6595 tr("Could not merge the medium '%s' to '%s'%s"),
6596 m->strLocationFull.c_str(),
6597 pTarget->m->strLocationFull.c_str(),
6598 vdError(aVRC).c_str());
6599 }
6600
6601 VDDestroy(hdd);
6602 }
6603 catch (HRESULT aRC) { rc = aRC; }
6604
6605 HRESULT rc2;
6606
6607 if (SUCCEEDED(rc))
6608 {
6609 /* all media but the target were successfully deleted by
6610 * VDMerge; reparent the last one and uninitialize deleted media. */
6611
6612 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6613
6614 if (task.mfMergeForward)
6615 {
6616 /* first, unregister the target since it may become a base
6617 * medium which needs re-registration */
6618 rc2 = m->pVirtualBox->unregisterHardDisk(pTarget, NULL /*&fNeedsGlobalSaveSettings*/);
6619 AssertComRC(rc2);
6620
6621 /* then, reparent it and disconnect the deleted branch at
6622 * both ends (chain->parent() is source's parent) */
6623 pTarget->deparent();
6624 pTarget->m->pParent = task.mParentForTarget;
6625 if (pTarget->m->pParent)
6626 {
6627 pTarget->m->pParent->m->llChildren.push_back(pTarget);
6628 deparent();
6629 }
6630
6631 /* then, register again */
6632 rc2 = m->pVirtualBox->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */ );
6633 AssertComRC(rc2);
6634 }
6635 else
6636 {
6637 Assert(pTarget->getChildren().size() == 1);
6638 Medium *targetChild = pTarget->getChildren().front();
6639
6640 /* disconnect the deleted branch at the elder end */
6641 targetChild->deparent();
6642
6643 /* reparent source's children and disconnect the deleted
6644 * branch at the younger end */
6645 if (task.mChildrenToReparent.size() > 0)
6646 {
6647 /* obey {parent,child} lock order */
6648 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
6649
6650 for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
6651 it != task.mChildrenToReparent.end();
6652 it++)
6653 {
6654 Medium *pMedium = *it;
6655 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
6656
6657 pMedium->deparent(); // removes pMedium from source
6658 pMedium->setParent(pTarget);
6659 }
6660 }
6661 }
6662
6663 /* unregister and uninitialize all media removed by the merge */
6664 MediumLockList::Base::iterator lockListBegin =
6665 task.mpMediumLockList->GetBegin();
6666 MediumLockList::Base::iterator lockListEnd =
6667 task.mpMediumLockList->GetEnd();
6668 for (MediumLockList::Base::iterator it = lockListBegin;
6669 it != lockListEnd;
6670 )
6671 {
6672 MediumLock &mediumLock = *it;
6673 /* Create a real copy of the medium pointer, as the medium
6674 * lock deletion below would invalidate the referenced object. */
6675 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
6676
6677 /* The target and all media not merged (readonly) are skipped */
6678 if ( pMedium == pTarget
6679 || pMedium->m->state == MediumState_LockedRead)
6680 {
6681 ++it;
6682 continue;
6683 }
6684
6685 rc2 = pMedium->m->pVirtualBox->unregisterHardDisk(pMedium,
6686 NULL /*pfNeedsGlobalSaveSettings*/);
6687 AssertComRC(rc2);
6688
6689 /* now, uninitialize the deleted medium (note that
6690 * due to the Deleting state, uninit() will not touch
6691 * the parent-child relationship so we need to
6692 * uninitialize each disk individually) */
6693
6694 /* note that the operation initiator medium (which is
6695 * normally also the source medium) is a special case
6696 * -- there is one more caller added by Task to it which
6697 * we must release. Also, if we are in sync mode, the
6698 * caller may still hold an AutoCaller instance for it
6699 * and therefore we cannot uninit() it (it's therefore
6700 * the caller's responsibility) */
6701 if (pMedium == this)
6702 {
6703 Assert(getChildren().size() == 0);
6704 Assert(m->backRefs.size() == 0);
6705 task.mMediumCaller.release();
6706 }
6707
6708 /* Delete the medium lock list entry, which also releases the
6709 * caller added by MergeChain before uninit() and updates the
6710 * iterator to point to the right place. */
6711 rc2 = task.mpMediumLockList->RemoveByIterator(it);
6712 AssertComRC(rc2);
6713
6714 if (task.isAsync() || pMedium != this)
6715 pMedium->uninit();
6716 }
6717 }
6718
6719 if (task.isAsync())
6720 {
6721 // in asynchronous mode, save settings now
6722 GuidList llRegistriesThatNeedSaving;
6723 addToRegistryIDList(llRegistriesThatNeedSaving);
6724 rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
6725 }
6726 else
6727 // synchronous mode: report save settings result to caller
6728 if (task.m_pllRegistriesThatNeedSaving)
6729 pTarget->addToRegistryIDList(*task.m_pllRegistriesThatNeedSaving);
6730
6731 if (FAILED(rc))
6732 {
6733 /* Here we come if either VDMerge() failed (in which case we
6734 * assume that it tried to do everything to make a further
6735 * retry possible -- e.g. not deleted intermediate media
6736 * and so on) or VirtualBox::saveSettings() failed (where we
6737 * should have the original tree but with intermediate storage
6738 * units deleted by VDMerge()). We have to only restore states
6739 * (through the MergeChain dtor) unless we are run synchronously
6740 * in which case it's the responsibility of the caller as stated
6741 * in the mergeTo() docs. The latter also implies that we
6742 * don't own the merge chain, so release it in this case. */
6743 if (task.isAsync())
6744 {
6745 Assert(task.mChildrenToReparent.size() == 0);
6746 cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
6747 }
6748 }
6749
6750 return rc;
6751}
6752
6753/**
6754 * Implementation code for the "clone" task.
6755 *
6756 * This only gets started from Medium::CloneTo() and always runs asynchronously.
6757 * As a result, we always save the VirtualBox.xml file when we're done here.
6758 *
6759 * @param task
6760 * @return
6761 */
6762HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
6763{
6764 HRESULT rc = S_OK;
6765
6766 const ComObjPtr<Medium> &pTarget = task.mTarget;
6767 const ComObjPtr<Medium> &pParent = task.mParent;
6768
6769 bool fCreatingTarget = false;
6770
6771 uint64_t size = 0, logicalSize = 0;
6772 MediumVariant_T variant = MediumVariant_Standard;
6773 bool fGenerateUuid = false;
6774
6775 try
6776 {
6777 /* Lock all in {parent,child} order. The lock is also used as a
6778 * signal from the task initiator (which releases it only after
6779 * RTThreadCreate()) that we can start the job. */
6780 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
6781
6782 fCreatingTarget = pTarget->m->state == MediumState_Creating;
6783
6784 /* The object may request a specific UUID (through a special form of
6785 * the setLocation() argument). Otherwise we have to generate it */
6786 Guid targetId = pTarget->m->id;
6787 fGenerateUuid = targetId.isEmpty();
6788 if (fGenerateUuid)
6789 {
6790 targetId.create();
6791 /* VirtualBox::registerHardDisk() will need UUID */
6792 unconst(pTarget->m->id) = targetId;
6793 }
6794
6795 PVBOXHDD hdd;
6796 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
6797 ComAssertRCThrow(vrc, E_FAIL);
6798
6799 try
6800 {
6801 /* Open all media in the source chain. */
6802 MediumLockList::Base::const_iterator sourceListBegin =
6803 task.mpSourceMediumLockList->GetBegin();
6804 MediumLockList::Base::const_iterator sourceListEnd =
6805 task.mpSourceMediumLockList->GetEnd();
6806 for (MediumLockList::Base::const_iterator it = sourceListBegin;
6807 it != sourceListEnd;
6808 ++it)
6809 {
6810 const MediumLock &mediumLock = *it;
6811 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6812 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6813
6814 /* sanity check */
6815 Assert(pMedium->m->state == MediumState_LockedRead);
6816
6817 /** Open all media in read-only mode. */
6818 vrc = VDOpen(hdd,
6819 pMedium->m->strFormat.c_str(),
6820 pMedium->m->strLocationFull.c_str(),
6821 VD_OPEN_FLAGS_READONLY,
6822 pMedium->m->vdImageIfaces);
6823 if (RT_FAILURE(vrc))
6824 throw setError(VBOX_E_FILE_ERROR,
6825 tr("Could not open the medium storage unit '%s'%s"),
6826 pMedium->m->strLocationFull.c_str(),
6827 vdError(vrc).c_str());
6828 }
6829
6830 Utf8Str targetFormat(pTarget->m->strFormat);
6831 Utf8Str targetLocation(pTarget->m->strLocationFull);
6832
6833 Assert( pTarget->m->state == MediumState_Creating
6834 || pTarget->m->state == MediumState_LockedWrite);
6835 Assert(m->state == MediumState_LockedRead);
6836 Assert( pParent.isNull()
6837 || pParent->m->state == MediumState_LockedRead);
6838
6839 /* unlock before the potentially lengthy operation */
6840 thisLock.release();
6841
6842 /* ensure the target directory exists */
6843 rc = VirtualBox::ensureFilePathExists(targetLocation);
6844 if (FAILED(rc))
6845 throw rc;
6846
6847 PVBOXHDD targetHdd;
6848 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
6849 ComAssertRCThrow(vrc, E_FAIL);
6850
6851 try
6852 {
6853 /* Open all media in the target chain. */
6854 MediumLockList::Base::const_iterator targetListBegin =
6855 task.mpTargetMediumLockList->GetBegin();
6856 MediumLockList::Base::const_iterator targetListEnd =
6857 task.mpTargetMediumLockList->GetEnd();
6858 for (MediumLockList::Base::const_iterator it = targetListBegin;
6859 it != targetListEnd;
6860 ++it)
6861 {
6862 const MediumLock &mediumLock = *it;
6863 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6864
6865 /* If the target medium is not created yet there's no
6866 * reason to open it. */
6867 if (pMedium == pTarget && fCreatingTarget)
6868 continue;
6869
6870 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6871
6872 /* sanity check */
6873 Assert( pMedium->m->state == MediumState_LockedRead
6874 || pMedium->m->state == MediumState_LockedWrite);
6875
6876 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
6877 if (pMedium->m->state != MediumState_LockedWrite)
6878 uOpenFlags = VD_OPEN_FLAGS_READONLY;
6879 if (pMedium->m->type == MediumType_Shareable)
6880 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6881
6882 /* Open all media in appropriate mode. */
6883 vrc = VDOpen(targetHdd,
6884 pMedium->m->strFormat.c_str(),
6885 pMedium->m->strLocationFull.c_str(),
6886 uOpenFlags,
6887 pMedium->m->vdImageIfaces);
6888 if (RT_FAILURE(vrc))
6889 throw setError(VBOX_E_FILE_ERROR,
6890 tr("Could not open the medium storage unit '%s'%s"),
6891 pMedium->m->strLocationFull.c_str(),
6892 vdError(vrc).c_str());
6893 }
6894
6895 /** @todo r=klaus target isn't locked, race getting the state */
6896 vrc = VDCopy(hdd,
6897 VD_LAST_IMAGE,
6898 targetHdd,
6899 targetFormat.c_str(),
6900 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
6901 false /* fMoveByRename */,
6902 0 /* cbSize */,
6903 task.mVariant,
6904 targetId.raw(),
6905 VD_OPEN_FLAGS_NORMAL,
6906 NULL /* pVDIfsOperation */,
6907 pTarget->m->vdImageIfaces,
6908 task.mVDOperationIfaces);
6909 if (RT_FAILURE(vrc))
6910 throw setError(VBOX_E_FILE_ERROR,
6911 tr("Could not create the clone medium '%s'%s"),
6912 targetLocation.c_str(), vdError(vrc).c_str());
6913
6914 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
6915 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
6916 unsigned uImageFlags;
6917 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
6918 if (RT_SUCCESS(vrc))
6919 variant = (MediumVariant_T)uImageFlags;
6920 }
6921 catch (HRESULT aRC) { rc = aRC; }
6922
6923 VDDestroy(targetHdd);
6924 }
6925 catch (HRESULT aRC) { rc = aRC; }
6926
6927 VDDestroy(hdd);
6928 }
6929 catch (HRESULT aRC) { rc = aRC; }
6930
6931 /* Only do the parent changes for newly created media. */
6932 if (SUCCEEDED(rc) && fCreatingTarget)
6933 {
6934 /* we set mParent & children() */
6935 AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6936
6937 Assert(pTarget->m->pParent.isNull());
6938
6939 if (pParent)
6940 {
6941 /* associate the clone with the parent and deassociate
6942 * from VirtualBox */
6943 pTarget->m->pParent = pParent;
6944 pParent->m->llChildren.push_back(pTarget);
6945
6946 /* register with mVirtualBox as the last step and move to
6947 * Created state only on success (leaving an orphan file is
6948 * better than breaking media registry consistency) */
6949 rc = pParent->m->pVirtualBox->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */);
6950
6951 if (FAILED(rc))
6952 /* break parent association on failure to register */
6953 pTarget->deparent(); // removes target from parent
6954 }
6955 else
6956 {
6957 /* just register */
6958 rc = m->pVirtualBox->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */);
6959 }
6960 }
6961
6962 if (fCreatingTarget)
6963 {
6964 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
6965
6966 if (SUCCEEDED(rc))
6967 {
6968 pTarget->m->state = MediumState_Created;
6969
6970 pTarget->m->size = size;
6971 pTarget->m->logicalSize = logicalSize;
6972 pTarget->m->variant = variant;
6973 }
6974 else
6975 {
6976 /* back to NotCreated on failure */
6977 pTarget->m->state = MediumState_NotCreated;
6978
6979 /* reset UUID to prevent it from being reused next time */
6980 if (fGenerateUuid)
6981 unconst(pTarget->m->id).clear();
6982 }
6983 }
6984
6985 // now, at the end of this task (always asynchronous), save the settings
6986 {
6987 // save the settings
6988 GuidList llRegistriesThatNeedSaving;
6989 addToRegistryIDList(llRegistriesThatNeedSaving);
6990 rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
6991 }
6992
6993 /* Everything is explicitly unlocked when the task exits,
6994 * as the task destruction also destroys the source chain. */
6995
6996 /* Make sure the source chain is released early. It could happen
6997 * that we get a deadlock in Appliance::Import when Medium::Close
6998 * is called & the source chain is released at the same time. */
6999 task.mpSourceMediumLockList->Clear();
7000
7001 return rc;
7002}
7003
7004/**
7005 * Implementation code for the "delete" task.
7006 *
7007 * This task always gets started from Medium::deleteStorage() and can run
7008 * synchronously or asynchronously depending on the "wait" parameter passed to
7009 * that function.
7010 *
7011 * @param task
7012 * @return
7013 */
7014HRESULT Medium::taskDeleteHandler(Medium::DeleteTask &task)
7015{
7016 NOREF(task);
7017 HRESULT rc = S_OK;
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 PVBOXHDD hdd;
7026 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7027 ComAssertRCThrow(vrc, E_FAIL);
7028
7029 Utf8Str format(m->strFormat);
7030 Utf8Str location(m->strLocationFull);
7031
7032 /* unlock before the potentially lengthy operation */
7033 Assert(m->state == MediumState_Deleting);
7034 thisLock.release();
7035
7036 try
7037 {
7038 vrc = VDOpen(hdd,
7039 format.c_str(),
7040 location.c_str(),
7041 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
7042 m->vdImageIfaces);
7043 if (RT_SUCCESS(vrc))
7044 vrc = VDClose(hdd, true /* fDelete */);
7045
7046 if (RT_FAILURE(vrc))
7047 throw setError(VBOX_E_FILE_ERROR,
7048 tr("Could not delete the medium storage unit '%s'%s"),
7049 location.c_str(), vdError(vrc).c_str());
7050
7051 }
7052 catch (HRESULT aRC) { rc = aRC; }
7053
7054 VDDestroy(hdd);
7055 }
7056 catch (HRESULT aRC) { rc = aRC; }
7057
7058 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7059
7060 /* go to the NotCreated state even on failure since the storage
7061 * may have been already partially deleted and cannot be used any
7062 * more. One will be able to manually re-open the storage if really
7063 * needed to re-register it. */
7064 m->state = MediumState_NotCreated;
7065
7066 /* Reset UUID to prevent Create* from reusing it again */
7067 unconst(m->id).clear();
7068
7069 return rc;
7070}
7071
7072/**
7073 * Implementation code for the "reset" task.
7074 *
7075 * This always gets started asynchronously from Medium::Reset().
7076 *
7077 * @param task
7078 * @return
7079 */
7080HRESULT Medium::taskResetHandler(Medium::ResetTask &task)
7081{
7082 HRESULT rc = S_OK;
7083
7084 uint64_t size = 0, logicalSize = 0;
7085 MediumVariant_T variant = MediumVariant_Standard;
7086
7087 try
7088 {
7089 /* The lock is also used as a signal from the task initiator (which
7090 * releases it only after RTThreadCreate()) that we can start the job */
7091 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7092
7093 /// @todo Below we use a pair of delete/create operations to reset
7094 /// the diff contents but the most efficient way will of course be
7095 /// to add a VDResetDiff() API call
7096
7097 PVBOXHDD hdd;
7098 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7099 ComAssertRCThrow(vrc, E_FAIL);
7100
7101 Guid id = m->id;
7102 Utf8Str format(m->strFormat);
7103 Utf8Str location(m->strLocationFull);
7104
7105 Medium *pParent = m->pParent;
7106 Guid parentId = pParent->m->id;
7107 Utf8Str parentFormat(pParent->m->strFormat);
7108 Utf8Str parentLocation(pParent->m->strLocationFull);
7109
7110 Assert(m->state == MediumState_LockedWrite);
7111
7112 /* unlock before the potentially lengthy operation */
7113 thisLock.release();
7114
7115 try
7116 {
7117 /* Open all media in the target chain but the last. */
7118 MediumLockList::Base::const_iterator targetListBegin =
7119 task.mpMediumLockList->GetBegin();
7120 MediumLockList::Base::const_iterator targetListEnd =
7121 task.mpMediumLockList->GetEnd();
7122 for (MediumLockList::Base::const_iterator it = targetListBegin;
7123 it != targetListEnd;
7124 ++it)
7125 {
7126 const MediumLock &mediumLock = *it;
7127 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7128
7129 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7130
7131 /* sanity check, "this" is checked above */
7132 Assert( pMedium == this
7133 || pMedium->m->state == MediumState_LockedRead);
7134
7135 /* Open all media in appropriate mode. */
7136 vrc = VDOpen(hdd,
7137 pMedium->m->strFormat.c_str(),
7138 pMedium->m->strLocationFull.c_str(),
7139 VD_OPEN_FLAGS_READONLY,
7140 pMedium->m->vdImageIfaces);
7141 if (RT_FAILURE(vrc))
7142 throw setError(VBOX_E_FILE_ERROR,
7143 tr("Could not open the medium storage unit '%s'%s"),
7144 pMedium->m->strLocationFull.c_str(),
7145 vdError(vrc).c_str());
7146
7147 /* Done when we hit the media which should be reset */
7148 if (pMedium == this)
7149 break;
7150 }
7151
7152 /* first, delete the storage unit */
7153 vrc = VDClose(hdd, true /* fDelete */);
7154 if (RT_FAILURE(vrc))
7155 throw setError(VBOX_E_FILE_ERROR,
7156 tr("Could not delete the medium storage unit '%s'%s"),
7157 location.c_str(), vdError(vrc).c_str());
7158
7159 /* next, create it again */
7160 vrc = VDOpen(hdd,
7161 parentFormat.c_str(),
7162 parentLocation.c_str(),
7163 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
7164 m->vdImageIfaces);
7165 if (RT_FAILURE(vrc))
7166 throw setError(VBOX_E_FILE_ERROR,
7167 tr("Could not open the medium storage unit '%s'%s"),
7168 parentLocation.c_str(), vdError(vrc).c_str());
7169
7170 vrc = VDCreateDiff(hdd,
7171 format.c_str(),
7172 location.c_str(),
7173 /// @todo use the same medium variant as before
7174 VD_IMAGE_FLAGS_NONE,
7175 NULL,
7176 id.raw(),
7177 parentId.raw(),
7178 VD_OPEN_FLAGS_NORMAL,
7179 m->vdImageIfaces,
7180 task.mVDOperationIfaces);
7181 if (RT_FAILURE(vrc))
7182 throw setError(VBOX_E_FILE_ERROR,
7183 tr("Could not create the differencing medium storage unit '%s'%s"),
7184 location.c_str(), vdError(vrc).c_str());
7185
7186 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
7187 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
7188 unsigned uImageFlags;
7189 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7190 if (RT_SUCCESS(vrc))
7191 variant = (MediumVariant_T)uImageFlags;
7192 }
7193 catch (HRESULT aRC) { rc = aRC; }
7194
7195 VDDestroy(hdd);
7196 }
7197 catch (HRESULT aRC) { rc = aRC; }
7198
7199 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7200
7201 m->size = size;
7202 m->logicalSize = logicalSize;
7203 m->variant = variant;
7204
7205 if (task.isAsync())
7206 {
7207 /* unlock ourselves when done */
7208 HRESULT rc2 = UnlockWrite(NULL);
7209 AssertComRC(rc2);
7210 }
7211
7212 /* Note that in sync mode, it's the caller's responsibility to
7213 * unlock the medium. */
7214
7215 return rc;
7216}
7217
7218/**
7219 * Implementation code for the "compact" task.
7220 *
7221 * @param task
7222 * @return
7223 */
7224HRESULT Medium::taskCompactHandler(Medium::CompactTask &task)
7225{
7226 HRESULT rc = S_OK;
7227
7228 /* Lock all in {parent,child} order. The lock is also used as a
7229 * signal from the task initiator (which releases it only after
7230 * RTThreadCreate()) that we can start the job. */
7231 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7232
7233 try
7234 {
7235 PVBOXHDD hdd;
7236 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7237 ComAssertRCThrow(vrc, E_FAIL);
7238
7239 try
7240 {
7241 /* Open all media in the chain. */
7242 MediumLockList::Base::const_iterator mediumListBegin =
7243 task.mpMediumLockList->GetBegin();
7244 MediumLockList::Base::const_iterator mediumListEnd =
7245 task.mpMediumLockList->GetEnd();
7246 MediumLockList::Base::const_iterator mediumListLast =
7247 mediumListEnd;
7248 mediumListLast--;
7249 for (MediumLockList::Base::const_iterator it = mediumListBegin;
7250 it != mediumListEnd;
7251 ++it)
7252 {
7253 const MediumLock &mediumLock = *it;
7254 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7255 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7256
7257 /* sanity check */
7258 if (it == mediumListLast)
7259 Assert(pMedium->m->state == MediumState_LockedWrite);
7260 else
7261 Assert(pMedium->m->state == MediumState_LockedRead);
7262
7263 /* Open all media but last in read-only mode. Do not handle
7264 * shareable media, as compaction and sharing are mutually
7265 * exclusive. */
7266 vrc = VDOpen(hdd,
7267 pMedium->m->strFormat.c_str(),
7268 pMedium->m->strLocationFull.c_str(),
7269 (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
7270 pMedium->m->vdImageIfaces);
7271 if (RT_FAILURE(vrc))
7272 throw setError(VBOX_E_FILE_ERROR,
7273 tr("Could not open the medium storage unit '%s'%s"),
7274 pMedium->m->strLocationFull.c_str(),
7275 vdError(vrc).c_str());
7276 }
7277
7278 Assert(m->state == MediumState_LockedWrite);
7279
7280 Utf8Str location(m->strLocationFull);
7281
7282 /* unlock before the potentially lengthy operation */
7283 thisLock.release();
7284
7285 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
7286 if (RT_FAILURE(vrc))
7287 {
7288 if (vrc == VERR_NOT_SUPPORTED)
7289 throw setError(VBOX_E_NOT_SUPPORTED,
7290 tr("Compacting is not yet supported for medium '%s'"),
7291 location.c_str());
7292 else if (vrc == VERR_NOT_IMPLEMENTED)
7293 throw setError(E_NOTIMPL,
7294 tr("Compacting is not implemented, medium '%s'"),
7295 location.c_str());
7296 else
7297 throw setError(VBOX_E_FILE_ERROR,
7298 tr("Could not compact medium '%s'%s"),
7299 location.c_str(),
7300 vdError(vrc).c_str());
7301 }
7302 }
7303 catch (HRESULT aRC) { rc = aRC; }
7304
7305 VDDestroy(hdd);
7306 }
7307 catch (HRESULT aRC) { rc = aRC; }
7308
7309 /* Everything is explicitly unlocked when the task exits,
7310 * as the task destruction also destroys the media chain. */
7311
7312 return rc;
7313}
7314
7315/**
7316 * Implementation code for the "resize" task.
7317 *
7318 * @param task
7319 * @return
7320 */
7321HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task)
7322{
7323 HRESULT rc = S_OK;
7324
7325 /* Lock all in {parent,child} order. The lock is also used as a
7326 * signal from the task initiator (which releases it only after
7327 * RTThreadCreate()) that we can start the job. */
7328 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7329
7330 try
7331 {
7332 PVBOXHDD hdd;
7333 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7334 ComAssertRCThrow(vrc, E_FAIL);
7335
7336 try
7337 {
7338 /* Open all media in the chain. */
7339 MediumLockList::Base::const_iterator mediumListBegin =
7340 task.mpMediumLockList->GetBegin();
7341 MediumLockList::Base::const_iterator mediumListEnd =
7342 task.mpMediumLockList->GetEnd();
7343 MediumLockList::Base::const_iterator mediumListLast =
7344 mediumListEnd;
7345 mediumListLast--;
7346 for (MediumLockList::Base::const_iterator it = mediumListBegin;
7347 it != mediumListEnd;
7348 ++it)
7349 {
7350 const MediumLock &mediumLock = *it;
7351 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7352 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7353
7354 /* sanity check */
7355 if (it == mediumListLast)
7356 Assert(pMedium->m->state == MediumState_LockedWrite);
7357 else
7358 Assert(pMedium->m->state == MediumState_LockedRead);
7359
7360 /* Open all media but last in read-only mode. Do not handle
7361 * shareable media, as compaction and sharing are mutually
7362 * exclusive. */
7363 vrc = VDOpen(hdd,
7364 pMedium->m->strFormat.c_str(),
7365 pMedium->m->strLocationFull.c_str(),
7366 (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
7367 pMedium->m->vdImageIfaces);
7368 if (RT_FAILURE(vrc))
7369 throw setError(VBOX_E_FILE_ERROR,
7370 tr("Could not open the medium storage unit '%s'%s"),
7371 pMedium->m->strLocationFull.c_str(),
7372 vdError(vrc).c_str());
7373 }
7374
7375 Assert(m->state == MediumState_LockedWrite);
7376
7377 Utf8Str location(m->strLocationFull);
7378
7379 /* unlock before the potentially lengthy operation */
7380 thisLock.release();
7381
7382 VDGEOMETRY geo = {0, 0, 0}; /* auto */
7383 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
7384 if (RT_FAILURE(vrc))
7385 {
7386 if (vrc == VERR_NOT_SUPPORTED)
7387 throw setError(VBOX_E_NOT_SUPPORTED,
7388 tr("Compacting is not yet supported for medium '%s'"),
7389 location.c_str());
7390 else if (vrc == VERR_NOT_IMPLEMENTED)
7391 throw setError(E_NOTIMPL,
7392 tr("Compacting is not implemented, medium '%s'"),
7393 location.c_str());
7394 else
7395 throw setError(VBOX_E_FILE_ERROR,
7396 tr("Could not compact medium '%s'%s"),
7397 location.c_str(),
7398 vdError(vrc).c_str());
7399 }
7400 }
7401 catch (HRESULT aRC) { rc = aRC; }
7402
7403 VDDestroy(hdd);
7404 }
7405 catch (HRESULT aRC) { rc = aRC; }
7406
7407 /* Everything is explicitly unlocked when the task exits,
7408 * as the task destruction also destroys the media chain. */
7409
7410 return rc;
7411}
7412
7413/**
7414 * Implementation code for the "export" task.
7415 *
7416 * This only gets started from Medium::exportFile() and always runs
7417 * asynchronously. It doesn't touch anything configuration related, so
7418 * we never save the VirtualBox.xml file here.
7419 *
7420 * @param task
7421 * @return
7422 */
7423HRESULT Medium::taskExportHandler(Medium::ExportTask &task)
7424{
7425 HRESULT rc = S_OK;
7426
7427 try
7428 {
7429 /* Lock all in {parent,child} order. The lock is also used as a
7430 * signal from the task initiator (which releases it only after
7431 * RTThreadCreate()) that we can start the job. */
7432 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7433
7434 PVBOXHDD hdd;
7435 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7436 ComAssertRCThrow(vrc, E_FAIL);
7437
7438 try
7439 {
7440 /* Open all media in the source chain. */
7441 MediumLockList::Base::const_iterator sourceListBegin =
7442 task.mpSourceMediumLockList->GetBegin();
7443 MediumLockList::Base::const_iterator sourceListEnd =
7444 task.mpSourceMediumLockList->GetEnd();
7445 for (MediumLockList::Base::const_iterator it = sourceListBegin;
7446 it != sourceListEnd;
7447 ++it)
7448 {
7449 const MediumLock &mediumLock = *it;
7450 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7451 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7452
7453 /* sanity check */
7454 Assert(pMedium->m->state == MediumState_LockedRead);
7455
7456 /* Open all media in read-only mode. */
7457 vrc = VDOpen(hdd,
7458 pMedium->m->strFormat.c_str(),
7459 pMedium->m->strLocationFull.c_str(),
7460 VD_OPEN_FLAGS_READONLY,
7461 pMedium->m->vdImageIfaces);
7462 if (RT_FAILURE(vrc))
7463 throw setError(VBOX_E_FILE_ERROR,
7464 tr("Could not open the medium storage unit '%s'%s"),
7465 pMedium->m->strLocationFull.c_str(),
7466 vdError(vrc).c_str());
7467 }
7468
7469 Utf8Str targetFormat(task.mFormat->getId());
7470 Utf8Str targetLocation(task.mFilename);
7471
7472 Assert(m->state == MediumState_LockedRead);
7473
7474 /* unlock before the potentially lengthy operation */
7475 thisLock.release();
7476
7477 /* ensure the target directory exists */
7478 rc = VirtualBox::ensureFilePathExists(targetLocation);
7479 if (FAILED(rc))
7480 throw rc;
7481
7482 PVBOXHDD targetHdd;
7483 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
7484 ComAssertRCThrow(vrc, E_FAIL);
7485
7486 try
7487 {
7488 vrc = VDCopy(hdd,
7489 VD_LAST_IMAGE,
7490 targetHdd,
7491 targetFormat.c_str(),
7492 targetLocation.c_str(),
7493 false /* fMoveByRename */,
7494 0 /* cbSize */,
7495 task.mVariant,
7496 NULL /* pDstUuid */,
7497 VD_OPEN_FLAGS_NORMAL,
7498 NULL /* pVDIfsOperation */,
7499 task.mVDImageIfaces,
7500 task.mVDOperationIfaces);
7501 if (RT_FAILURE(vrc))
7502 throw setError(VBOX_E_FILE_ERROR,
7503 tr("Could not create the clone medium '%s'%s"),
7504 targetLocation.c_str(), vdError(vrc).c_str());
7505 }
7506 catch (HRESULT aRC) { rc = aRC; }
7507
7508 VDDestroy(targetHdd);
7509 }
7510 catch (HRESULT aRC) { rc = aRC; }
7511
7512 VDDestroy(hdd);
7513 }
7514 catch (HRESULT aRC) { rc = aRC; }
7515
7516 /* Everything is explicitly unlocked when the task exits,
7517 * as the task destruction also destroys the source chain. */
7518
7519 /* Make sure the source chain is released early, otherwise it can
7520 * lead to deadlocks with concurrent IAppliance activities. */
7521 task.mpSourceMediumLockList->Clear();
7522
7523 return rc;
7524}
7525
7526/**
7527 * Implementation code for the "import" task.
7528 *
7529 * This only gets started from Medium::importFile() and always runs
7530 * asynchronously. It potentially touches the media registry, so we
7531 * always save the VirtualBox.xml file when we're done here.
7532 *
7533 * @param task
7534 * @return
7535 */
7536HRESULT Medium::taskImportHandler(Medium::ImportTask &task)
7537{
7538 HRESULT rc = S_OK;
7539
7540 const ComObjPtr<Medium> &pParent = task.mParent;
7541
7542 bool fCreatingTarget = false;
7543
7544 uint64_t size = 0, logicalSize = 0;
7545 MediumVariant_T variant = MediumVariant_Standard;
7546 bool fGenerateUuid = false;
7547
7548 try
7549 {
7550 /* Lock all in {parent,child} order. The lock is also used as a
7551 * signal from the task initiator (which releases it only after
7552 * RTThreadCreate()) that we can start the job. */
7553 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
7554
7555 fCreatingTarget = m->state == MediumState_Creating;
7556
7557 /* The object may request a specific UUID (through a special form of
7558 * the setLocation() argument). Otherwise we have to generate it */
7559 Guid targetId = m->id;
7560 fGenerateUuid = targetId.isEmpty();
7561 if (fGenerateUuid)
7562 {
7563 targetId.create();
7564 /* VirtualBox::registerHardDisk() will need UUID */
7565 unconst(m->id) = targetId;
7566 }
7567
7568
7569 PVBOXHDD hdd;
7570 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
7571 ComAssertRCThrow(vrc, E_FAIL);
7572
7573 try
7574 {
7575 /* Open source medium. */
7576 vrc = VDOpen(hdd,
7577 task.mFormat->getId().c_str(),
7578 task.mFilename.c_str(),
7579 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL,
7580 task.mVDImageIfaces);
7581 if (RT_FAILURE(vrc))
7582 throw setError(VBOX_E_FILE_ERROR,
7583 tr("Could not open the medium storage unit '%s'%s"),
7584 task.mFilename.c_str(),
7585 vdError(vrc).c_str());
7586
7587 Utf8Str targetFormat(m->strFormat);
7588 Utf8Str targetLocation(m->strLocationFull);
7589
7590 Assert( m->state == MediumState_Creating
7591 || m->state == MediumState_LockedWrite);
7592 Assert( pParent.isNull()
7593 || pParent->m->state == MediumState_LockedRead);
7594
7595 /* unlock before the potentially lengthy operation */
7596 thisLock.release();
7597
7598 /* ensure the target directory exists */
7599 rc = VirtualBox::ensureFilePathExists(targetLocation);
7600 if (FAILED(rc))
7601 throw rc;
7602
7603 PVBOXHDD targetHdd;
7604 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
7605 ComAssertRCThrow(vrc, E_FAIL);
7606
7607 try
7608 {
7609 /* Open all media in the target chain. */
7610 MediumLockList::Base::const_iterator targetListBegin =
7611 task.mpTargetMediumLockList->GetBegin();
7612 MediumLockList::Base::const_iterator targetListEnd =
7613 task.mpTargetMediumLockList->GetEnd();
7614 for (MediumLockList::Base::const_iterator it = targetListBegin;
7615 it != targetListEnd;
7616 ++it)
7617 {
7618 const MediumLock &mediumLock = *it;
7619 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7620
7621 /* If the target medium is not created yet there's no
7622 * reason to open it. */
7623 if (pMedium == this && fCreatingTarget)
7624 continue;
7625
7626 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7627
7628 /* sanity check */
7629 Assert( pMedium->m->state == MediumState_LockedRead
7630 || pMedium->m->state == MediumState_LockedWrite);
7631
7632 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7633 if (pMedium->m->state != MediumState_LockedWrite)
7634 uOpenFlags = VD_OPEN_FLAGS_READONLY;
7635 if (pMedium->m->type == MediumType_Shareable)
7636 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7637
7638 /* Open all media in appropriate mode. */
7639 vrc = VDOpen(targetHdd,
7640 pMedium->m->strFormat.c_str(),
7641 pMedium->m->strLocationFull.c_str(),
7642 uOpenFlags,
7643 pMedium->m->vdImageIfaces);
7644 if (RT_FAILURE(vrc))
7645 throw setError(VBOX_E_FILE_ERROR,
7646 tr("Could not open the medium storage unit '%s'%s"),
7647 pMedium->m->strLocationFull.c_str(),
7648 vdError(vrc).c_str());
7649 }
7650
7651 /** @todo r=klaus target isn't locked, race getting the state */
7652 vrc = VDCopy(hdd,
7653 VD_LAST_IMAGE,
7654 targetHdd,
7655 targetFormat.c_str(),
7656 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
7657 false /* fMoveByRename */,
7658 0 /* cbSize */,
7659 task.mVariant,
7660 targetId.raw(),
7661 VD_OPEN_FLAGS_NORMAL,
7662 NULL /* pVDIfsOperation */,
7663 m->vdImageIfaces,
7664 task.mVDOperationIfaces);
7665 if (RT_FAILURE(vrc))
7666 throw setError(VBOX_E_FILE_ERROR,
7667 tr("Could not create the clone medium '%s'%s"),
7668 targetLocation.c_str(), vdError(vrc).c_str());
7669
7670 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
7671 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
7672 unsigned uImageFlags;
7673 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
7674 if (RT_SUCCESS(vrc))
7675 variant = (MediumVariant_T)uImageFlags;
7676 }
7677 catch (HRESULT aRC) { rc = aRC; }
7678
7679 VDDestroy(targetHdd);
7680 }
7681 catch (HRESULT aRC) { rc = aRC; }
7682
7683 VDDestroy(hdd);
7684 }
7685 catch (HRESULT aRC) { rc = aRC; }
7686
7687 /* Only do the parent changes for newly created media. */
7688 if (SUCCEEDED(rc) && fCreatingTarget)
7689 {
7690 /* we set mParent & children() */
7691 AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7692
7693 Assert(m->pParent.isNull());
7694
7695 if (pParent)
7696 {
7697 /* associate the clone with the parent and deassociate
7698 * from VirtualBox */
7699 m->pParent = pParent;
7700 pParent->m->llChildren.push_back(this);
7701
7702 /* register with mVirtualBox as the last step and move to
7703 * Created state only on success (leaving an orphan file is
7704 * better than breaking media registry consistency) */
7705 rc = pParent->m->pVirtualBox->registerHardDisk(this, NULL /* llRegistriesThatNeedSaving */);
7706
7707 if (FAILED(rc))
7708 /* break parent association on failure to register */
7709 this->deparent(); // removes target from parent
7710 }
7711 else
7712 {
7713 /* just register */
7714 rc = m->pVirtualBox->registerHardDisk(this, NULL /* pllRegistriesThatNeedSaving */);
7715 }
7716 }
7717
7718 if (fCreatingTarget)
7719 {
7720 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
7721
7722 if (SUCCEEDED(rc))
7723 {
7724 m->state = MediumState_Created;
7725
7726 m->size = size;
7727 m->logicalSize = logicalSize;
7728 m->variant = variant;
7729 }
7730 else
7731 {
7732 /* back to NotCreated on failure */
7733 m->state = MediumState_NotCreated;
7734
7735 /* reset UUID to prevent it from being reused next time */
7736 if (fGenerateUuid)
7737 unconst(m->id).clear();
7738 }
7739 }
7740
7741 // now, at the end of this task (always asynchronous), save the settings
7742 {
7743 // save the settings
7744 GuidList llRegistriesThatNeedSaving;
7745 addToRegistryIDList(llRegistriesThatNeedSaving);
7746 rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
7747 }
7748
7749 /* Everything is explicitly unlocked when the task exits,
7750 * as the task destruction also destroys the target chain. */
7751
7752 /* Make sure the target chain is released early, otherwise it can
7753 * lead to deadlocks with concurrent IAppliance activities. */
7754 task.mpTargetMediumLockList->Clear();
7755
7756 return rc;
7757}
7758
7759/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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