VirtualBox

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

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

Main: use VirtualBox::saveRegistries() instead of plain VirtualBox::saveSettings() on media property changes to make sure the correct registry gets saved

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

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