VirtualBox

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

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

Main: fail with meaningful error message if medium path is not fully qualified; this is now reported in the GUI 'create disk' wizard, GUI still needs fixing

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