VirtualBox

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

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

Main/Medium: use the correct image interface for importFile

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