VirtualBox

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

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

Storage: Implement offical support for other disk types like DVD and floppy images. DMG images can be used now without hacks

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

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