VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MediumImpl.cpp@ 49868

最後變更 在這個檔案從49868是 49866,由 vboxsync 提交於 11 年 前

Main/Medium: fix regression in the wrapper conversion which caused hangs on every IMedium.close call

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

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