VirtualBox

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

最後變更 在這個檔案從34779是 34779,由 vboxsync 提交於 14 年 前

Main/Medium: do not allow changing the medium type of an already attached medium to immutable.

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

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