VirtualBox

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

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

Main/Medium: if the medium is inaccessible try to get the up to date info and if that's not available refuse to change a medium to Shareable.

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