VirtualBox

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

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

Main/Medium: new MediumType_Readonly, replaces the DeviceType_DVD hacks and later will allow truly readonly floppies and so on.

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

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