VirtualBox

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

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

Main: source re-org.

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