VirtualBox

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

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

Main: use paths relative to machine folder in machine registries

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

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