VirtualBox

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

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

Main: rework 'save registries' logic to ensure that all media registries get saved, not just the global VirtualBox.xml file

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