VirtualBox

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

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

Main: sort Medium methods to have the same order in .h und .cpp

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

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