VirtualBox

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

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

Main: add missing saveRegistries() in Medium::DeleteStorage()

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

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