VirtualBox

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

Last change on this file since 50914 was 50874, checked in by vboxsync, 11 years ago

6813 src-all/ProgressImp.cpp + some formatting/line length sorting

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

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