VirtualBox

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

最後變更 在這個檔案從79954是 79954,由 vboxsync 提交於 6 年 前

bugref:8344. Fixed a bug with an uppercase letter suffix in the image name (like ".VMDK").

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 373.9 KB
 
1/* $Id: MediumImpl.cpp 79954 2019-07-24 11:53:32Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2008-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MEDIUM
19#include "MediumImpl.h"
20#include "MediumIOImpl.h"
21#include "TokenImpl.h"
22#include "ProgressImpl.h"
23#include "SystemPropertiesImpl.h"
24#include "VirtualBoxImpl.h"
25#include "ExtPackManagerImpl.h"
26
27#include "AutoCaller.h"
28#include "LoggingNew.h"
29#include "ThreadTask.h"
30#include "VBox/com/MultiResult.h"
31#include "VBox/com/ErrorInfo.h"
32
33#include <VBox/err.h>
34#include <VBox/settings.h>
35
36#include <iprt/param.h>
37#include <iprt/path.h>
38#include <iprt/file.h>
39#include <iprt/cpp/utils.h>
40#include <iprt/memsafer.h>
41#include <iprt/base64.h>
42#include <iprt/vfs.h>
43#include <iprt/fsvfs.h>
44
45#include <VBox/vd.h>
46
47#include <algorithm>
48#include <list>
49#include <set>
50#include <map>
51
52
53typedef std::list<Guid> GuidList;
54
55
56#ifdef VBOX_WITH_EXTPACK
57static const char g_szVDPlugin[] = "VDPluginCrypt";
58#endif
59
60
61////////////////////////////////////////////////////////////////////////////////
62//
63// Medium data definition
64//
65////////////////////////////////////////////////////////////////////////////////
66
67/** Describes how a machine refers to this medium. */
68struct BackRef
69{
70 /** Equality predicate for stdc++. */
71 struct EqualsTo : public std::unary_function <BackRef, bool>
72 {
73 explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
74
75 bool operator()(const argument_type &aThat) const
76 {
77 return aThat.machineId == machineId;
78 }
79
80 const Guid machineId;
81 };
82
83 BackRef(const Guid &aMachineId,
84 const Guid &aSnapshotId = Guid::Empty)
85 : machineId(aMachineId),
86 fInCurState(aSnapshotId.isZero())
87 {
88 if (aSnapshotId.isValid() && !aSnapshotId.isZero())
89 llSnapshotIds.push_back(aSnapshotId);
90 }
91
92 Guid machineId;
93 bool fInCurState : 1;
94 GuidList llSnapshotIds;
95};
96
97typedef std::list<BackRef> BackRefList;
98
99struct Medium::Data
100{
101 Data()
102 : pVirtualBox(NULL),
103 state(MediumState_NotCreated),
104 variant(MediumVariant_Standard),
105 size(0),
106 readers(0),
107 preLockState(MediumState_NotCreated),
108 queryInfoSem(LOCKCLASS_MEDIUMQUERY),
109 queryInfoRunning(false),
110 type(MediumType_Normal),
111 devType(DeviceType_HardDisk),
112 logicalSize(0),
113 hddOpenMode(OpenReadWrite),
114 autoReset(false),
115 hostDrive(false),
116 implicit(false),
117 fClosing(false),
118 uOpenFlagsDef(VD_OPEN_FLAGS_IGNORE_FLUSH),
119 numCreateDiffTasks(0),
120 vdDiskIfaces(NULL),
121 vdImageIfaces(NULL),
122 fMoveThisMedium(false)
123 { }
124
125 /** weak VirtualBox parent */
126 VirtualBox * const pVirtualBox;
127
128 // pParent and llChildren are protected by VirtualBox::i_getMediaTreeLockHandle()
129 ComObjPtr<Medium> pParent;
130 MediaList llChildren; // to add a child, just call push_back; to remove
131 // a child, call child->deparent() which does a lookup
132
133 GuidList llRegistryIDs; // media registries in which this medium is listed
134
135 const Guid id;
136 Utf8Str strDescription;
137 MediumState_T state;
138 MediumVariant_T variant;
139 Utf8Str strLocationFull;
140 uint64_t size;
141 Utf8Str strLastAccessError;
142
143 BackRefList backRefs;
144
145 size_t readers;
146 MediumState_T preLockState;
147
148 /** Special synchronization for operations which must wait for
149 * Medium::i_queryInfo in another thread to complete. Using a SemRW is
150 * not quite ideal, but at least it is subject to the lock validator,
151 * unlike the SemEventMulti which we had here for many years. Catching
152 * possible deadlocks is more important than a tiny bit of efficiency. */
153 RWLockHandle queryInfoSem;
154 bool queryInfoRunning : 1;
155
156 const Utf8Str strFormat;
157 ComObjPtr<MediumFormat> formatObj;
158
159 MediumType_T type;
160 DeviceType_T devType;
161 uint64_t logicalSize;
162
163 HDDOpenMode hddOpenMode;
164
165 bool autoReset : 1;
166
167 /** New UUID to be set on the next Medium::i_queryInfo call. */
168 const Guid uuidImage;
169 /** New parent UUID to be set on the next Medium::i_queryInfo call. */
170 const Guid uuidParentImage;
171
172 bool hostDrive : 1;
173
174 settings::StringsMap mapProperties;
175
176 bool implicit : 1;
177 /** Flag whether the medium is in the process of being closed. */
178 bool fClosing: 1;
179
180 /** Default flags passed to VDOpen(). */
181 unsigned uOpenFlagsDef;
182
183 uint32_t numCreateDiffTasks;
184
185 Utf8Str vdError; /*< Error remembered by the VD error callback. */
186
187 VDINTERFACEERROR vdIfError;
188
189 VDINTERFACECONFIG vdIfConfig;
190
191 /** The handle to the default VD TCP/IP interface. */
192 VDIFINST hTcpNetInst;
193
194 PVDINTERFACE vdDiskIfaces;
195 PVDINTERFACE vdImageIfaces;
196
197 /** Flag if the medium is going to move to a new
198 * location. */
199 bool fMoveThisMedium;
200 /** new location path */
201 Utf8Str strNewLocationFull;
202};
203
204typedef struct VDSOCKETINT
205{
206 /** Socket handle. */
207 RTSOCKET hSocket;
208} VDSOCKETINT, *PVDSOCKETINT;
209
210////////////////////////////////////////////////////////////////////////////////
211//
212// Globals
213//
214////////////////////////////////////////////////////////////////////////////////
215
216/**
217 * Medium::Task class for asynchronous operations.
218 *
219 * @note Instances of this class must be created using new() because the
220 * task thread function will delete them when the task is complete.
221 *
222 * @note The constructor of this class adds a caller on the managed Medium
223 * object which is automatically released upon destruction.
224 */
225class Medium::Task : public ThreadTask
226{
227public:
228 Task(Medium *aMedium, Progress *aProgress, bool fNotifyAboutChanges = true)
229 : ThreadTask("Medium::Task"),
230 mVDOperationIfaces(NULL),
231 mMedium(aMedium),
232 mMediumCaller(aMedium),
233 mProgress(aProgress),
234 mVirtualBoxCaller(NULL),
235 mNotifyAboutChanges(fNotifyAboutChanges)
236 {
237 AssertReturnVoidStmt(aMedium, mRC = E_FAIL);
238 mRC = mMediumCaller.rc();
239 if (FAILED(mRC))
240 return;
241
242 /* Get strong VirtualBox reference, see below. */
243 VirtualBox *pVirtualBox = aMedium->m->pVirtualBox;
244 mVirtualBox = pVirtualBox;
245 mVirtualBoxCaller.attach(pVirtualBox);
246 mRC = mVirtualBoxCaller.rc();
247 if (FAILED(mRC))
248 return;
249
250 /* Set up a per-operation progress interface, can be used freely (for
251 * binary operations you can use it either on the source or target). */
252 if (mProgress)
253 {
254 mVDIfProgress.pfnProgress = aProgress->i_vdProgressCallback;
255 int vrc = VDInterfaceAdd(&mVDIfProgress.Core,
256 "Medium::Task::vdInterfaceProgress",
257 VDINTERFACETYPE_PROGRESS,
258 mProgress,
259 sizeof(mVDIfProgress),
260 &mVDOperationIfaces);
261 AssertRC(vrc);
262 if (RT_FAILURE(vrc))
263 mRC = E_FAIL;
264 }
265 }
266
267 // Make all destructors virtual. Just in case.
268 virtual ~Task()
269 {
270 /* send the notification of completion.*/
271 if ( isAsync()
272 && !mProgress.isNull())
273 mProgress->i_notifyComplete(mRC);
274 }
275
276 HRESULT rc() const { return mRC; }
277 bool isOk() const { return SUCCEEDED(rc()); }
278 bool NotifyAboutChanges() const { return mNotifyAboutChanges; }
279
280 const ComPtr<Progress>& GetProgressObject() const {return mProgress;}
281
282 /**
283 * Runs Medium::Task::executeTask() on the current thread
284 * instead of creating a new one.
285 */
286 HRESULT runNow()
287 {
288 LogFlowFuncEnter();
289
290 mRC = executeTask();
291
292 LogFlowFunc(("rc=%Rhrc\n", mRC));
293 LogFlowFuncLeave();
294 return mRC;
295 }
296
297 /**
298 * Implementation code for the "create base" task.
299 * Used as function for execution from a standalone thread.
300 */
301 void handler()
302 {
303 LogFlowFuncEnter();
304 try
305 {
306 mRC = executeTask(); /* (destructor picks up mRC, see above) */
307 LogFlowFunc(("rc=%Rhrc\n", mRC));
308 }
309 catch (...)
310 {
311 LogRel(("Some exception in the function Medium::Task:handler()\n"));
312 }
313
314 LogFlowFuncLeave();
315 }
316
317 PVDINTERFACE mVDOperationIfaces;
318
319 const ComObjPtr<Medium> mMedium;
320 AutoCaller mMediumCaller;
321
322protected:
323 HRESULT mRC;
324
325private:
326 virtual HRESULT executeTask() = 0;
327
328 const ComObjPtr<Progress> mProgress;
329
330 VDINTERFACEPROGRESS mVDIfProgress;
331
332 /* Must have a strong VirtualBox reference during a task otherwise the
333 * reference count might drop to 0 while a task is still running. This
334 * would result in weird behavior, including deadlocks due to uninit and
335 * locking order issues. The deadlock often is not detectable because the
336 * uninit uses event semaphores which sabotages deadlock detection. */
337 ComObjPtr<VirtualBox> mVirtualBox;
338 AutoCaller mVirtualBoxCaller;
339 bool mNotifyAboutChanges;
340};
341
342class Medium::CreateBaseTask : public Medium::Task
343{
344public:
345 CreateBaseTask(Medium *aMedium,
346 Progress *aProgress,
347 uint64_t aSize,
348 MediumVariant_T aVariant,
349 bool fNotifyAboutChanges = true)
350 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
351 mSize(aSize),
352 mVariant(aVariant)
353 {
354 m_strTaskName = "createBase";
355 }
356
357 uint64_t mSize;
358 MediumVariant_T mVariant;
359
360private:
361 HRESULT executeTask()
362 {
363 return mMedium->i_taskCreateBaseHandler(*this);
364 }
365};
366
367class Medium::CreateDiffTask : public Medium::Task
368{
369public:
370 CreateDiffTask(Medium *aMedium,
371 Progress *aProgress,
372 Medium *aTarget,
373 MediumVariant_T aVariant,
374 MediumLockList *aMediumLockList,
375 bool fKeepMediumLockList = false,
376 bool fNotifyAboutChanges = true)
377 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
378 mpMediumLockList(aMediumLockList),
379 mTarget(aTarget),
380 mVariant(aVariant),
381 mTargetCaller(aTarget),
382 mfKeepMediumLockList(fKeepMediumLockList)
383 {
384 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
385 mRC = mTargetCaller.rc();
386 if (FAILED(mRC))
387 return;
388 m_strTaskName = "createDiff";
389 }
390
391 ~CreateDiffTask()
392 {
393 if (!mfKeepMediumLockList && mpMediumLockList)
394 delete mpMediumLockList;
395 }
396
397 MediumLockList *mpMediumLockList;
398
399 const ComObjPtr<Medium> mTarget;
400 MediumVariant_T mVariant;
401
402private:
403 HRESULT executeTask()
404 {
405 return mMedium->i_taskCreateDiffHandler(*this);
406 }
407
408 AutoCaller mTargetCaller;
409 bool mfKeepMediumLockList;
410};
411
412class Medium::CloneTask : public Medium::Task
413{
414public:
415 CloneTask(Medium *aMedium,
416 Progress *aProgress,
417 Medium *aTarget,
418 MediumVariant_T aVariant,
419 Medium *aParent,
420 uint32_t idxSrcImageSame,
421 uint32_t idxDstImageSame,
422 MediumLockList *aSourceMediumLockList,
423 MediumLockList *aTargetMediumLockList,
424 bool fKeepSourceMediumLockList = false,
425 bool fKeepTargetMediumLockList = false,
426 bool fNotifyAboutChanges = true)
427 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
428 mTarget(aTarget),
429 mParent(aParent),
430 mpSourceMediumLockList(aSourceMediumLockList),
431 mpTargetMediumLockList(aTargetMediumLockList),
432 mVariant(aVariant),
433 midxSrcImageSame(idxSrcImageSame),
434 midxDstImageSame(idxDstImageSame),
435 mTargetCaller(aTarget),
436 mParentCaller(aParent),
437 mfKeepSourceMediumLockList(fKeepSourceMediumLockList),
438 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
439 {
440 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
441 mRC = mTargetCaller.rc();
442 if (FAILED(mRC))
443 return;
444 /* aParent may be NULL */
445 mRC = mParentCaller.rc();
446 if (FAILED(mRC))
447 return;
448 AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
449 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
450 m_strTaskName = "createClone";
451 }
452
453 ~CloneTask()
454 {
455 if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
456 delete mpSourceMediumLockList;
457 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
458 delete mpTargetMediumLockList;
459 }
460
461 const ComObjPtr<Medium> mTarget;
462 const ComObjPtr<Medium> mParent;
463 MediumLockList *mpSourceMediumLockList;
464 MediumLockList *mpTargetMediumLockList;
465 MediumVariant_T mVariant;
466 uint32_t midxSrcImageSame;
467 uint32_t midxDstImageSame;
468
469private:
470 HRESULT executeTask()
471 {
472 return mMedium->i_taskCloneHandler(*this);
473 }
474
475 AutoCaller mTargetCaller;
476 AutoCaller mParentCaller;
477 bool mfKeepSourceMediumLockList;
478 bool mfKeepTargetMediumLockList;
479};
480
481class Medium::MoveTask : public Medium::Task
482{
483public:
484 MoveTask(Medium *aMedium,
485 Progress *aProgress,
486 MediumVariant_T aVariant,
487 MediumLockList *aMediumLockList,
488 bool fKeepMediumLockList = false,
489 bool fNotifyAboutChanges = true)
490 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
491 mpMediumLockList(aMediumLockList),
492 mVariant(aVariant),
493 mfKeepMediumLockList(fKeepMediumLockList)
494 {
495 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
496 m_strTaskName = "createMove";
497 }
498
499 ~MoveTask()
500 {
501 if (!mfKeepMediumLockList && mpMediumLockList)
502 delete mpMediumLockList;
503 }
504
505 MediumLockList *mpMediumLockList;
506 MediumVariant_T mVariant;
507
508private:
509 HRESULT executeTask()
510 {
511 return mMedium->i_taskMoveHandler(*this);
512 }
513
514 bool mfKeepMediumLockList;
515};
516
517class Medium::CompactTask : public Medium::Task
518{
519public:
520 CompactTask(Medium *aMedium,
521 Progress *aProgress,
522 MediumLockList *aMediumLockList,
523 bool fKeepMediumLockList = false,
524 bool fNotifyAboutChanges = true)
525 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
526 mpMediumLockList(aMediumLockList),
527 mfKeepMediumLockList(fKeepMediumLockList)
528 {
529 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
530 m_strTaskName = "createCompact";
531 }
532
533 ~CompactTask()
534 {
535 if (!mfKeepMediumLockList && mpMediumLockList)
536 delete mpMediumLockList;
537 }
538
539 MediumLockList *mpMediumLockList;
540
541private:
542 HRESULT executeTask()
543 {
544 return mMedium->i_taskCompactHandler(*this);
545 }
546
547 bool mfKeepMediumLockList;
548};
549
550class Medium::ResizeTask : public Medium::Task
551{
552public:
553 ResizeTask(Medium *aMedium,
554 uint64_t aSize,
555 Progress *aProgress,
556 MediumLockList *aMediumLockList,
557 bool fKeepMediumLockList = false,
558 bool fNotifyAboutChanges = true)
559 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
560 mSize(aSize),
561 mpMediumLockList(aMediumLockList),
562 mfKeepMediumLockList(fKeepMediumLockList)
563 {
564 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
565 m_strTaskName = "createResize";
566 }
567
568 ~ResizeTask()
569 {
570 if (!mfKeepMediumLockList && mpMediumLockList)
571 delete mpMediumLockList;
572 }
573
574 uint64_t mSize;
575 MediumLockList *mpMediumLockList;
576
577private:
578 HRESULT executeTask()
579 {
580 return mMedium->i_taskResizeHandler(*this);
581 }
582
583 bool mfKeepMediumLockList;
584};
585
586class Medium::ResetTask : public Medium::Task
587{
588public:
589 ResetTask(Medium *aMedium,
590 Progress *aProgress,
591 MediumLockList *aMediumLockList,
592 bool fKeepMediumLockList = false,
593 bool fNotifyAboutChanges = true)
594 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
595 mpMediumLockList(aMediumLockList),
596 mfKeepMediumLockList(fKeepMediumLockList)
597 {
598 m_strTaskName = "createReset";
599 }
600
601 ~ResetTask()
602 {
603 if (!mfKeepMediumLockList && mpMediumLockList)
604 delete mpMediumLockList;
605 }
606
607 MediumLockList *mpMediumLockList;
608
609private:
610 HRESULT executeTask()
611 {
612 return mMedium->i_taskResetHandler(*this);
613 }
614
615 bool mfKeepMediumLockList;
616};
617
618class Medium::DeleteTask : public Medium::Task
619{
620public:
621 DeleteTask(Medium *aMedium,
622 Progress *aProgress,
623 MediumLockList *aMediumLockList,
624 bool fKeepMediumLockList = false,
625 bool fNotifyAboutChanges = true)
626 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
627 mpMediumLockList(aMediumLockList),
628 mfKeepMediumLockList(fKeepMediumLockList)
629 {
630 m_strTaskName = "createDelete";
631 }
632
633 ~DeleteTask()
634 {
635 if (!mfKeepMediumLockList && mpMediumLockList)
636 delete mpMediumLockList;
637 }
638
639 MediumLockList *mpMediumLockList;
640
641private:
642 HRESULT executeTask()
643 {
644 return mMedium->i_taskDeleteHandler(*this);
645 }
646
647 bool mfKeepMediumLockList;
648};
649
650class Medium::MergeTask : public Medium::Task
651{
652public:
653 MergeTask(Medium *aMedium,
654 Medium *aTarget,
655 bool fMergeForward,
656 Medium *aParentForTarget,
657 MediumLockList *aChildrenToReparent,
658 Progress *aProgress,
659 MediumLockList *aMediumLockList,
660 bool fKeepMediumLockList = false,
661 bool fNotifyAboutChanges = true)
662 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
663 mTarget(aTarget),
664 mfMergeForward(fMergeForward),
665 mParentForTarget(aParentForTarget),
666 mpChildrenToReparent(aChildrenToReparent),
667 mpMediumLockList(aMediumLockList),
668 mTargetCaller(aTarget),
669 mParentForTargetCaller(aParentForTarget),
670 mfKeepMediumLockList(fKeepMediumLockList)
671 {
672 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
673 m_strTaskName = "createMerge";
674 }
675
676 ~MergeTask()
677 {
678 if (!mfKeepMediumLockList && mpMediumLockList)
679 delete mpMediumLockList;
680 if (mpChildrenToReparent)
681 delete mpChildrenToReparent;
682 }
683
684 const ComObjPtr<Medium> mTarget;
685 bool mfMergeForward;
686 /* When mpChildrenToReparent is null then mParentForTarget is non-null and
687 * vice versa. In other words: they are used in different cases. */
688 const ComObjPtr<Medium> mParentForTarget;
689 MediumLockList *mpChildrenToReparent;
690 MediumLockList *mpMediumLockList;
691
692private:
693 HRESULT executeTask()
694 {
695 return mMedium->i_taskMergeHandler(*this);
696 }
697
698 AutoCaller mTargetCaller;
699 AutoCaller mParentForTargetCaller;
700 bool mfKeepMediumLockList;
701};
702
703class Medium::ImportTask : public Medium::Task
704{
705public:
706 ImportTask(Medium *aMedium,
707 Progress *aProgress,
708 const char *aFilename,
709 MediumFormat *aFormat,
710 MediumVariant_T aVariant,
711 RTVFSIOSTREAM aVfsIosSrc,
712 Medium *aParent,
713 MediumLockList *aTargetMediumLockList,
714 bool fKeepTargetMediumLockList = false,
715 bool fNotifyAboutChanges = true)
716 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
717 mFilename(aFilename),
718 mFormat(aFormat),
719 mVariant(aVariant),
720 mParent(aParent),
721 mpTargetMediumLockList(aTargetMediumLockList),
722 mpVfsIoIf(NULL),
723 mParentCaller(aParent),
724 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
725 {
726 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
727 /* aParent may be NULL */
728 mRC = mParentCaller.rc();
729 if (FAILED(mRC))
730 return;
731
732 mVDImageIfaces = aMedium->m->vdImageIfaces;
733
734 int vrc = VDIfCreateFromVfsStream(aVfsIosSrc, RTFILE_O_READ, &mpVfsIoIf);
735 AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
736
737 vrc = VDInterfaceAdd(&mpVfsIoIf->Core, "Medium::ImportTaskVfsIos",
738 VDINTERFACETYPE_IO, mpVfsIoIf,
739 sizeof(VDINTERFACEIO), &mVDImageIfaces);
740 AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
741 m_strTaskName = "createImport";
742 }
743
744 ~ImportTask()
745 {
746 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
747 delete mpTargetMediumLockList;
748 if (mpVfsIoIf)
749 {
750 VDIfDestroyFromVfsStream(mpVfsIoIf);
751 mpVfsIoIf = NULL;
752 }
753 }
754
755 Utf8Str mFilename;
756 ComObjPtr<MediumFormat> mFormat;
757 MediumVariant_T mVariant;
758 const ComObjPtr<Medium> mParent;
759 MediumLockList *mpTargetMediumLockList;
760 PVDINTERFACE mVDImageIfaces;
761 PVDINTERFACEIO mpVfsIoIf; /**< Pointer to the VFS I/O stream to VD I/O interface wrapper. */
762
763private:
764 HRESULT executeTask()
765 {
766 return mMedium->i_taskImportHandler(*this);
767 }
768
769 AutoCaller mParentCaller;
770 bool mfKeepTargetMediumLockList;
771};
772
773class Medium::EncryptTask : public Medium::Task
774{
775public:
776 EncryptTask(Medium *aMedium,
777 const com::Utf8Str &strNewPassword,
778 const com::Utf8Str &strCurrentPassword,
779 const com::Utf8Str &strCipher,
780 const com::Utf8Str &strNewPasswordId,
781 Progress *aProgress,
782 MediumLockList *aMediumLockList)
783 : Medium::Task(aMedium, aProgress, false),
784 mstrNewPassword(strNewPassword),
785 mstrCurrentPassword(strCurrentPassword),
786 mstrCipher(strCipher),
787 mstrNewPasswordId(strNewPasswordId),
788 mpMediumLockList(aMediumLockList)
789 {
790 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
791 /* aParent may be NULL */
792 mRC = mParentCaller.rc();
793 if (FAILED(mRC))
794 return;
795
796 mVDImageIfaces = aMedium->m->vdImageIfaces;
797 m_strTaskName = "createEncrypt";
798 }
799
800 ~EncryptTask()
801 {
802 if (mstrNewPassword.length())
803 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
804 if (mstrCurrentPassword.length())
805 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
806
807 /* Keep any errors which might be set when deleting the lock list. */
808 ErrorInfoKeeper eik;
809 delete mpMediumLockList;
810 }
811
812 Utf8Str mstrNewPassword;
813 Utf8Str mstrCurrentPassword;
814 Utf8Str mstrCipher;
815 Utf8Str mstrNewPasswordId;
816 MediumLockList *mpMediumLockList;
817 PVDINTERFACE mVDImageIfaces;
818
819private:
820 HRESULT executeTask()
821 {
822 return mMedium->i_taskEncryptHandler(*this);
823 }
824
825 AutoCaller mParentCaller;
826};
827
828
829////////////////////////////////////////////////////////////////////////////////
830//
831// Medium constructor / destructor
832//
833////////////////////////////////////////////////////////////////////////////////
834
835DEFINE_EMPTY_CTOR_DTOR(Medium)
836
837HRESULT Medium::FinalConstruct()
838{
839 m = new Data;
840
841 /* Initialize the callbacks of the VD error interface */
842 m->vdIfError.pfnError = i_vdErrorCall;
843 m->vdIfError.pfnMessage = NULL;
844
845 /* Initialize the callbacks of the VD config interface */
846 m->vdIfConfig.pfnAreKeysValid = i_vdConfigAreKeysValid;
847 m->vdIfConfig.pfnQuerySize = i_vdConfigQuerySize;
848 m->vdIfConfig.pfnQuery = i_vdConfigQuery;
849 m->vdIfConfig.pfnUpdate = i_vdConfigUpdate;
850 m->vdIfConfig.pfnQueryBytes = NULL;
851
852 /* Initialize the per-disk interface chain (could be done more globally,
853 * but it's not wasting much time or space so it's not worth it). */
854 int vrc;
855 vrc = VDInterfaceAdd(&m->vdIfError.Core,
856 "Medium::vdInterfaceError",
857 VDINTERFACETYPE_ERROR, this,
858 sizeof(VDINTERFACEERROR), &m->vdDiskIfaces);
859 AssertRCReturn(vrc, E_FAIL);
860
861 /* Initialize the per-image interface chain */
862 vrc = VDInterfaceAdd(&m->vdIfConfig.Core,
863 "Medium::vdInterfaceConfig",
864 VDINTERFACETYPE_CONFIG, this,
865 sizeof(VDINTERFACECONFIG), &m->vdImageIfaces);
866 AssertRCReturn(vrc, E_FAIL);
867
868 /* Initialize the callbacks of the VD TCP interface (we always use the host
869 * IP stack for now) */
870 vrc = VDIfTcpNetInstDefaultCreate(&m->hTcpNetInst, &m->vdImageIfaces);
871 AssertRCReturn(vrc, E_FAIL);
872
873 return BaseFinalConstruct();
874}
875
876void Medium::FinalRelease()
877{
878 uninit();
879
880 VDIfTcpNetInstDefaultDestroy(m->hTcpNetInst);
881 delete m;
882
883 BaseFinalRelease();
884}
885
886/**
887 * Initializes an empty hard disk object without creating or opening an associated
888 * storage unit.
889 *
890 * This gets called by VirtualBox::CreateMedium() in which case uuidMachineRegistry
891 * is empty since starting with VirtualBox 4.0, we no longer add opened media to a
892 * registry automatically (this is deferred until the medium is attached to a machine).
893 *
894 * This also gets called when VirtualBox creates diff images; in this case uuidMachineRegistry
895 * is set to the registry of the parent image to make sure they all end up in the same
896 * file.
897 *
898 * For hard disks that don't have the MediumFormatCapabilities_CreateFixed or
899 * MediumFormatCapabilities_CreateDynamic capability (and therefore cannot be created or deleted
900 * with the means of VirtualBox) the associated storage unit is assumed to be
901 * ready for use so the state of the hard disk object will be set to Created.
902 *
903 * @param aVirtualBox VirtualBox object.
904 * @param aFormat
905 * @param aLocation Storage unit location.
906 * @param uuidMachineRegistry The registry to which this medium should be added
907 * (global registry UUID or machine UUID or empty if none).
908 * @param aDeviceType Device Type.
909 */
910HRESULT Medium::init(VirtualBox *aVirtualBox,
911 const Utf8Str &aFormat,
912 const Utf8Str &aLocation,
913 const Guid &uuidMachineRegistry,
914 const DeviceType_T aDeviceType)
915{
916 AssertReturn(aVirtualBox != NULL, E_FAIL);
917 AssertReturn(!aFormat.isEmpty(), E_FAIL);
918
919 /* Enclose the state transition NotReady->InInit->Ready */
920 AutoInitSpan autoInitSpan(this);
921 AssertReturn(autoInitSpan.isOk(), E_FAIL);
922
923 HRESULT rc = S_OK;
924
925 unconst(m->pVirtualBox) = aVirtualBox;
926
927 if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
928 m->llRegistryIDs.push_back(uuidMachineRegistry);
929
930 /* no storage yet */
931 m->state = MediumState_NotCreated;
932
933 /* cannot be a host drive */
934 m->hostDrive = false;
935
936 m->devType = aDeviceType;
937
938 /* No storage unit is created yet, no need to call Medium::i_queryInfo */
939
940 rc = i_setFormat(aFormat);
941 if (FAILED(rc)) return rc;
942
943 rc = i_setLocation(aLocation);
944 if (FAILED(rc)) return rc;
945
946 if (!(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateFixed
947 | MediumFormatCapabilities_CreateDynamic))
948 )
949 {
950 /* Storage for mediums of this format can neither be explicitly
951 * created by VirtualBox nor deleted, so we place the medium to
952 * Inaccessible state here and also add it to the registry. The
953 * state means that one has to use RefreshState() to update the
954 * medium format specific fields. */
955 m->state = MediumState_Inaccessible;
956 // create new UUID
957 unconst(m->id).create();
958
959 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
960 ComObjPtr<Medium> pMedium;
961
962 /*
963 * Check whether the UUID is taken already and create a new one
964 * if required.
965 * Try this only a limited amount of times in case the PRNG is broken
966 * in some way to prevent an endless loop.
967 */
968 for (unsigned i = 0; i < 5; i++)
969 {
970 bool fInUse;
971
972 fInUse = m->pVirtualBox->i_isMediaUuidInUse(m->id, aDeviceType);
973 if (fInUse)
974 {
975 // create new UUID
976 unconst(m->id).create();
977 }
978 else
979 break;
980 }
981
982 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
983 Assert(this == pMedium || FAILED(rc));
984 }
985
986 /* Confirm a successful initialization when it's the case */
987 if (SUCCEEDED(rc))
988 autoInitSpan.setSucceeded();
989
990 return rc;
991}
992
993/**
994 * Initializes the medium object by opening the storage unit at the specified
995 * location. The enOpenMode parameter defines whether the medium will be opened
996 * read/write or read-only.
997 *
998 * This gets called by VirtualBox::OpenMedium() and also by
999 * Machine::AttachDevice() and createImplicitDiffs() when new diff
1000 * images are created.
1001 *
1002 * There is no registry for this case since starting with VirtualBox 4.0, we
1003 * no longer add opened media to a registry automatically (this is deferred
1004 * until the medium is attached to a machine).
1005 *
1006 * For hard disks, the UUID, format and the parent of this medium will be
1007 * determined when reading the medium storage unit. For DVD and floppy images,
1008 * which have no UUIDs in their storage units, new UUIDs are created.
1009 * If the detected or set parent is not known to VirtualBox, then this method
1010 * will fail.
1011 *
1012 * @param aVirtualBox VirtualBox object.
1013 * @param aLocation Storage unit location.
1014 * @param enOpenMode Whether to open the medium read/write or read-only.
1015 * @param fForceNewUuid Whether a new UUID should be set to avoid duplicates.
1016 * @param aDeviceType Device type of medium.
1017 */
1018HRESULT Medium::init(VirtualBox *aVirtualBox,
1019 const Utf8Str &aLocation,
1020 HDDOpenMode enOpenMode,
1021 bool fForceNewUuid,
1022 DeviceType_T aDeviceType)
1023{
1024 AssertReturn(aVirtualBox, E_INVALIDARG);
1025 AssertReturn(!aLocation.isEmpty(), E_INVALIDARG);
1026
1027 HRESULT rc = S_OK;
1028
1029 {
1030 /* Enclose the state transition NotReady->InInit->Ready */
1031 AutoInitSpan autoInitSpan(this);
1032 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1033
1034 unconst(m->pVirtualBox) = aVirtualBox;
1035
1036 /* there must be a storage unit */
1037 m->state = MediumState_Created;
1038
1039 /* remember device type for correct unregistering later */
1040 m->devType = aDeviceType;
1041
1042 /* cannot be a host drive */
1043 m->hostDrive = false;
1044
1045 /* remember the open mode (defaults to ReadWrite) */
1046 m->hddOpenMode = enOpenMode;
1047
1048 if (aDeviceType == DeviceType_DVD)
1049 m->type = MediumType_Readonly;
1050 else if (aDeviceType == DeviceType_Floppy)
1051 m->type = MediumType_Writethrough;
1052
1053 rc = i_setLocation(aLocation);
1054 if (FAILED(rc)) return rc;
1055
1056 /* get all the information about the medium from the storage unit */
1057 if (fForceNewUuid)
1058 unconst(m->uuidImage).create();
1059
1060 m->state = MediumState_Inaccessible;
1061 m->strLastAccessError = tr("Accessibility check was not yet performed");
1062
1063 /* Confirm a successful initialization before the call to i_queryInfo.
1064 * Otherwise we can end up with a AutoCaller deadlock because the
1065 * medium becomes visible but is not marked as initialized. Causes
1066 * locking trouble (e.g. trying to save media registries) which is
1067 * hard to solve. */
1068 autoInitSpan.setSucceeded();
1069 }
1070
1071 /* we're normal code from now on, no longer init */
1072 AutoCaller autoCaller(this);
1073 if (FAILED(autoCaller.rc()))
1074 return autoCaller.rc();
1075
1076 /* need to call i_queryInfo immediately to correctly place the medium in
1077 * the respective media tree and update other information such as uuid */
1078 rc = i_queryInfo(fForceNewUuid /* fSetImageId */, false /* fSetParentId */,
1079 autoCaller);
1080 if (SUCCEEDED(rc))
1081 {
1082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1083
1084 /* if the storage unit is not accessible, it's not acceptable for the
1085 * newly opened media so convert this into an error */
1086 if (m->state == MediumState_Inaccessible)
1087 {
1088 Assert(!m->strLastAccessError.isEmpty());
1089 rc = setError(E_FAIL, "%s", m->strLastAccessError.c_str());
1090 alock.release();
1091 autoCaller.release();
1092 uninit();
1093 }
1094 else
1095 {
1096 AssertStmt(!m->id.isZero(),
1097 alock.release(); autoCaller.release(); uninit(); return E_FAIL);
1098
1099 /* storage format must be detected by Medium::i_queryInfo if the
1100 * medium is accessible */
1101 AssertStmt(!m->strFormat.isEmpty(),
1102 alock.release(); autoCaller.release(); uninit(); return E_FAIL);
1103 }
1104 }
1105 else
1106 {
1107 /* opening this image failed, mark the object as dead */
1108 autoCaller.release();
1109 uninit();
1110 }
1111
1112 return rc;
1113}
1114
1115/**
1116 * Initializes the medium object by loading its data from the given settings
1117 * node. The medium will always be opened read/write.
1118 *
1119 * In this case, since we're loading from a registry, uuidMachineRegistry is
1120 * always set: it's either the global registry UUID or a machine UUID when
1121 * loading from a per-machine registry.
1122 *
1123 * @param aParent Parent medium disk or NULL for a root (base) medium.
1124 * @param aDeviceType Device type of the medium.
1125 * @param uuidMachineRegistry The registry to which this medium should be
1126 * added (global registry UUID or machine UUID).
1127 * @param data Configuration settings.
1128 * @param strMachineFolder The machine folder with which to resolve relative paths;
1129 * if empty, then we use the VirtualBox home directory
1130 *
1131 * @note Locks the medium tree for writing.
1132 */
1133HRESULT Medium::initOne(Medium *aParent,
1134 DeviceType_T aDeviceType,
1135 const Guid &uuidMachineRegistry,
1136 const settings::Medium &data,
1137 const Utf8Str &strMachineFolder)
1138{
1139 HRESULT rc;
1140
1141 if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
1142 m->llRegistryIDs.push_back(uuidMachineRegistry);
1143
1144 /* register with VirtualBox/parent early, since uninit() will
1145 * unconditionally unregister on failure */
1146 if (aParent)
1147 {
1148 // differencing medium: add to parent
1149 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1150 // no need to check maximum depth as settings reading did it
1151 i_setParent(aParent);
1152 }
1153
1154 /* see below why we don't call Medium::i_queryInfo (and therefore treat
1155 * the medium as inaccessible for now */
1156 m->state = MediumState_Inaccessible;
1157 m->strLastAccessError = tr("Accessibility check was not yet performed");
1158
1159 /* required */
1160 unconst(m->id) = data.uuid;
1161
1162 /* assume not a host drive */
1163 m->hostDrive = false;
1164
1165 /* optional */
1166 m->strDescription = data.strDescription;
1167
1168 /* required */
1169 if (aDeviceType == DeviceType_HardDisk)
1170 {
1171 AssertReturn(!data.strFormat.isEmpty(), E_FAIL);
1172 rc = i_setFormat(data.strFormat);
1173 if (FAILED(rc)) return rc;
1174 }
1175 else
1176 {
1177 /// @todo handle host drive settings here as well?
1178 if (!data.strFormat.isEmpty())
1179 rc = i_setFormat(data.strFormat);
1180 else
1181 rc = i_setFormat("RAW");
1182 if (FAILED(rc)) return rc;
1183 }
1184
1185 /* optional, only for diffs, default is false; we can only auto-reset
1186 * diff media so they must have a parent */
1187 if (aParent != NULL)
1188 m->autoReset = data.fAutoReset;
1189 else
1190 m->autoReset = false;
1191
1192 /* properties (after setting the format as it populates the map). Note that
1193 * if some properties are not supported but present in the settings file,
1194 * they will still be read and accessible (for possible backward
1195 * compatibility; we can also clean them up from the XML upon next
1196 * XML format version change if we wish) */
1197 for (settings::StringsMap::const_iterator it = data.properties.begin();
1198 it != data.properties.end();
1199 ++it)
1200 {
1201 const Utf8Str &name = it->first;
1202 const Utf8Str &value = it->second;
1203 m->mapProperties[name] = value;
1204 }
1205
1206 /* try to decrypt an optional iSCSI initiator secret */
1207 settings::StringsMap::const_iterator itCph = data.properties.find("InitiatorSecretEncrypted");
1208 if ( itCph != data.properties.end()
1209 && !itCph->second.isEmpty())
1210 {
1211 Utf8Str strPlaintext;
1212 int vrc = m->pVirtualBox->i_decryptSetting(&strPlaintext, itCph->second);
1213 if (RT_SUCCESS(vrc))
1214 m->mapProperties["InitiatorSecret"] = strPlaintext;
1215 }
1216
1217 Utf8Str strFull;
1218 if (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
1219 {
1220 // compose full path of the medium, if it's not fully qualified...
1221 // slightly convoluted logic here. If the caller has given us a
1222 // machine folder, then a relative path will be relative to that:
1223 if ( !strMachineFolder.isEmpty()
1224 && !RTPathStartsWithRoot(data.strLocation.c_str())
1225 )
1226 {
1227 strFull = strMachineFolder;
1228 strFull += RTPATH_SLASH;
1229 strFull += data.strLocation;
1230 }
1231 else
1232 {
1233 // Otherwise use the old VirtualBox "make absolute path" logic:
1234 rc = m->pVirtualBox->i_calculateFullPath(data.strLocation, strFull);
1235 if (FAILED(rc)) return rc;
1236 }
1237 }
1238 else
1239 strFull = data.strLocation;
1240
1241 rc = i_setLocation(strFull);
1242 if (FAILED(rc)) return rc;
1243
1244 if (aDeviceType == DeviceType_HardDisk)
1245 {
1246 /* type is only for base hard disks */
1247 if (m->pParent.isNull())
1248 m->type = data.hdType;
1249 }
1250 else if (aDeviceType == DeviceType_DVD)
1251 m->type = MediumType_Readonly;
1252 else
1253 m->type = MediumType_Writethrough;
1254
1255 /* remember device type for correct unregistering later */
1256 m->devType = aDeviceType;
1257
1258 LogFlowThisFunc(("m->strLocationFull='%s', m->strFormat=%s, m->id={%RTuuid}\n",
1259 m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw()));
1260
1261 return S_OK;
1262}
1263
1264/**
1265 * Initializes the medium object and its children by loading its data from the
1266 * given settings node. The medium will always be opened read/write.
1267 *
1268 * In this case, since we're loading from a registry, uuidMachineRegistry is
1269 * always set: it's either the global registry UUID or a machine UUID when
1270 * loading from a per-machine registry.
1271 *
1272 * @param aVirtualBox VirtualBox object.
1273 * @param aParent Parent medium disk or NULL for a root (base) medium.
1274 * @param aDeviceType Device type of the medium.
1275 * @param uuidMachineRegistry The registry to which this medium should be added
1276 * (global registry UUID or machine UUID).
1277 * @param data Configuration settings.
1278 * @param strMachineFolder The machine folder with which to resolve relative
1279 * paths; if empty, then we use the VirtualBox home directory
1280 * @param mediaTreeLock Autolock.
1281 *
1282 * @note Locks the medium tree for writing.
1283 */
1284HRESULT Medium::init(VirtualBox *aVirtualBox,
1285 Medium *aParent,
1286 DeviceType_T aDeviceType,
1287 const Guid &uuidMachineRegistry,
1288 const settings::Medium &data,
1289 const Utf8Str &strMachineFolder,
1290 AutoWriteLock &mediaTreeLock)
1291{
1292 using namespace settings;
1293
1294 Assert(aVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
1295 AssertReturn(aVirtualBox, E_INVALIDARG);
1296
1297 /* Enclose the state transition NotReady->InInit->Ready */
1298 AutoInitSpan autoInitSpan(this);
1299 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1300
1301 unconst(m->pVirtualBox) = aVirtualBox;
1302
1303 // Do not inline this method call, as the purpose of having this separate
1304 // is to save on stack size. Less local variables are the key for reaching
1305 // deep recursion levels with small stack (XPCOM/g++ without optimization).
1306 HRESULT rc = initOne(aParent, aDeviceType, uuidMachineRegistry, data, strMachineFolder);
1307
1308
1309 /* Don't call Medium::i_queryInfo for registered media to prevent the calling
1310 * thread (i.e. the VirtualBox server startup thread) from an unexpected
1311 * freeze but mark it as initially inaccessible instead. The vital UUID,
1312 * location and format properties are read from the registry file above; to
1313 * get the actual state and the rest of the data, the user will have to call
1314 * COMGETTER(State). */
1315
1316 /* load all children */
1317 for (settings::MediaList::const_iterator it = data.llChildren.begin();
1318 it != data.llChildren.end();
1319 ++it)
1320 {
1321 const settings::Medium &med = *it;
1322
1323 ComObjPtr<Medium> pMedium;
1324 pMedium.createObject();
1325 rc = pMedium->init(aVirtualBox,
1326 this, // parent
1327 aDeviceType,
1328 uuidMachineRegistry,
1329 med, // child data
1330 strMachineFolder,
1331 mediaTreeLock);
1332 if (FAILED(rc)) break;
1333
1334 rc = m->pVirtualBox->i_registerMedium(pMedium, &pMedium, mediaTreeLock);
1335 if (FAILED(rc)) break;
1336 }
1337
1338 /* Confirm a successful initialization when it's the case */
1339 if (SUCCEEDED(rc))
1340 autoInitSpan.setSucceeded();
1341
1342 return rc;
1343}
1344
1345/**
1346 * Initializes the medium object by providing the host drive information.
1347 * Not used for anything but the host floppy/host DVD case.
1348 *
1349 * There is no registry for this case.
1350 *
1351 * @param aVirtualBox VirtualBox object.
1352 * @param aDeviceType Device type of the medium.
1353 * @param aLocation Location of the host drive.
1354 * @param aDescription Comment for this host drive.
1355 *
1356 * @note Locks VirtualBox lock for writing.
1357 */
1358HRESULT Medium::init(VirtualBox *aVirtualBox,
1359 DeviceType_T aDeviceType,
1360 const Utf8Str &aLocation,
1361 const Utf8Str &aDescription /* = Utf8Str::Empty */)
1362{
1363 ComAssertRet(aDeviceType == DeviceType_DVD || aDeviceType == DeviceType_Floppy, E_INVALIDARG);
1364 ComAssertRet(!aLocation.isEmpty(), E_INVALIDARG);
1365
1366 /* Enclose the state transition NotReady->InInit->Ready */
1367 AutoInitSpan autoInitSpan(this);
1368 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1369
1370 unconst(m->pVirtualBox) = aVirtualBox;
1371
1372 // We do not store host drives in VirtualBox.xml or anywhere else, so if we want
1373 // host drives to be identifiable by UUID and not give the drive a different UUID
1374 // every time VirtualBox starts, we need to fake a reproducible UUID here:
1375 RTUUID uuid;
1376 RTUuidClear(&uuid);
1377 if (aDeviceType == DeviceType_DVD)
1378 memcpy(&uuid.au8[0], "DVD", 3);
1379 else
1380 memcpy(&uuid.au8[0], "FD", 2);
1381 /* use device name, adjusted to the end of uuid, shortened if necessary */
1382 size_t lenLocation = aLocation.length();
1383 if (lenLocation > 12)
1384 memcpy(&uuid.au8[4], aLocation.c_str() + (lenLocation - 12), 12);
1385 else
1386 memcpy(&uuid.au8[4 + 12 - lenLocation], aLocation.c_str(), lenLocation);
1387 unconst(m->id) = uuid;
1388
1389 if (aDeviceType == DeviceType_DVD)
1390 m->type = MediumType_Readonly;
1391 else
1392 m->type = MediumType_Writethrough;
1393 m->devType = aDeviceType;
1394 m->state = MediumState_Created;
1395 m->hostDrive = true;
1396 HRESULT rc = i_setFormat("RAW");
1397 if (FAILED(rc)) return rc;
1398 rc = i_setLocation(aLocation);
1399 if (FAILED(rc)) return rc;
1400 m->strDescription = aDescription;
1401
1402 autoInitSpan.setSucceeded();
1403 return S_OK;
1404}
1405
1406/**
1407 * Uninitializes the instance.
1408 *
1409 * Called either from FinalRelease() or by the parent when it gets destroyed.
1410 *
1411 * @note All children of this medium get uninitialized by calling their
1412 * uninit() methods.
1413 */
1414void Medium::uninit()
1415{
1416 /* It is possible that some previous/concurrent uninit has already cleared
1417 * the pVirtualBox reference, and in this case we don't need to continue.
1418 * Normally this would be handled through the AutoUninitSpan magic, however
1419 * this cannot be done at this point as the media tree must be locked
1420 * before reaching the AutoUninitSpan, otherwise deadlocks can happen.
1421 *
1422 * NOTE: The tree lock is higher priority than the medium caller and medium
1423 * object locks, i.e. the medium caller may have to be released and be
1424 * re-acquired in the right place later. See Medium::getParent() for sample
1425 * code how to do this safely. */
1426 VirtualBox *pVirtualBox = m->pVirtualBox;
1427 if (!pVirtualBox)
1428 return;
1429
1430 /* Caller must not hold the object or media tree lock over uninit(). */
1431 Assert(!isWriteLockOnCurrentThread());
1432 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
1433
1434 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1435
1436 /* Enclose the state transition Ready->InUninit->NotReady */
1437 AutoUninitSpan autoUninitSpan(this);
1438 if (autoUninitSpan.uninitDone())
1439 return;
1440
1441 if (!m->formatObj.isNull())
1442 m->formatObj.setNull();
1443
1444 if (m->state == MediumState_Deleting)
1445 {
1446 /* This medium has been already deleted (directly or as part of a
1447 * merge). Reparenting has already been done. */
1448 Assert(m->pParent.isNull());
1449 }
1450 else
1451 {
1452 MediaList llChildren(m->llChildren);
1453 m->llChildren.clear();
1454 autoUninitSpan.setSucceeded();
1455
1456 while (!llChildren.empty())
1457 {
1458 ComObjPtr<Medium> pChild = llChildren.front();
1459 llChildren.pop_front();
1460 pChild->m->pParent.setNull();
1461 treeLock.release();
1462 pChild->uninit();
1463 treeLock.acquire();
1464 }
1465
1466 if (m->pParent)
1467 {
1468 // this is a differencing disk: then remove it from the parent's children list
1469 i_deparent();
1470 }
1471 }
1472
1473 unconst(m->pVirtualBox) = NULL;
1474}
1475
1476/**
1477 * Internal helper that removes "this" from the list of children of its
1478 * parent. Used in uninit() and other places when reparenting is necessary.
1479 *
1480 * The caller must hold the medium tree lock!
1481 */
1482void Medium::i_deparent()
1483{
1484 MediaList &llParent = m->pParent->m->llChildren;
1485 for (MediaList::iterator it = llParent.begin();
1486 it != llParent.end();
1487 ++it)
1488 {
1489 Medium *pParentsChild = *it;
1490 if (this == pParentsChild)
1491 {
1492 llParent.erase(it);
1493 break;
1494 }
1495 }
1496 m->pParent.setNull();
1497}
1498
1499/**
1500 * Internal helper that removes "this" from the list of children of its
1501 * parent. Used in uninit() and other places when reparenting is necessary.
1502 *
1503 * The caller must hold the medium tree lock!
1504 */
1505void Medium::i_setParent(const ComObjPtr<Medium> &pParent)
1506{
1507 m->pParent = pParent;
1508 if (pParent)
1509 pParent->m->llChildren.push_back(this);
1510}
1511
1512
1513////////////////////////////////////////////////////////////////////////////////
1514//
1515// IMedium public methods
1516//
1517////////////////////////////////////////////////////////////////////////////////
1518
1519HRESULT Medium::getId(com::Guid &aId)
1520{
1521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1522
1523 aId = m->id;
1524
1525 return S_OK;
1526}
1527
1528HRESULT Medium::getDescription(AutoCaller &autoCaller, com::Utf8Str &aDescription)
1529{
1530 NOREF(autoCaller);
1531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1532
1533 aDescription = m->strDescription;
1534
1535 return S_OK;
1536}
1537
1538HRESULT Medium::setDescription(AutoCaller &autoCaller, const com::Utf8Str &aDescription)
1539{
1540 /// @todo update m->strDescription and save the global registry (and local
1541 /// registries of portable VMs referring to this medium), this will also
1542 /// require to add the mRegistered flag to data
1543
1544 HRESULT rc = S_OK;
1545
1546 MediumLockList *pMediumLockList(new MediumLockList());
1547
1548 try
1549 {
1550 autoCaller.release();
1551
1552 // to avoid redundant locking, which just takes a time, just call required functions.
1553 // the error will be just stored and will be reported after locks will be acquired again
1554
1555 const char *pszError = NULL;
1556
1557
1558 /* Build the lock list. */
1559 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
1560 this /* pToLockWrite */,
1561 true /* fMediumLockWriteAll */,
1562 NULL,
1563 *pMediumLockList);
1564 if (FAILED(rc))
1565 {
1566 pszError = tr("Failed to create medium lock list for '%s'");
1567 }
1568 else
1569 {
1570 rc = pMediumLockList->Lock();
1571 if (FAILED(rc))
1572 pszError = tr("Failed to lock media '%s'");
1573 }
1574
1575 // locking: we need the tree lock first because we access parent pointers
1576 // and we need to write-lock the media involved
1577 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1578
1579 autoCaller.add();
1580 AssertComRCThrowRC(autoCaller.rc());
1581
1582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1583
1584 if (FAILED(rc))
1585 throw setError(rc, pszError, i_getLocationFull().c_str());
1586
1587 /* Set a new description */
1588 m->strDescription = aDescription;
1589
1590 // save the settings
1591 alock.release();
1592 autoCaller.release();
1593 treeLock.release();
1594 i_markRegistriesModified();
1595 m->pVirtualBox->i_saveModifiedRegistries();
1596 m->pVirtualBox->i_onMediumConfigChanged(this);
1597 }
1598 catch (HRESULT aRC) { rc = aRC; }
1599
1600 delete pMediumLockList;
1601
1602 return rc;
1603}
1604
1605HRESULT Medium::getState(MediumState_T *aState)
1606{
1607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1608 *aState = m->state;
1609
1610 return S_OK;
1611}
1612
1613HRESULT Medium::getVariant(std::vector<MediumVariant_T> &aVariant)
1614{
1615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1616
1617 const size_t cBits = sizeof(MediumVariant_T) * 8;
1618 aVariant.resize(cBits);
1619 for (size_t i = 0; i < cBits; ++i)
1620 aVariant[i] = (MediumVariant_T)(m->variant & RT_BIT(i));
1621
1622 return S_OK;
1623}
1624
1625HRESULT Medium::getLocation(com::Utf8Str &aLocation)
1626{
1627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1628
1629 aLocation = m->strLocationFull;
1630
1631 return S_OK;
1632}
1633
1634HRESULT Medium::getName(com::Utf8Str &aName)
1635{
1636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1637
1638 aName = i_getName();
1639
1640 return S_OK;
1641}
1642
1643HRESULT Medium::getDeviceType(DeviceType_T *aDeviceType)
1644{
1645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1646
1647 *aDeviceType = m->devType;
1648
1649 return S_OK;
1650}
1651
1652HRESULT Medium::getHostDrive(BOOL *aHostDrive)
1653{
1654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 *aHostDrive = m->hostDrive;
1657
1658 return S_OK;
1659}
1660
1661HRESULT Medium::getSize(LONG64 *aSize)
1662{
1663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1664
1665 *aSize = m->size;
1666
1667 return S_OK;
1668}
1669
1670HRESULT Medium::getFormat(com::Utf8Str &aFormat)
1671{
1672 /* no need to lock, m->strFormat is const */
1673
1674 aFormat = m->strFormat;
1675 return S_OK;
1676}
1677
1678HRESULT Medium::getMediumFormat(ComPtr<IMediumFormat> &aMediumFormat)
1679{
1680 /* no need to lock, m->formatObj is const */
1681 m->formatObj.queryInterfaceTo(aMediumFormat.asOutParam());
1682
1683 return S_OK;
1684}
1685
1686HRESULT Medium::getType(AutoCaller &autoCaller, MediumType_T *aType)
1687{
1688 NOREF(autoCaller);
1689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1690
1691 *aType = m->type;
1692
1693 return S_OK;
1694}
1695
1696HRESULT Medium::setType(AutoCaller &autoCaller, MediumType_T aType)
1697{
1698 autoCaller.release();
1699
1700 /* It is possible that some previous/concurrent uninit has already cleared
1701 * the pVirtualBox reference, see #uninit(). */
1702 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1703
1704 // we access m->pParent
1705 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
1706
1707 autoCaller.add();
1708 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1709
1710 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 switch (m->state)
1713 {
1714 case MediumState_Created:
1715 case MediumState_Inaccessible:
1716 break;
1717 default:
1718 return i_setStateError();
1719 }
1720
1721 if (m->type == aType)
1722 {
1723 /* Nothing to do */
1724 return S_OK;
1725 }
1726
1727 DeviceType_T devType = i_getDeviceType();
1728 // DVD media can only be readonly.
1729 if (devType == DeviceType_DVD && aType != MediumType_Readonly)
1730 return setError(VBOX_E_INVALID_OBJECT_STATE,
1731 tr("Cannot change the type of DVD medium '%s'"),
1732 m->strLocationFull.c_str());
1733 // Floppy media can only be writethrough or readonly.
1734 if ( devType == DeviceType_Floppy
1735 && aType != MediumType_Writethrough
1736 && aType != MediumType_Readonly)
1737 return setError(VBOX_E_INVALID_OBJECT_STATE,
1738 tr("Cannot change the type of floppy medium '%s'"),
1739 m->strLocationFull.c_str());
1740
1741 /* cannot change the type of a differencing medium */
1742 if (m->pParent)
1743 return setError(VBOX_E_INVALID_OBJECT_STATE,
1744 tr("Cannot change the type of medium '%s' because it is a differencing medium"),
1745 m->strLocationFull.c_str());
1746
1747 /* Cannot change the type of a medium being in use by more than one VM.
1748 * If the change is to Immutable or MultiAttach then it must not be
1749 * directly attached to any VM, otherwise the assumptions about indirect
1750 * attachment elsewhere are violated and the VM becomes inaccessible.
1751 * Attaching an immutable medium triggers the diff creation, and this is
1752 * vital for the correct operation. */
1753 if ( m->backRefs.size() > 1
1754 || ( ( aType == MediumType_Immutable
1755 || aType == MediumType_MultiAttach)
1756 && m->backRefs.size() > 0))
1757 return setError(VBOX_E_INVALID_OBJECT_STATE,
1758 tr("Cannot change the type of medium '%s' because it is attached to %d virtual machines"),
1759 m->strLocationFull.c_str(), m->backRefs.size());
1760
1761 switch (aType)
1762 {
1763 case MediumType_Normal:
1764 case MediumType_Immutable:
1765 case MediumType_MultiAttach:
1766 {
1767 /* normal can be easily converted to immutable and vice versa even
1768 * if they have children as long as they are not attached to any
1769 * machine themselves */
1770 break;
1771 }
1772 case MediumType_Writethrough:
1773 case MediumType_Shareable:
1774 case MediumType_Readonly:
1775 {
1776 /* cannot change to writethrough, shareable or readonly
1777 * if there are children */
1778 if (i_getChildren().size() != 0)
1779 return setError(VBOX_E_OBJECT_IN_USE,
1780 tr("Cannot change type for medium '%s' since it has %d child media"),
1781 m->strLocationFull.c_str(), i_getChildren().size());
1782 if (aType == MediumType_Shareable)
1783 {
1784 MediumVariant_T variant = i_getVariant();
1785 if (!(variant & MediumVariant_Fixed))
1786 return setError(VBOX_E_INVALID_OBJECT_STATE,
1787 tr("Cannot change type for medium '%s' to 'Shareable' since it is a dynamic medium storage unit"),
1788 m->strLocationFull.c_str());
1789 }
1790 else if (aType == MediumType_Readonly && devType == DeviceType_HardDisk)
1791 {
1792 // Readonly hard disks are not allowed, this medium type is reserved for
1793 // DVDs and floppy images at the moment. Later we might allow readonly hard
1794 // disks, but that's extremely unusual and many guest OSes will have trouble.
1795 return setError(VBOX_E_INVALID_OBJECT_STATE,
1796 tr("Cannot change type for medium '%s' to 'Readonly' since it is a hard disk"),
1797 m->strLocationFull.c_str());
1798 }
1799 break;
1800 }
1801 default:
1802 AssertFailedReturn(E_FAIL);
1803 }
1804
1805 if (aType == MediumType_MultiAttach)
1806 {
1807 // This type is new with VirtualBox 4.0 and therefore requires settings
1808 // version 1.11 in the settings backend. Unfortunately it is not enough to do
1809 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
1810 // two reasons: The medium type is a property of the media registry tree, which
1811 // can reside in the global config file (for pre-4.0 media); we would therefore
1812 // possibly need to bump the global config version. We don't want to do that though
1813 // because that might make downgrading to pre-4.0 impossible.
1814 // As a result, we can only use these two new types if the medium is NOT in the
1815 // global registry:
1816 const Guid &uuidGlobalRegistry = m->pVirtualBox->i_getGlobalRegistryId();
1817 if (i_isInRegistry(uuidGlobalRegistry))
1818 return setError(VBOX_E_INVALID_OBJECT_STATE,
1819 tr("Cannot change type for medium '%s': the media type 'MultiAttach' can only be used "
1820 "on media registered with a machine that was created with VirtualBox 4.0 or later"),
1821 m->strLocationFull.c_str());
1822 }
1823
1824 m->type = aType;
1825
1826 // save the settings
1827 mlock.release();
1828 autoCaller.release();
1829 treeLock.release();
1830 i_markRegistriesModified();
1831 m->pVirtualBox->i_saveModifiedRegistries();
1832 m->pVirtualBox->i_onMediumConfigChanged(this);
1833
1834 return S_OK;
1835}
1836
1837HRESULT Medium::getAllowedTypes(std::vector<MediumType_T> &aAllowedTypes)
1838{
1839 NOREF(aAllowedTypes);
1840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1841
1842 ReturnComNotImplemented();
1843}
1844
1845HRESULT Medium::getParent(AutoCaller &autoCaller, ComPtr<IMedium> &aParent)
1846{
1847 autoCaller.release();
1848
1849 /* It is possible that some previous/concurrent uninit has already cleared
1850 * the pVirtualBox reference, see #uninit(). */
1851 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1852
1853 /* we access m->pParent */
1854 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
1855
1856 autoCaller.add();
1857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1858
1859 m->pParent.queryInterfaceTo(aParent.asOutParam());
1860
1861 return S_OK;
1862}
1863
1864HRESULT Medium::getChildren(AutoCaller &autoCaller, std::vector<ComPtr<IMedium> > &aChildren)
1865{
1866 autoCaller.release();
1867
1868 /* It is possible that some previous/concurrent uninit has already cleared
1869 * the pVirtualBox reference, see #uninit(). */
1870 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1871
1872 /* we access children */
1873 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
1874
1875 autoCaller.add();
1876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1877
1878 MediaList children(this->i_getChildren());
1879 aChildren.resize(children.size());
1880 size_t i = 0;
1881 for (MediaList::const_iterator it = children.begin(); it != children.end(); ++it, ++i)
1882 (*it).queryInterfaceTo(aChildren[i].asOutParam());
1883 return S_OK;
1884}
1885
1886HRESULT Medium::getBase(AutoCaller &autoCaller, ComPtr<IMedium> &aBase)
1887{
1888 autoCaller.release();
1889
1890 /* i_getBase() will do callers/locking */
1891 i_getBase().queryInterfaceTo(aBase.asOutParam());
1892
1893 return S_OK;
1894}
1895
1896HRESULT Medium::getReadOnly(AutoCaller &autoCaller, BOOL *aReadOnly)
1897{
1898 autoCaller.release();
1899
1900 /* isReadOnly() will do locking */
1901 *aReadOnly = i_isReadOnly();
1902
1903 return S_OK;
1904}
1905
1906HRESULT Medium::getLogicalSize(LONG64 *aLogicalSize)
1907{
1908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1909
1910 *aLogicalSize = m->logicalSize;
1911
1912 return S_OK;
1913}
1914
1915HRESULT Medium::getAutoReset(BOOL *aAutoReset)
1916{
1917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1918
1919 if (m->pParent.isNull())
1920 *aAutoReset = FALSE;
1921 else
1922 *aAutoReset = m->autoReset;
1923
1924 return S_OK;
1925}
1926
1927HRESULT Medium::setAutoReset(BOOL aAutoReset)
1928{
1929 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1930
1931 if (m->pParent.isNull())
1932 return setError(VBOX_E_NOT_SUPPORTED,
1933 tr("Medium '%s' is not differencing"),
1934 m->strLocationFull.c_str());
1935
1936 if (m->autoReset != !!aAutoReset)
1937 {
1938 m->autoReset = !!aAutoReset;
1939
1940 // save the settings
1941 mlock.release();
1942 i_markRegistriesModified();
1943 m->pVirtualBox->i_saveModifiedRegistries();
1944 m->pVirtualBox->i_onMediumConfigChanged(this);
1945 }
1946
1947 return S_OK;
1948}
1949
1950HRESULT Medium::getLastAccessError(com::Utf8Str &aLastAccessError)
1951{
1952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1953
1954 aLastAccessError = m->strLastAccessError;
1955
1956 return S_OK;
1957}
1958
1959HRESULT Medium::getMachineIds(std::vector<com::Guid> &aMachineIds)
1960{
1961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1962
1963 if (m->backRefs.size() != 0)
1964 {
1965 BackRefList brlist(m->backRefs);
1966 aMachineIds.resize(brlist.size());
1967 size_t i = 0;
1968 for (BackRefList::const_iterator it = brlist.begin(); it != brlist.end(); ++it, ++i)
1969 aMachineIds[i] = it->machineId;
1970 }
1971
1972 return S_OK;
1973}
1974
1975HRESULT Medium::setIds(AutoCaller &autoCaller,
1976 BOOL aSetImageId,
1977 const com::Guid &aImageId,
1978 BOOL aSetParentId,
1979 const com::Guid &aParentId)
1980{
1981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1982
1983 switch (m->state)
1984 {
1985 case MediumState_Created:
1986 break;
1987 default:
1988 return i_setStateError();
1989 }
1990
1991 Guid imageId, parentId;
1992 if (aSetImageId)
1993 {
1994 if (aImageId.isZero())
1995 imageId.create();
1996 else
1997 {
1998 imageId = aImageId;
1999 if (!imageId.isValid())
2000 return setError(E_INVALIDARG, tr("Argument %s is invalid"), "aImageId");
2001 }
2002 }
2003 if (aSetParentId)
2004 {
2005 if (aParentId.isZero())
2006 parentId.create();
2007 else
2008 parentId = aParentId;
2009 }
2010
2011 const Guid uPrevImage = m->uuidImage;
2012 unconst(m->uuidImage) = imageId;
2013 ComObjPtr<Medium> pPrevParent = i_getParent();
2014 unconst(m->uuidParentImage) = parentId;
2015
2016 // must not hold any locks before calling Medium::i_queryInfo
2017 alock.release();
2018
2019 HRESULT rc = i_queryInfo(!!aSetImageId /* fSetImageId */,
2020 !!aSetParentId /* fSetParentId */,
2021 autoCaller);
2022
2023 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
2024 const Guid uCurrImage = m->uuidImage;
2025 ComObjPtr<Medium> pCurrParent = i_getParent();
2026 arlock.release();
2027
2028 if (SUCCEEDED(rc))
2029 {
2030 if (uCurrImage != uPrevImage)
2031 m->pVirtualBox->i_onMediumConfigChanged(this);
2032 if (pPrevParent != pCurrParent)
2033 {
2034 if (pPrevParent)
2035 m->pVirtualBox->i_onMediumConfigChanged(pPrevParent);
2036 if (pCurrParent)
2037 m->pVirtualBox->i_onMediumConfigChanged(pCurrParent);
2038 }
2039 }
2040
2041 return rc;
2042}
2043
2044HRESULT Medium::refreshState(AutoCaller &autoCaller, MediumState_T *aState)
2045{
2046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2047
2048 HRESULT rc = S_OK;
2049
2050 switch (m->state)
2051 {
2052 case MediumState_Created:
2053 case MediumState_Inaccessible:
2054 case MediumState_LockedRead:
2055 {
2056 // must not hold any locks before calling Medium::i_queryInfo
2057 alock.release();
2058
2059 rc = i_queryInfo(false /* fSetImageId */, false /* fSetParentId */,
2060 autoCaller);
2061
2062 alock.acquire();
2063 break;
2064 }
2065 default:
2066 break;
2067 }
2068
2069 *aState = m->state;
2070
2071 return rc;
2072}
2073
2074HRESULT Medium::getSnapshotIds(const com::Guid &aMachineId,
2075 std::vector<com::Guid> &aSnapshotIds)
2076{
2077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2078
2079 for (BackRefList::const_iterator it = m->backRefs.begin();
2080 it != m->backRefs.end(); ++it)
2081 {
2082 if (it->machineId == aMachineId)
2083 {
2084 size_t size = it->llSnapshotIds.size();
2085
2086 /* if the medium is attached to the machine in the current state, we
2087 * return its ID as the first element of the array */
2088 if (it->fInCurState)
2089 ++size;
2090
2091 if (size > 0)
2092 {
2093 aSnapshotIds.resize(size);
2094
2095 size_t j = 0;
2096 if (it->fInCurState)
2097 aSnapshotIds[j++] = it->machineId.toUtf16();
2098
2099 for(GuidList::const_iterator jt = it->llSnapshotIds.begin(); jt != it->llSnapshotIds.end(); ++jt, ++j)
2100 aSnapshotIds[j] = (*jt);
2101 }
2102
2103 break;
2104 }
2105 }
2106
2107 return S_OK;
2108}
2109
2110HRESULT Medium::lockRead(ComPtr<IToken> &aToken)
2111{
2112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2113
2114 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
2115 if (m->queryInfoRunning)
2116 {
2117 /* Must not hold the media tree lock, as Medium::i_queryInfo needs this
2118 * lock and thus we would run into a deadlock here. */
2119 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2120 while (m->queryInfoRunning)
2121 {
2122 alock.release();
2123 /* must not hold the object lock now */
2124 Assert(!isWriteLockOnCurrentThread());
2125 {
2126 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
2127 }
2128 alock.acquire();
2129 }
2130 }
2131
2132 HRESULT rc = S_OK;
2133
2134 switch (m->state)
2135 {
2136 case MediumState_Created:
2137 case MediumState_Inaccessible:
2138 case MediumState_LockedRead:
2139 {
2140 ++m->readers;
2141
2142 ComAssertMsgBreak(m->readers != 0, ("Counter overflow"), rc = E_FAIL);
2143
2144 /* Remember pre-lock state */
2145 if (m->state != MediumState_LockedRead)
2146 m->preLockState = m->state;
2147
2148 LogFlowThisFunc(("Okay - prev state=%d readers=%d\n", m->state, m->readers));
2149 m->state = MediumState_LockedRead;
2150
2151 ComObjPtr<MediumLockToken> pToken;
2152 rc = pToken.createObject();
2153 if (SUCCEEDED(rc))
2154 rc = pToken->init(this, false /* fWrite */);
2155 if (FAILED(rc))
2156 {
2157 --m->readers;
2158 if (m->readers == 0)
2159 m->state = m->preLockState;
2160 return rc;
2161 }
2162
2163 pToken.queryInterfaceTo(aToken.asOutParam());
2164 break;
2165 }
2166 default:
2167 {
2168 LogFlowThisFunc(("Failing - state=%d\n", m->state));
2169 rc = i_setStateError();
2170 break;
2171 }
2172 }
2173
2174 return rc;
2175}
2176
2177/**
2178 * @note @a aState may be NULL if the state value is not needed (only for
2179 * in-process calls).
2180 */
2181HRESULT Medium::i_unlockRead(MediumState_T *aState)
2182{
2183 AutoCaller autoCaller(this);
2184 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2185
2186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2187
2188 HRESULT rc = S_OK;
2189
2190 switch (m->state)
2191 {
2192 case MediumState_LockedRead:
2193 {
2194 ComAssertMsgBreak(m->readers != 0, ("Counter underflow"), rc = E_FAIL);
2195 --m->readers;
2196
2197 /* Reset the state after the last reader */
2198 if (m->readers == 0)
2199 {
2200 m->state = m->preLockState;
2201 /* There are cases where we inject the deleting state into
2202 * a medium locked for reading. Make sure #unmarkForDeletion()
2203 * gets the right state afterwards. */
2204 if (m->preLockState == MediumState_Deleting)
2205 m->preLockState = MediumState_Created;
2206 }
2207
2208 LogFlowThisFunc(("new state=%d\n", m->state));
2209 break;
2210 }
2211 default:
2212 {
2213 LogFlowThisFunc(("Failing - state=%d\n", m->state));
2214 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
2215 tr("Medium '%s' is not locked for reading"),
2216 m->strLocationFull.c_str());
2217 break;
2218 }
2219 }
2220
2221 /* return the current state after */
2222 if (aState)
2223 *aState = m->state;
2224
2225 return rc;
2226}
2227HRESULT Medium::lockWrite(ComPtr<IToken> &aToken)
2228{
2229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2230
2231 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
2232 if (m->queryInfoRunning)
2233 {
2234 /* Must not hold the media tree lock, as Medium::i_queryInfo needs this
2235 * lock and thus we would run into a deadlock here. */
2236 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2237 while (m->queryInfoRunning)
2238 {
2239 alock.release();
2240 /* must not hold the object lock now */
2241 Assert(!isWriteLockOnCurrentThread());
2242 {
2243 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
2244 }
2245 alock.acquire();
2246 }
2247 }
2248
2249 HRESULT rc = S_OK;
2250
2251 switch (m->state)
2252 {
2253 case MediumState_Created:
2254 case MediumState_Inaccessible:
2255 {
2256 m->preLockState = m->state;
2257
2258 LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2259 m->state = MediumState_LockedWrite;
2260
2261 ComObjPtr<MediumLockToken> pToken;
2262 rc = pToken.createObject();
2263 if (SUCCEEDED(rc))
2264 rc = pToken->init(this, true /* fWrite */);
2265 if (FAILED(rc))
2266 {
2267 m->state = m->preLockState;
2268 return rc;
2269 }
2270
2271 pToken.queryInterfaceTo(aToken.asOutParam());
2272 break;
2273 }
2274 default:
2275 {
2276 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2277 rc = i_setStateError();
2278 break;
2279 }
2280 }
2281
2282 return rc;
2283}
2284
2285/**
2286 * @note @a aState may be NULL if the state value is not needed (only for
2287 * in-process calls).
2288 */
2289HRESULT Medium::i_unlockWrite(MediumState_T *aState)
2290{
2291 AutoCaller autoCaller(this);
2292 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2293
2294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2295
2296 HRESULT rc = S_OK;
2297
2298 switch (m->state)
2299 {
2300 case MediumState_LockedWrite:
2301 {
2302 m->state = m->preLockState;
2303 /* There are cases where we inject the deleting state into
2304 * a medium locked for writing. Make sure #unmarkForDeletion()
2305 * gets the right state afterwards. */
2306 if (m->preLockState == MediumState_Deleting)
2307 m->preLockState = MediumState_Created;
2308 LogFlowThisFunc(("new state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2309 break;
2310 }
2311 default:
2312 {
2313 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2314 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
2315 tr("Medium '%s' is not locked for writing"),
2316 m->strLocationFull.c_str());
2317 break;
2318 }
2319 }
2320
2321 /* return the current state after */
2322 if (aState)
2323 *aState = m->state;
2324
2325 return rc;
2326}
2327
2328HRESULT Medium::close(AutoCaller &aAutoCaller)
2329{
2330 // make a copy of VirtualBox pointer which gets nulled by uninit()
2331 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
2332
2333 Guid uId = i_getId();
2334 DeviceType_T devType = i_getDeviceType();
2335 MultiResult mrc = i_close(aAutoCaller);
2336
2337 pVirtualBox->i_saveModifiedRegistries();
2338
2339 if (SUCCEEDED(mrc) && uId.isValid() && !uId.isZero())
2340 pVirtualBox->i_onMediumRegistered(uId, devType, FALSE);
2341
2342 return mrc;
2343}
2344
2345HRESULT Medium::getProperty(const com::Utf8Str &aName,
2346 com::Utf8Str &aValue)
2347{
2348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2349
2350 settings::StringsMap::const_iterator it = m->mapProperties.find(aName);
2351 if (it == m->mapProperties.end())
2352 {
2353 if (!aName.startsWith("Special/"))
2354 return setError(VBOX_E_OBJECT_NOT_FOUND,
2355 tr("Property '%s' does not exist"), aName.c_str());
2356 else
2357 /* be more silent here */
2358 return VBOX_E_OBJECT_NOT_FOUND;
2359 }
2360
2361 aValue = it->second;
2362
2363 return S_OK;
2364}
2365
2366HRESULT Medium::setProperty(const com::Utf8Str &aName,
2367 const com::Utf8Str &aValue)
2368{
2369 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2370
2371 switch (m->state)
2372 {
2373 case MediumState_NotCreated:
2374 case MediumState_Created:
2375 case MediumState_Inaccessible:
2376 break;
2377 default:
2378 return i_setStateError();
2379 }
2380
2381 settings::StringsMap::iterator it = m->mapProperties.find(aName);
2382 if ( !aName.startsWith("Special/")
2383 && !i_isPropertyForFilter(aName))
2384 {
2385 if (it == m->mapProperties.end())
2386 return setError(VBOX_E_OBJECT_NOT_FOUND,
2387 tr("Property '%s' does not exist"),
2388 aName.c_str());
2389 it->second = aValue;
2390 }
2391 else
2392 {
2393 if (it == m->mapProperties.end())
2394 {
2395 if (!aValue.isEmpty())
2396 m->mapProperties[aName] = aValue;
2397 }
2398 else
2399 {
2400 if (!aValue.isEmpty())
2401 it->second = aValue;
2402 else
2403 m->mapProperties.erase(it);
2404 }
2405 }
2406
2407 // save the settings
2408 mlock.release();
2409 i_markRegistriesModified();
2410 m->pVirtualBox->i_saveModifiedRegistries();
2411 m->pVirtualBox->i_onMediumConfigChanged(this);
2412
2413 return S_OK;
2414}
2415
2416HRESULT Medium::getProperties(const com::Utf8Str &aNames,
2417 std::vector<com::Utf8Str> &aReturnNames,
2418 std::vector<com::Utf8Str> &aReturnValues)
2419{
2420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2421
2422 /// @todo make use of aNames according to the documentation
2423 NOREF(aNames);
2424
2425 aReturnNames.resize(m->mapProperties.size());
2426 aReturnValues.resize(m->mapProperties.size());
2427 size_t i = 0;
2428 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
2429 it != m->mapProperties.end();
2430 ++it, ++i)
2431 {
2432 aReturnNames[i] = it->first;
2433 aReturnValues[i] = it->second;
2434 }
2435 return S_OK;
2436}
2437
2438HRESULT Medium::setProperties(const std::vector<com::Utf8Str> &aNames,
2439 const std::vector<com::Utf8Str> &aValues)
2440{
2441 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 /* first pass: validate names */
2444 for (size_t i = 0;
2445 i < aNames.size();
2446 ++i)
2447 {
2448 Utf8Str strName(aNames[i]);
2449 if ( !strName.startsWith("Special/")
2450 && !i_isPropertyForFilter(strName)
2451 && m->mapProperties.find(strName) == m->mapProperties.end())
2452 return setError(VBOX_E_OBJECT_NOT_FOUND,
2453 tr("Property '%s' does not exist"), strName.c_str());
2454 }
2455
2456 /* second pass: assign */
2457 for (size_t i = 0;
2458 i < aNames.size();
2459 ++i)
2460 {
2461 Utf8Str strName(aNames[i]);
2462 Utf8Str strValue(aValues[i]);
2463 settings::StringsMap::iterator it = m->mapProperties.find(strName);
2464 if ( !strName.startsWith("Special/")
2465 && !i_isPropertyForFilter(strName))
2466 {
2467 AssertReturn(it != m->mapProperties.end(), E_FAIL);
2468 it->second = strValue;
2469 }
2470 else
2471 {
2472 if (it == m->mapProperties.end())
2473 {
2474 if (!strValue.isEmpty())
2475 m->mapProperties[strName] = strValue;
2476 }
2477 else
2478 {
2479 if (!strValue.isEmpty())
2480 it->second = strValue;
2481 else
2482 m->mapProperties.erase(it);
2483 }
2484 }
2485 }
2486
2487 // save the settings
2488 mlock.release();
2489 i_markRegistriesModified();
2490 m->pVirtualBox->i_saveModifiedRegistries();
2491 m->pVirtualBox->i_onMediumConfigChanged(this);
2492
2493 return S_OK;
2494}
2495
2496HRESULT Medium::createBaseStorage(LONG64 aLogicalSize,
2497 const std::vector<MediumVariant_T> &aVariant,
2498 ComPtr<IProgress> &aProgress)
2499{
2500 if (aLogicalSize < 0)
2501 return setError(E_INVALIDARG, tr("The medium size argument (%lld) is negative"), aLogicalSize);
2502
2503 HRESULT rc = S_OK;
2504 ComObjPtr<Progress> pProgress;
2505 Medium::Task *pTask = NULL;
2506
2507 try
2508 {
2509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2510
2511 ULONG mediumVariantFlags = 0;
2512
2513 if (aVariant.size())
2514 {
2515 for (size_t i = 0; i < aVariant.size(); i++)
2516 mediumVariantFlags |= (ULONG)aVariant[i];
2517 }
2518
2519 mediumVariantFlags &= ((unsigned)~MediumVariant_Diff);
2520
2521 if ( !(mediumVariantFlags & MediumVariant_Fixed)
2522 && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateDynamic))
2523 throw setError(VBOX_E_NOT_SUPPORTED,
2524 tr("Medium format '%s' does not support dynamic storage creation"),
2525 m->strFormat.c_str());
2526
2527 if ( (mediumVariantFlags & MediumVariant_Fixed)
2528 && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateFixed))
2529 throw setError(VBOX_E_NOT_SUPPORTED,
2530 tr("Medium format '%s' does not support fixed storage creation"),
2531 m->strFormat.c_str());
2532
2533 if ( (mediumVariantFlags & MediumVariant_Formatted)
2534 && i_getDeviceType() != DeviceType_Floppy)
2535 throw setError(VBOX_E_NOT_SUPPORTED,
2536 tr("Medium variant 'formatted' applies to floppy images only"));
2537
2538 if (m->state != MediumState_NotCreated)
2539 throw i_setStateError();
2540
2541 pProgress.createObject();
2542 rc = pProgress->init(m->pVirtualBox,
2543 static_cast<IMedium*>(this),
2544 (mediumVariantFlags & MediumVariant_Fixed)
2545 ? BstrFmt(tr("Creating fixed medium storage unit '%s'"), m->strLocationFull.c_str()).raw()
2546 : BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
2547 TRUE /* aCancelable */);
2548 if (FAILED(rc))
2549 throw rc;
2550
2551 /* setup task object to carry out the operation asynchronously */
2552 pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize,
2553 (MediumVariant_T)mediumVariantFlags);
2554 rc = pTask->rc();
2555 AssertComRC(rc);
2556 if (FAILED(rc))
2557 throw rc;
2558
2559 m->state = MediumState_Creating;
2560 }
2561 catch (HRESULT aRC) { rc = aRC; }
2562
2563 if (SUCCEEDED(rc))
2564 {
2565 rc = pTask->createThread();
2566 pTask = NULL;
2567
2568 if (SUCCEEDED(rc))
2569 pProgress.queryInterfaceTo(aProgress.asOutParam());
2570 }
2571 else if (pTask != NULL)
2572 delete pTask;
2573
2574 return rc;
2575}
2576
2577HRESULT Medium::deleteStorage(ComPtr<IProgress> &aProgress)
2578{
2579 ComObjPtr<Progress> pProgress;
2580
2581 MultiResult mrc = i_deleteStorage(&pProgress,
2582 false /* aWait */,
2583 true /* aNotify */);
2584 /* Must save the registries in any case, since an entry was removed. */
2585 m->pVirtualBox->i_saveModifiedRegistries();
2586
2587 if (SUCCEEDED(mrc))
2588 pProgress.queryInterfaceTo(aProgress.asOutParam());
2589
2590 return mrc;
2591}
2592
2593HRESULT Medium::createDiffStorage(AutoCaller &autoCaller,
2594 const ComPtr<IMedium> &aTarget,
2595 const std::vector<MediumVariant_T> &aVariant,
2596 ComPtr<IProgress> &aProgress)
2597{
2598 IMedium *aT = aTarget;
2599 ComObjPtr<Medium> diff = static_cast<Medium*>(aT);
2600
2601 autoCaller.release();
2602
2603 /* It is possible that some previous/concurrent uninit has already cleared
2604 * the pVirtualBox reference, see #uninit(). */
2605 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
2606
2607 // we access m->pParent
2608 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
2609
2610 autoCaller.add();
2611 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2612
2613 AutoMultiWriteLock2 alock(this, diff COMMA_LOCKVAL_SRC_POS);
2614
2615 if (m->type == MediumType_Writethrough)
2616 return setError(VBOX_E_INVALID_OBJECT_STATE,
2617 tr("Medium type of '%s' is Writethrough"),
2618 m->strLocationFull.c_str());
2619 else if (m->type == MediumType_Shareable)
2620 return setError(VBOX_E_INVALID_OBJECT_STATE,
2621 tr("Medium type of '%s' is Shareable"),
2622 m->strLocationFull.c_str());
2623 else if (m->type == MediumType_Readonly)
2624 return setError(VBOX_E_INVALID_OBJECT_STATE,
2625 tr("Medium type of '%s' is Readonly"),
2626 m->strLocationFull.c_str());
2627
2628 /* Apply the normal locking logic to the entire chain. */
2629 MediumLockList *pMediumLockList(new MediumLockList());
2630 alock.release();
2631 autoCaller.release();
2632 treeLock.release();
2633 HRESULT rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
2634 diff /* pToLockWrite */,
2635 false /* fMediumLockWriteAll */,
2636 this,
2637 *pMediumLockList);
2638 treeLock.acquire();
2639 autoCaller.add();
2640 if (FAILED(autoCaller.rc()))
2641 rc = autoCaller.rc();
2642 alock.acquire();
2643 if (FAILED(rc))
2644 {
2645 delete pMediumLockList;
2646 return rc;
2647 }
2648
2649 alock.release();
2650 autoCaller.release();
2651 treeLock.release();
2652 rc = pMediumLockList->Lock();
2653 treeLock.acquire();
2654 autoCaller.add();
2655 if (FAILED(autoCaller.rc()))
2656 rc = autoCaller.rc();
2657 alock.acquire();
2658 if (FAILED(rc))
2659 {
2660 delete pMediumLockList;
2661
2662 return setError(rc, tr("Could not lock medium when creating diff '%s'"),
2663 diff->i_getLocationFull().c_str());
2664 }
2665
2666 Guid parentMachineRegistry;
2667 if (i_getFirstRegistryMachineId(parentMachineRegistry))
2668 {
2669 /* since this medium has been just created it isn't associated yet */
2670 diff->m->llRegistryIDs.push_back(parentMachineRegistry);
2671 alock.release();
2672 autoCaller.release();
2673 treeLock.release();
2674 diff->i_markRegistriesModified();
2675 treeLock.acquire();
2676 autoCaller.add();
2677 alock.acquire();
2678 }
2679
2680 alock.release();
2681 autoCaller.release();
2682 treeLock.release();
2683
2684 ComObjPtr<Progress> pProgress;
2685
2686 ULONG mediumVariantFlags = 0;
2687
2688 if (aVariant.size())
2689 {
2690 for (size_t i = 0; i < aVariant.size(); i++)
2691 mediumVariantFlags |= (ULONG)aVariant[i];
2692 }
2693
2694 if (mediumVariantFlags & MediumVariant_Formatted)
2695 {
2696 delete pMediumLockList;
2697 return setError(VBOX_E_NOT_SUPPORTED,
2698 tr("Medium variant 'formatted' applies to floppy images only"));
2699 }
2700
2701 rc = i_createDiffStorage(diff, (MediumVariant_T)mediumVariantFlags, pMediumLockList,
2702 &pProgress, false /* aWait */, true /* aNotify */);
2703 if (FAILED(rc))
2704 delete pMediumLockList;
2705 else
2706 pProgress.queryInterfaceTo(aProgress.asOutParam());
2707
2708 return rc;
2709}
2710
2711HRESULT Medium::mergeTo(const ComPtr<IMedium> &aTarget,
2712 ComPtr<IProgress> &aProgress)
2713{
2714 IMedium *aT = aTarget;
2715
2716 ComAssertRet(aT != this, E_INVALIDARG);
2717
2718 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aT);
2719
2720 bool fMergeForward = false;
2721 ComObjPtr<Medium> pParentForTarget;
2722 MediumLockList *pChildrenToReparent = NULL;
2723 MediumLockList *pMediumLockList = NULL;
2724
2725 HRESULT rc = S_OK;
2726
2727 rc = i_prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward,
2728 pParentForTarget, pChildrenToReparent, pMediumLockList);
2729 if (FAILED(rc)) return rc;
2730
2731 ComObjPtr<Progress> pProgress;
2732
2733 rc = i_mergeTo(pTarget, fMergeForward, pParentForTarget, pChildrenToReparent,
2734 pMediumLockList, &pProgress, false /* aWait */, true /* aNotify */);
2735 if (FAILED(rc))
2736 i_cancelMergeTo(pChildrenToReparent, pMediumLockList);
2737 else
2738 pProgress.queryInterfaceTo(aProgress.asOutParam());
2739
2740 return rc;
2741}
2742
2743HRESULT Medium::cloneToBase(const ComPtr<IMedium> &aTarget,
2744 const std::vector<MediumVariant_T> &aVariant,
2745 ComPtr<IProgress> &aProgress)
2746{
2747 int rc = S_OK;
2748
2749 rc = cloneTo(aTarget, aVariant, NULL, aProgress);
2750 return rc;
2751}
2752
2753HRESULT Medium::cloneTo(const ComPtr<IMedium> &aTarget,
2754 const std::vector<MediumVariant_T> &aVariant,
2755 const ComPtr<IMedium> &aParent,
2756 ComPtr<IProgress> &aProgress)
2757{
2758 /** @todo r=klaus The code below needs to be double checked with regard
2759 * to lock order violations, it probably causes lock order issues related
2760 * to the AutoCaller usage. */
2761 ComAssertRet(aTarget != this, E_INVALIDARG);
2762
2763 IMedium *aT = aTarget;
2764 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aT);
2765 ComObjPtr<Medium> pParent;
2766 if (aParent)
2767 {
2768 IMedium *aP = aParent;
2769 pParent = static_cast<Medium*>(aP);
2770 }
2771
2772 HRESULT rc = S_OK;
2773 ComObjPtr<Progress> pProgress;
2774 Medium::Task *pTask = NULL;
2775
2776 try
2777 {
2778 // locking: we need the tree lock first because we access parent pointers
2779 // and we need to write-lock the media involved
2780 uint32_t cHandles = 3;
2781 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
2782 this->lockHandle(),
2783 pTarget->lockHandle() };
2784 /* Only add parent to the lock if it is not null */
2785 if (!pParent.isNull())
2786 pHandles[cHandles++] = pParent->lockHandle();
2787 AutoWriteLock alock(cHandles,
2788 pHandles
2789 COMMA_LOCKVAL_SRC_POS);
2790
2791 if ( pTarget->m->state != MediumState_NotCreated
2792 && pTarget->m->state != MediumState_Created)
2793 throw pTarget->i_setStateError();
2794
2795 /* Build the source lock list. */
2796 MediumLockList *pSourceMediumLockList(new MediumLockList());
2797 alock.release();
2798 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
2799 NULL /* pToLockWrite */,
2800 false /* fMediumLockWriteAll */,
2801 NULL,
2802 *pSourceMediumLockList);
2803 alock.acquire();
2804 if (FAILED(rc))
2805 {
2806 delete pSourceMediumLockList;
2807 throw rc;
2808 }
2809
2810 /* Build the target lock list (including the to-be parent chain). */
2811 MediumLockList *pTargetMediumLockList(new MediumLockList());
2812 alock.release();
2813 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
2814 pTarget /* pToLockWrite */,
2815 false /* fMediumLockWriteAll */,
2816 pParent,
2817 *pTargetMediumLockList);
2818 alock.acquire();
2819 if (FAILED(rc))
2820 {
2821 delete pSourceMediumLockList;
2822 delete pTargetMediumLockList;
2823 throw rc;
2824 }
2825
2826 alock.release();
2827 rc = pSourceMediumLockList->Lock();
2828 alock.acquire();
2829 if (FAILED(rc))
2830 {
2831 delete pSourceMediumLockList;
2832 delete pTargetMediumLockList;
2833 throw setError(rc,
2834 tr("Failed to lock source media '%s'"),
2835 i_getLocationFull().c_str());
2836 }
2837 alock.release();
2838 rc = pTargetMediumLockList->Lock();
2839 alock.acquire();
2840 if (FAILED(rc))
2841 {
2842 delete pSourceMediumLockList;
2843 delete pTargetMediumLockList;
2844 throw setError(rc,
2845 tr("Failed to lock target media '%s'"),
2846 pTarget->i_getLocationFull().c_str());
2847 }
2848
2849 pProgress.createObject();
2850 rc = pProgress->init(m->pVirtualBox,
2851 static_cast <IMedium *>(this),
2852 BstrFmt(tr("Creating clone medium '%s'"), pTarget->m->strLocationFull.c_str()).raw(),
2853 TRUE /* aCancelable */);
2854 if (FAILED(rc))
2855 {
2856 delete pSourceMediumLockList;
2857 delete pTargetMediumLockList;
2858 throw rc;
2859 }
2860
2861 ULONG mediumVariantFlags = 0;
2862
2863 if (aVariant.size())
2864 {
2865 for (size_t i = 0; i < aVariant.size(); i++)
2866 mediumVariantFlags |= (ULONG)aVariant[i];
2867 }
2868
2869 if (mediumVariantFlags & MediumVariant_Formatted)
2870 {
2871 delete pSourceMediumLockList;
2872 delete pTargetMediumLockList;
2873 throw setError(VBOX_E_NOT_SUPPORTED,
2874 tr("Medium variant 'formatted' applies to floppy images only"));
2875 }
2876
2877 /* setup task object to carry out the operation asynchronously */
2878 pTask = new Medium::CloneTask(this, pProgress, pTarget,
2879 (MediumVariant_T)mediumVariantFlags,
2880 pParent, UINT32_MAX, UINT32_MAX,
2881 pSourceMediumLockList, pTargetMediumLockList);
2882 rc = pTask->rc();
2883 AssertComRC(rc);
2884 if (FAILED(rc))
2885 throw rc;
2886
2887 if (pTarget->m->state == MediumState_NotCreated)
2888 pTarget->m->state = MediumState_Creating;
2889 }
2890 catch (HRESULT aRC) { rc = aRC; }
2891
2892 if (SUCCEEDED(rc))
2893 {
2894 rc = pTask->createThread();
2895 pTask = NULL;
2896 if (SUCCEEDED(rc))
2897 pProgress.queryInterfaceTo(aProgress.asOutParam());
2898 }
2899 else if (pTask != NULL)
2900 delete pTask;
2901
2902 return rc;
2903}
2904
2905HRESULT Medium::moveTo(AutoCaller &autoCaller, const com::Utf8Str &aLocation, ComPtr<IProgress> &aProgress)
2906{
2907 ComObjPtr<Medium> pParent;
2908 ComObjPtr<Progress> pProgress;
2909 HRESULT rc = S_OK;
2910 Medium::Task *pTask = NULL;
2911
2912 try
2913 {
2914 /// @todo NEWMEDIA for file names, add the default extension if no extension
2915 /// is present (using the information from the VD backend which also implies
2916 /// that one more parameter should be passed to moveTo() requesting
2917 /// that functionality since it is only allowed when called from this method
2918
2919 /// @todo NEWMEDIA rename the file and set m->location on success, then save
2920 /// the global registry (and local registries of portable VMs referring to
2921 /// this medium), this will also require to add the mRegistered flag to data
2922
2923 autoCaller.release();
2924
2925 // locking: we need the tree lock first because we access parent pointers
2926 // and we need to write-lock the media involved
2927 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2928
2929 autoCaller.add();
2930 AssertComRCThrowRC(autoCaller.rc());
2931
2932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2933
2934 /* play with locations */
2935 {
2936 /* get source path and filename */
2937 Utf8Str sourcePath = i_getLocationFull();
2938 Utf8Str sourceFName = i_getName();
2939
2940 if (aLocation.isEmpty())
2941 {
2942 rc = setError(VERR_PATH_ZERO_LENGTH,
2943 tr("Medium '%s' can't be moved. Destination path is empty."),
2944 i_getLocationFull().c_str());
2945 throw rc;
2946 }
2947
2948 /* extract destination path and filename */
2949 Utf8Str destPath(aLocation);
2950 Utf8Str destFName(destPath);
2951 destFName.stripPath();
2952
2953 Utf8Str suffix(destFName);
2954 suffix.stripSuffix();
2955
2956 if (suffix.equals(destFName) && !destFName.isEmpty())
2957 {
2958 /*
2959 * The target path has no filename: Either "/path/to/new/location" or
2960 * just "newname" (no trailing backslash or there is no filename with
2961 * extension(suffix)).
2962 */
2963 if (destPath.equals(destFName))
2964 {
2965 /* new path contains only "newname", no path, no extension */
2966 destFName.append(RTPathSuffix(sourceFName.c_str()));
2967 destPath = destFName;
2968 }
2969 else
2970 {
2971 /* new path looks like "/path/to/new/location" */
2972 destFName.setNull();
2973 destPath.append(RTPATH_SLASH);
2974 }
2975 }
2976
2977 if (destFName.isEmpty())
2978 {
2979 /* No target name */
2980 destPath.append(sourceFName);
2981 }
2982 else
2983 {
2984 if (destPath.equals(destFName))
2985 {
2986 /*
2987 * The target path contains of only a filename without a directory.
2988 * Move the medium within the source directory to the new name
2989 * (actually rename operation).
2990 * Scratches sourcePath!
2991 */
2992 destPath = sourcePath.stripFilename().append(RTPATH_SLASH).append(destFName);
2993 }
2994
2995 suffix = RTPathSuffix(sourceFName.c_str());
2996
2997 /* Suffix is empty and one is deduced from the medium format */
2998 if (suffix.isEmpty())
2999 {
3000 suffix = i_getFormat();
3001 if (suffix.compare("RAW", Utf8Str::CaseInsensitive) == 0)
3002 {
3003 DeviceType_T devType = i_getDeviceType();
3004 switch (devType)
3005 {
3006 case DeviceType_DVD:
3007 suffix = "iso";
3008 break;
3009 case DeviceType_Floppy:
3010 suffix = "img";
3011 break;
3012 default:
3013 rc = setError(VERR_NOT_A_FILE,
3014 tr("Medium '%s' has RAW type. \"Move\" operation isn't supported for this type."),
3015 i_getLocationFull().c_str());
3016 throw rc;
3017 }
3018 }
3019 else if (suffix.compare("Parallels", Utf8Str::CaseInsensitive) == 0)
3020 {
3021 suffix = "hdd";
3022 }
3023
3024 /* Set the target extension like on the source. Any conversions are prohibited */
3025 suffix.toLower();
3026 destPath.stripSuffix().append('.').append(suffix);
3027 }
3028 else
3029 destPath.stripSuffix().append(suffix);
3030 }
3031
3032 /* Simple check for existence */
3033 if (RTFileExists(destPath.c_str()))
3034 {
3035 rc = setError(VBOX_E_FILE_ERROR,
3036 tr("The given path '%s' is an existing file. Delete or rename this file."),
3037 destPath.c_str());
3038 throw rc;
3039 }
3040
3041 if (!i_isMediumFormatFile())
3042 {
3043 rc = setError(VERR_NOT_A_FILE,
3044 tr("Medium '%s' isn't a file object. \"Move\" operation isn't supported."),
3045 i_getLocationFull().c_str());
3046 throw rc;
3047 }
3048 /* Path must be absolute */
3049 if (!RTPathStartsWithRoot(destPath.c_str()))
3050 {
3051 rc = setError(VBOX_E_FILE_ERROR,
3052 tr("The given path '%s' is not fully qualified"),
3053 destPath.c_str());
3054 throw rc;
3055 }
3056 /* Check path for a new file object */
3057 rc = VirtualBox::i_ensureFilePathExists(destPath, true);
3058 if (FAILED(rc))
3059 throw rc;
3060
3061 /* Set needed variables for "moving" procedure. It'll be used later in separate thread task */
3062 rc = i_preparationForMoving(destPath);
3063 if (FAILED(rc))
3064 {
3065 rc = setError(VERR_NO_CHANGE,
3066 tr("Medium '%s' is already in the correct location"),
3067 i_getLocationFull().c_str());
3068 throw rc;
3069 }
3070 }
3071
3072 /* Check VMs which have this medium attached to*/
3073 std::vector<com::Guid> aMachineIds;
3074 rc = getMachineIds(aMachineIds);
3075 std::vector<com::Guid>::const_iterator currMachineID = aMachineIds.begin();
3076 std::vector<com::Guid>::const_iterator lastMachineID = aMachineIds.end();
3077
3078 while (currMachineID != lastMachineID)
3079 {
3080 Guid id(*currMachineID);
3081 ComObjPtr<Machine> aMachine;
3082
3083 alock.release();
3084 autoCaller.release();
3085 treeLock.release();
3086 rc = m->pVirtualBox->i_findMachine(id, false, true, &aMachine);
3087 treeLock.acquire();
3088 autoCaller.add();
3089 AssertComRCThrowRC(autoCaller.rc());
3090 alock.acquire();
3091
3092 if (SUCCEEDED(rc))
3093 {
3094 ComObjPtr<SessionMachine> sm;
3095 ComPtr<IInternalSessionControl> ctl;
3096
3097 alock.release();
3098 autoCaller.release();
3099 treeLock.release();
3100 bool ses = aMachine->i_isSessionOpenVM(sm, &ctl);
3101 treeLock.acquire();
3102 autoCaller.add();
3103 AssertComRCThrowRC(autoCaller.rc());
3104 alock.acquire();
3105
3106 if (ses)
3107 {
3108 rc = setError(VERR_VM_UNEXPECTED_VM_STATE,
3109 tr("At least the VM '%s' to whom this medium '%s' attached has currently an opened session. Stop all VMs before relocating this medium"),
3110 id.toString().c_str(),
3111 i_getLocationFull().c_str());
3112 throw rc;
3113 }
3114 }
3115 ++currMachineID;
3116 }
3117
3118 /* Build the source lock list. */
3119 MediumLockList *pMediumLockList(new MediumLockList());
3120 alock.release();
3121 autoCaller.release();
3122 treeLock.release();
3123 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
3124 this /* pToLockWrite */,
3125 true /* fMediumLockWriteAll */,
3126 NULL,
3127 *pMediumLockList);
3128 treeLock.acquire();
3129 autoCaller.add();
3130 AssertComRCThrowRC(autoCaller.rc());
3131 alock.acquire();
3132 if (FAILED(rc))
3133 {
3134 delete pMediumLockList;
3135 throw setError(rc,
3136 tr("Failed to create medium lock list for '%s'"),
3137 i_getLocationFull().c_str());
3138 }
3139 alock.release();
3140 autoCaller.release();
3141 treeLock.release();
3142 rc = pMediumLockList->Lock();
3143 treeLock.acquire();
3144 autoCaller.add();
3145 AssertComRCThrowRC(autoCaller.rc());
3146 alock.acquire();
3147 if (FAILED(rc))
3148 {
3149 delete pMediumLockList;
3150 throw setError(rc,
3151 tr("Failed to lock media '%s'"),
3152 i_getLocationFull().c_str());
3153 }
3154
3155 pProgress.createObject();
3156 rc = pProgress->init(m->pVirtualBox,
3157 static_cast <IMedium *>(this),
3158 BstrFmt(tr("Moving medium '%s'"), m->strLocationFull.c_str()).raw(),
3159 TRUE /* aCancelable */);
3160
3161 /* Do the disk moving. */
3162 if (SUCCEEDED(rc))
3163 {
3164 ULONG mediumVariantFlags = i_getVariant();
3165
3166 /* setup task object to carry out the operation asynchronously */
3167 pTask = new Medium::MoveTask(this, pProgress,
3168 (MediumVariant_T)mediumVariantFlags,
3169 pMediumLockList);
3170 rc = pTask->rc();
3171 AssertComRC(rc);
3172 if (FAILED(rc))
3173 throw rc;
3174 }
3175
3176 }
3177 catch (HRESULT aRC) { rc = aRC; }
3178
3179 if (SUCCEEDED(rc))
3180 {
3181 rc = pTask->createThread();
3182 pTask = NULL;
3183 if (SUCCEEDED(rc))
3184 pProgress.queryInterfaceTo(aProgress.asOutParam());
3185 }
3186 else
3187 {
3188 if (pTask)
3189 delete pTask;
3190 }
3191
3192 return rc;
3193}
3194
3195HRESULT Medium::setLocation(const com::Utf8Str &aLocation)
3196{
3197 HRESULT rc = S_OK;
3198
3199 try
3200 {
3201 // locking: we need the tree lock first because we access parent pointers
3202 // and we need to write-lock the media involved
3203 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3204
3205 AutoCaller autoCaller(this);
3206 AssertComRCThrowRC(autoCaller.rc());
3207
3208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3209
3210 Utf8Str destPath(aLocation);
3211
3212 // some check for file based medium
3213 if (i_isMediumFormatFile())
3214 {
3215 /* Path must be absolute */
3216 if (!RTPathStartsWithRoot(destPath.c_str()))
3217 {
3218 rc = setError(VBOX_E_FILE_ERROR,
3219 tr("The given path '%s' is not fully qualified"),
3220 destPath.c_str());
3221 throw rc;
3222 }
3223
3224 /* Simple check for existence */
3225 if (!RTFileExists(destPath.c_str()))
3226 {
3227 rc = setError(VBOX_E_FILE_ERROR,
3228 tr("The given path '%s' is not an existing file. New location is invalid."),
3229 destPath.c_str());
3230 throw rc;
3231 }
3232 }
3233
3234 /* Check VMs which have this medium attached to*/
3235 std::vector<com::Guid> aMachineIds;
3236 rc = getMachineIds(aMachineIds);
3237
3238 // switch locks only if there are machines with this medium attached
3239 if (!aMachineIds.empty())
3240 {
3241 std::vector<com::Guid>::const_iterator currMachineID = aMachineIds.begin();
3242 std::vector<com::Guid>::const_iterator lastMachineID = aMachineIds.end();
3243
3244 alock.release();
3245 autoCaller.release();
3246 treeLock.release();
3247
3248 while (currMachineID != lastMachineID)
3249 {
3250 Guid id(*currMachineID);
3251 ComObjPtr<Machine> aMachine;
3252 rc = m->pVirtualBox->i_findMachine(id, false, true, &aMachine);
3253 if (SUCCEEDED(rc))
3254 {
3255 ComObjPtr<SessionMachine> sm;
3256 ComPtr<IInternalSessionControl> ctl;
3257
3258 bool ses = aMachine->i_isSessionOpenVM(sm, &ctl);
3259 if (ses)
3260 {
3261 treeLock.acquire();
3262 autoCaller.add();
3263 AssertComRCThrowRC(autoCaller.rc());
3264 alock.acquire();
3265
3266 rc = setError(VERR_VM_UNEXPECTED_VM_STATE,
3267 tr("At least the VM '%s' to whom this medium '%s' attached has currently an opened session. Stop all VMs before set location for this medium"),
3268 id.toString().c_str(),
3269 i_getLocationFull().c_str());
3270 throw rc;
3271 }
3272 }
3273 ++currMachineID;
3274 }
3275
3276 treeLock.acquire();
3277 autoCaller.add();
3278 AssertComRCThrowRC(autoCaller.rc());
3279 alock.acquire();
3280 }
3281
3282 m->strLocationFull = destPath;
3283
3284 // save the settings
3285 alock.release();
3286 autoCaller.release();
3287 treeLock.release();
3288
3289 i_markRegistriesModified();
3290 m->pVirtualBox->i_saveModifiedRegistries();
3291
3292 MediumState_T mediumState;
3293 refreshState(autoCaller, &mediumState);
3294 m->pVirtualBox->i_onMediumConfigChanged(this);
3295 }
3296 catch (HRESULT aRC) { rc = aRC; }
3297
3298 return rc;
3299}
3300
3301HRESULT Medium::compact(ComPtr<IProgress> &aProgress)
3302{
3303 HRESULT rc = S_OK;
3304 ComObjPtr<Progress> pProgress;
3305 Medium::Task *pTask = NULL;
3306
3307 try
3308 {
3309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3310
3311 /* Build the medium lock list. */
3312 MediumLockList *pMediumLockList(new MediumLockList());
3313 alock.release();
3314 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3315 this /* pToLockWrite */,
3316 false /* fMediumLockWriteAll */,
3317 NULL,
3318 *pMediumLockList);
3319 alock.acquire();
3320 if (FAILED(rc))
3321 {
3322 delete pMediumLockList;
3323 throw rc;
3324 }
3325
3326 alock.release();
3327 rc = pMediumLockList->Lock();
3328 alock.acquire();
3329 if (FAILED(rc))
3330 {
3331 delete pMediumLockList;
3332 throw setError(rc,
3333 tr("Failed to lock media when compacting '%s'"),
3334 i_getLocationFull().c_str());
3335 }
3336
3337 pProgress.createObject();
3338 rc = pProgress->init(m->pVirtualBox,
3339 static_cast <IMedium *>(this),
3340 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
3341 TRUE /* aCancelable */);
3342 if (FAILED(rc))
3343 {
3344 delete pMediumLockList;
3345 throw rc;
3346 }
3347
3348 /* setup task object to carry out the operation asynchronously */
3349 pTask = new Medium::CompactTask(this, pProgress, pMediumLockList);
3350 rc = pTask->rc();
3351 AssertComRC(rc);
3352 if (FAILED(rc))
3353 throw rc;
3354 }
3355 catch (HRESULT aRC) { rc = aRC; }
3356
3357 if (SUCCEEDED(rc))
3358 {
3359 rc = pTask->createThread();
3360 pTask = NULL;
3361 if (SUCCEEDED(rc))
3362 pProgress.queryInterfaceTo(aProgress.asOutParam());
3363 }
3364 else if (pTask != NULL)
3365 delete pTask;
3366
3367 return rc;
3368}
3369
3370HRESULT Medium::resize(LONG64 aLogicalSize,
3371 ComPtr<IProgress> &aProgress)
3372{
3373 HRESULT rc = S_OK;
3374 ComObjPtr<Progress> pProgress;
3375
3376 /* Build the medium lock list. */
3377 MediumLockList *pMediumLockList(new MediumLockList());
3378
3379 try
3380 {
3381 const char *pszError = NULL;
3382
3383 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3384 this /* pToLockWrite */,
3385 false /* fMediumLockWriteAll */,
3386 NULL,
3387 *pMediumLockList);
3388 if (FAILED(rc))
3389 {
3390 pszError = tr("Failed to create medium lock list when resizing '%s'");
3391 }
3392 else
3393 {
3394 rc = pMediumLockList->Lock();
3395 if (FAILED(rc))
3396 pszError = tr("Failed to lock media when resizing '%s'");
3397 }
3398
3399
3400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3401
3402 if (FAILED(rc))
3403 {
3404 throw setError(rc, pszError, i_getLocationFull().c_str());
3405 }
3406
3407 pProgress.createObject();
3408 rc = pProgress->init(m->pVirtualBox,
3409 static_cast <IMedium *>(this),
3410 BstrFmt(tr("Resizing medium '%s'"), m->strLocationFull.c_str()).raw(),
3411 TRUE /* aCancelable */);
3412 if (FAILED(rc))
3413 {
3414 throw rc;
3415 }
3416 }
3417 catch (HRESULT aRC) { rc = aRC; }
3418
3419 if (SUCCEEDED(rc))
3420 rc = i_resize(aLogicalSize, pMediumLockList, &pProgress, false /* aWait */, true /* aNotify */);
3421
3422 if (SUCCEEDED(rc))
3423 pProgress.queryInterfaceTo(aProgress.asOutParam());
3424 else
3425 delete pMediumLockList;
3426
3427 return rc;
3428}
3429
3430HRESULT Medium::reset(AutoCaller &autoCaller, ComPtr<IProgress> &aProgress)
3431{
3432 HRESULT rc = S_OK;
3433 ComObjPtr<Progress> pProgress;
3434 Medium::Task *pTask = NULL;
3435
3436 try
3437 {
3438 autoCaller.release();
3439
3440 /* It is possible that some previous/concurrent uninit has already
3441 * cleared the pVirtualBox reference, see #uninit(). */
3442 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
3443
3444 /* canClose() needs the tree lock */
3445 AutoMultiWriteLock2 multilock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL,
3446 this->lockHandle()
3447 COMMA_LOCKVAL_SRC_POS);
3448
3449 autoCaller.add();
3450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3451
3452 LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
3453
3454 if (m->pParent.isNull())
3455 throw setError(VBOX_E_NOT_SUPPORTED,
3456 tr("Medium type of '%s' is not differencing"),
3457 m->strLocationFull.c_str());
3458
3459 rc = i_canClose();
3460 if (FAILED(rc))
3461 throw rc;
3462
3463 /* Build the medium lock list. */
3464 MediumLockList *pMediumLockList(new MediumLockList());
3465 multilock.release();
3466 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
3467 this /* pToLockWrite */,
3468 false /* fMediumLockWriteAll */,
3469 NULL,
3470 *pMediumLockList);
3471 multilock.acquire();
3472 if (FAILED(rc))
3473 {
3474 delete pMediumLockList;
3475 throw rc;
3476 }
3477
3478 multilock.release();
3479 rc = pMediumLockList->Lock();
3480 multilock.acquire();
3481 if (FAILED(rc))
3482 {
3483 delete pMediumLockList;
3484 throw setError(rc,
3485 tr("Failed to lock media when resetting '%s'"),
3486 i_getLocationFull().c_str());
3487 }
3488
3489 pProgress.createObject();
3490 rc = pProgress->init(m->pVirtualBox,
3491 static_cast<IMedium*>(this),
3492 BstrFmt(tr("Resetting differencing medium '%s'"), m->strLocationFull.c_str()).raw(),
3493 FALSE /* aCancelable */);
3494 if (FAILED(rc))
3495 throw rc;
3496
3497 /* setup task object to carry out the operation asynchronously */
3498 pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
3499 rc = pTask->rc();
3500 AssertComRC(rc);
3501 if (FAILED(rc))
3502 throw rc;
3503 }
3504 catch (HRESULT aRC) { rc = aRC; }
3505
3506 if (SUCCEEDED(rc))
3507 {
3508 rc = pTask->createThread();
3509 pTask = NULL;
3510 if (SUCCEEDED(rc))
3511 pProgress.queryInterfaceTo(aProgress.asOutParam());
3512 }
3513 else if (pTask != NULL)
3514 delete pTask;
3515
3516 LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
3517
3518 return rc;
3519}
3520
3521HRESULT Medium::changeEncryption(const com::Utf8Str &aCurrentPassword, const com::Utf8Str &aCipher,
3522 const com::Utf8Str &aNewPassword, const com::Utf8Str &aNewPasswordId,
3523 ComPtr<IProgress> &aProgress)
3524{
3525 HRESULT rc = S_OK;
3526 ComObjPtr<Progress> pProgress;
3527 Medium::Task *pTask = NULL;
3528
3529 try
3530 {
3531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3532
3533 DeviceType_T devType = i_getDeviceType();
3534 /* Cannot encrypt DVD or floppy images so far. */
3535 if ( devType == DeviceType_DVD
3536 || devType == DeviceType_Floppy)
3537 return setError(VBOX_E_INVALID_OBJECT_STATE,
3538 tr("Cannot encrypt DVD or Floppy medium '%s'"),
3539 m->strLocationFull.c_str());
3540
3541 /* Cannot encrypt media which are attached to more than one virtual machine. */
3542 if (m->backRefs.size() > 1)
3543 return setError(VBOX_E_INVALID_OBJECT_STATE,
3544 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines"),
3545 m->strLocationFull.c_str(), m->backRefs.size());
3546
3547 if (i_getChildren().size() != 0)
3548 return setError(VBOX_E_INVALID_OBJECT_STATE,
3549 tr("Cannot encrypt medium '%s' because it has %d children"),
3550 m->strLocationFull.c_str(), i_getChildren().size());
3551
3552 /* Build the medium lock list. */
3553 MediumLockList *pMediumLockList(new MediumLockList());
3554 alock.release();
3555 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3556 this /* pToLockWrite */,
3557 true /* fMediumLockAllWrite */,
3558 NULL,
3559 *pMediumLockList);
3560 alock.acquire();
3561 if (FAILED(rc))
3562 {
3563 delete pMediumLockList;
3564 throw rc;
3565 }
3566
3567 alock.release();
3568 rc = pMediumLockList->Lock();
3569 alock.acquire();
3570 if (FAILED(rc))
3571 {
3572 delete pMediumLockList;
3573 throw setError(rc,
3574 tr("Failed to lock media for encryption '%s'"),
3575 i_getLocationFull().c_str());
3576 }
3577
3578 /*
3579 * Check all media in the chain to not contain any branches or references to
3580 * other virtual machines, we support encrypting only a list of differencing media at the moment.
3581 */
3582 MediumLockList::Base::const_iterator mediumListBegin = pMediumLockList->GetBegin();
3583 MediumLockList::Base::const_iterator mediumListEnd = pMediumLockList->GetEnd();
3584 for (MediumLockList::Base::const_iterator it = mediumListBegin;
3585 it != mediumListEnd;
3586 ++it)
3587 {
3588 const MediumLock &mediumLock = *it;
3589 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
3590 AutoReadLock mediumReadLock(pMedium COMMA_LOCKVAL_SRC_POS);
3591
3592 Assert(pMedium->m->state == MediumState_LockedWrite);
3593
3594 if (pMedium->m->backRefs.size() > 1)
3595 {
3596 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3597 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines"),
3598 pMedium->m->strLocationFull.c_str(), pMedium->m->backRefs.size());
3599 break;
3600 }
3601 else if (pMedium->i_getChildren().size() > 1)
3602 {
3603 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3604 tr("Cannot encrypt medium '%s' because it has %d children"),
3605 pMedium->m->strLocationFull.c_str(), pMedium->i_getChildren().size());
3606 break;
3607 }
3608 }
3609
3610 if (FAILED(rc))
3611 {
3612 delete pMediumLockList;
3613 throw rc;
3614 }
3615
3616 const char *pszAction = "Encrypting";
3617 if ( aCurrentPassword.isNotEmpty()
3618 && aCipher.isEmpty())
3619 pszAction = "Decrypting";
3620
3621 pProgress.createObject();
3622 rc = pProgress->init(m->pVirtualBox,
3623 static_cast <IMedium *>(this),
3624 BstrFmt(tr("%s medium '%s'"), pszAction, m->strLocationFull.c_str()).raw(),
3625 TRUE /* aCancelable */);
3626 if (FAILED(rc))
3627 {
3628 delete pMediumLockList;
3629 throw rc;
3630 }
3631
3632 /* setup task object to carry out the operation asynchronously */
3633 pTask = new Medium::EncryptTask(this, aNewPassword, aCurrentPassword,
3634 aCipher, aNewPasswordId, pProgress, pMediumLockList);
3635 rc = pTask->rc();
3636 AssertComRC(rc);
3637 if (FAILED(rc))
3638 throw rc;
3639 }
3640 catch (HRESULT aRC) { rc = aRC; }
3641
3642 if (SUCCEEDED(rc))
3643 {
3644 rc = pTask->createThread();
3645 pTask = NULL;
3646 if (SUCCEEDED(rc))
3647 pProgress.queryInterfaceTo(aProgress.asOutParam());
3648 }
3649 else if (pTask != NULL)
3650 delete pTask;
3651
3652 return rc;
3653}
3654
3655HRESULT Medium::getEncryptionSettings(AutoCaller &autoCaller, com::Utf8Str &aCipher, com::Utf8Str &aPasswordId)
3656{
3657#ifndef VBOX_WITH_EXTPACK
3658 RT_NOREF(aCipher, aPasswordId);
3659#endif
3660 HRESULT rc = S_OK;
3661
3662 try
3663 {
3664 autoCaller.release();
3665 ComObjPtr<Medium> pBase = i_getBase();
3666 autoCaller.add();
3667 if (FAILED(autoCaller.rc()))
3668 throw rc;
3669 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3670
3671 /* Check whether encryption is configured for this medium. */
3672 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
3673 if (it == pBase->m->mapProperties.end())
3674 throw VBOX_E_NOT_SUPPORTED;
3675
3676# ifdef VBOX_WITH_EXTPACK
3677 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
3678 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
3679 {
3680 /* Load the plugin */
3681 Utf8Str strPlugin;
3682 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
3683 if (SUCCEEDED(rc))
3684 {
3685 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
3686 if (RT_FAILURE(vrc))
3687 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
3688 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
3689 i_vdError(vrc).c_str());
3690 }
3691 else
3692 throw setError(VBOX_E_NOT_SUPPORTED,
3693 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
3694 ORACLE_PUEL_EXTPACK_NAME);
3695 }
3696 else
3697 throw setError(VBOX_E_NOT_SUPPORTED,
3698 tr("Encryption is not supported because the extension pack '%s' is missing"),
3699 ORACLE_PUEL_EXTPACK_NAME);
3700
3701 PVDISK pDisk = NULL;
3702 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
3703 ComAssertRCThrow(vrc, E_FAIL);
3704
3705 MediumCryptoFilterSettings CryptoSettings;
3706
3707 i_taskEncryptSettingsSetup(&CryptoSettings, NULL, it->second.c_str(), NULL, false /* fCreateKeyStore */);
3708 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ | VD_FILTER_FLAGS_INFO, CryptoSettings.vdFilterIfaces);
3709 if (RT_FAILURE(vrc))
3710 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
3711 tr("Failed to load the encryption filter: %s"),
3712 i_vdError(vrc).c_str());
3713
3714 it = pBase->m->mapProperties.find("CRYPT/KeyId");
3715 if (it == pBase->m->mapProperties.end())
3716 throw setError(VBOX_E_INVALID_OBJECT_STATE,
3717 tr("Image is configured for encryption but doesn't has a KeyId set"));
3718
3719 aPasswordId = it->second.c_str();
3720 aCipher = CryptoSettings.pszCipherReturned;
3721 RTStrFree(CryptoSettings.pszCipherReturned);
3722
3723 VDDestroy(pDisk);
3724# else
3725 throw setError(VBOX_E_NOT_SUPPORTED,
3726 tr("Encryption is not supported because extension pack support is not built in"));
3727# endif
3728 }
3729 catch (HRESULT aRC) { rc = aRC; }
3730
3731 return rc;
3732}
3733
3734HRESULT Medium::checkEncryptionPassword(const com::Utf8Str &aPassword)
3735{
3736 HRESULT rc = S_OK;
3737
3738 try
3739 {
3740 ComObjPtr<Medium> pBase = i_getBase();
3741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3742
3743 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
3744 if (it == pBase->m->mapProperties.end())
3745 throw setError(VBOX_E_NOT_SUPPORTED,
3746 tr("The image is not configured for encryption"));
3747
3748 if (aPassword.isEmpty())
3749 throw setError(E_INVALIDARG,
3750 tr("The given password must not be empty"));
3751
3752# ifdef VBOX_WITH_EXTPACK
3753 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
3754 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
3755 {
3756 /* Load the plugin */
3757 Utf8Str strPlugin;
3758 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
3759 if (SUCCEEDED(rc))
3760 {
3761 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
3762 if (RT_FAILURE(vrc))
3763 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
3764 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
3765 i_vdError(vrc).c_str());
3766 }
3767 else
3768 throw setError(VBOX_E_NOT_SUPPORTED,
3769 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
3770 ORACLE_PUEL_EXTPACK_NAME);
3771 }
3772 else
3773 throw setError(VBOX_E_NOT_SUPPORTED,
3774 tr("Encryption is not supported because the extension pack '%s' is missing"),
3775 ORACLE_PUEL_EXTPACK_NAME);
3776
3777 PVDISK pDisk = NULL;
3778 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
3779 ComAssertRCThrow(vrc, E_FAIL);
3780
3781 MediumCryptoFilterSettings CryptoSettings;
3782
3783 i_taskEncryptSettingsSetup(&CryptoSettings, NULL, it->second.c_str(), aPassword.c_str(),
3784 false /* fCreateKeyStore */);
3785 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettings.vdFilterIfaces);
3786 if (vrc == VERR_VD_PASSWORD_INCORRECT)
3787 throw setError(VBOX_E_PASSWORD_INCORRECT,
3788 tr("The given password is incorrect"));
3789 else if (RT_FAILURE(vrc))
3790 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
3791 tr("Failed to load the encryption filter: %s"),
3792 i_vdError(vrc).c_str());
3793
3794 VDDestroy(pDisk);
3795# else
3796 throw setError(VBOX_E_NOT_SUPPORTED,
3797 tr("Encryption is not supported because extension pack support is not built in"));
3798# endif
3799 }
3800 catch (HRESULT aRC) { rc = aRC; }
3801
3802 return rc;
3803}
3804
3805HRESULT Medium::openForIO(BOOL aWritable, com::Utf8Str const &aPassword, ComPtr<IMediumIO> &aMediumIO)
3806{
3807 /*
3808 * Input validation.
3809 */
3810 if (aWritable && i_isReadOnly())
3811 return setError(E_ACCESSDENIED, tr("Write access denied: read-only"));
3812
3813 com::Utf8Str const strKeyId = i_getKeyId();
3814 if (strKeyId.isEmpty() && aPassword.isNotEmpty())
3815 return setError(E_INVALIDARG, tr("Password given for unencrypted medium"));
3816 if (strKeyId.isNotEmpty() && aPassword.isEmpty())
3817 return setError(E_INVALIDARG, tr("Password needed for encrypted medium"));
3818
3819 /*
3820 * Create IO object and return it.
3821 */
3822 ComObjPtr<MediumIO> ptrIO;
3823 HRESULT hrc = ptrIO.createObject();
3824 if (SUCCEEDED(hrc))
3825 {
3826 hrc = ptrIO->initForMedium(this, m->pVirtualBox, aWritable != FALSE, strKeyId, aPassword);
3827 if (SUCCEEDED(hrc))
3828 ptrIO.queryInterfaceTo(aMediumIO.asOutParam());
3829 }
3830 return hrc;
3831}
3832
3833
3834////////////////////////////////////////////////////////////////////////////////
3835//
3836// Medium public internal methods
3837//
3838////////////////////////////////////////////////////////////////////////////////
3839
3840/**
3841 * Internal method to return the medium's parent medium. Must have caller + locking!
3842 * @return
3843 */
3844const ComObjPtr<Medium>& Medium::i_getParent() const
3845{
3846 return m->pParent;
3847}
3848
3849/**
3850 * Internal method to return the medium's list of child media. Must have caller + locking!
3851 * @return
3852 */
3853const MediaList& Medium::i_getChildren() const
3854{
3855 return m->llChildren;
3856}
3857
3858/**
3859 * Internal method to return the medium's GUID. Must have caller + locking!
3860 * @return
3861 */
3862const Guid& Medium::i_getId() const
3863{
3864 return m->id;
3865}
3866
3867/**
3868 * Internal method to return the medium's state. Must have caller + locking!
3869 * @return
3870 */
3871MediumState_T Medium::i_getState() const
3872{
3873 return m->state;
3874}
3875
3876/**
3877 * Internal method to return the medium's variant. Must have caller + locking!
3878 * @return
3879 */
3880MediumVariant_T Medium::i_getVariant() const
3881{
3882 return m->variant;
3883}
3884
3885/**
3886 * Internal method which returns true if this medium represents a host drive.
3887 * @return
3888 */
3889bool Medium::i_isHostDrive() const
3890{
3891 return m->hostDrive;
3892}
3893
3894/**
3895 * Internal method to return the medium's full location. Must have caller + locking!
3896 * @return
3897 */
3898const Utf8Str& Medium::i_getLocationFull() const
3899{
3900 return m->strLocationFull;
3901}
3902
3903/**
3904 * Internal method to return the medium's format string. Must have caller + locking!
3905 * @return
3906 */
3907const Utf8Str& Medium::i_getFormat() const
3908{
3909 return m->strFormat;
3910}
3911
3912/**
3913 * Internal method to return the medium's format object. Must have caller + locking!
3914 * @return
3915 */
3916const ComObjPtr<MediumFormat>& Medium::i_getMediumFormat() const
3917{
3918 return m->formatObj;
3919}
3920
3921/**
3922 * Internal method that returns true if the medium is represented by a file on the host disk
3923 * (and not iSCSI or something).
3924 * @return
3925 */
3926bool Medium::i_isMediumFormatFile() const
3927{
3928 if ( m->formatObj
3929 && (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
3930 )
3931 return true;
3932 return false;
3933}
3934
3935/**
3936 * Internal method to return the medium's size. Must have caller + locking!
3937 * @return
3938 */
3939uint64_t Medium::i_getSize() const
3940{
3941 return m->size;
3942}
3943
3944/**
3945 * Internal method to return the medium's size. Must have caller + locking!
3946 * @return
3947 */
3948uint64_t Medium::i_getLogicalSize() const
3949{
3950 return m->logicalSize;
3951}
3952
3953/**
3954 * Returns the medium device type. Must have caller + locking!
3955 * @return
3956 */
3957DeviceType_T Medium::i_getDeviceType() const
3958{
3959 return m->devType;
3960}
3961
3962/**
3963 * Returns the medium type. Must have caller + locking!
3964 * @return
3965 */
3966MediumType_T Medium::i_getType() const
3967{
3968 return m->type;
3969}
3970
3971/**
3972 * Returns a short version of the location attribute.
3973 *
3974 * @note Must be called from under this object's read or write lock.
3975 */
3976Utf8Str Medium::i_getName()
3977{
3978 Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
3979 return name;
3980}
3981
3982/**
3983 * This adds the given UUID to the list of media registries in which this
3984 * medium should be registered. The UUID can either be a machine UUID,
3985 * to add a machine registry, or the global registry UUID as returned by
3986 * VirtualBox::getGlobalRegistryId().
3987 *
3988 * Note that for hard disks, this method does nothing if the medium is
3989 * already in another registry to avoid having hard disks in more than
3990 * one registry, which causes trouble with keeping diff images in sync.
3991 * See getFirstRegistryMachineId() for details.
3992 *
3993 * @param id
3994 * @return true if the registry was added; false if the given id was already on the list.
3995 */
3996bool Medium::i_addRegistry(const Guid& id)
3997{
3998 AutoCaller autoCaller(this);
3999 if (FAILED(autoCaller.rc()))
4000 return false;
4001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4002
4003 bool fAdd = true;
4004
4005 // hard disks cannot be in more than one registry
4006 if ( m->devType == DeviceType_HardDisk
4007 && m->llRegistryIDs.size() > 0)
4008 fAdd = false;
4009
4010 // no need to add the UUID twice
4011 if (fAdd)
4012 {
4013 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
4014 it != m->llRegistryIDs.end();
4015 ++it)
4016 {
4017 if ((*it) == id)
4018 {
4019 fAdd = false;
4020 break;
4021 }
4022 }
4023 }
4024
4025 if (fAdd)
4026 m->llRegistryIDs.push_back(id);
4027
4028 return fAdd;
4029}
4030
4031/**
4032 * This adds the given UUID to the list of media registries in which this
4033 * medium should be registered. The UUID can either be a machine UUID,
4034 * to add a machine registry, or the global registry UUID as returned by
4035 * VirtualBox::getGlobalRegistryId(). This recurses over all children.
4036 *
4037 * Note that for hard disks, this method does nothing if the medium is
4038 * already in another registry to avoid having hard disks in more than
4039 * one registry, which causes trouble with keeping diff images in sync.
4040 * See getFirstRegistryMachineId() for details.
4041 *
4042 * @note the caller must hold the media tree lock for reading.
4043 *
4044 * @param id
4045 * @return true if the registry was added; false if the given id was already on the list.
4046 */
4047bool Medium::i_addRegistryRecursive(const Guid &id)
4048{
4049 AutoCaller autoCaller(this);
4050 if (FAILED(autoCaller.rc()))
4051 return false;
4052
4053 bool fAdd = i_addRegistry(id);
4054
4055 // protected by the medium tree lock held by our original caller
4056 for (MediaList::const_iterator it = i_getChildren().begin();
4057 it != i_getChildren().end();
4058 ++it)
4059 {
4060 Medium *pChild = *it;
4061 fAdd |= pChild->i_addRegistryRecursive(id);
4062 }
4063
4064 return fAdd;
4065}
4066
4067/**
4068 * Removes the given UUID from the list of media registry UUIDs of this medium.
4069 *
4070 * @param id
4071 * @return true if the UUID was found or false if not.
4072 */
4073bool Medium::i_removeRegistry(const Guid &id)
4074{
4075 AutoCaller autoCaller(this);
4076 if (FAILED(autoCaller.rc()))
4077 return false;
4078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4079
4080 bool fRemove = false;
4081
4082 /// @todo r=klaus eliminate this code, replace it by using find.
4083 for (GuidList::iterator it = m->llRegistryIDs.begin();
4084 it != m->llRegistryIDs.end();
4085 ++it)
4086 {
4087 if ((*it) == id)
4088 {
4089 // getting away with this as the iterator isn't used after
4090 m->llRegistryIDs.erase(it);
4091 fRemove = true;
4092 break;
4093 }
4094 }
4095
4096 return fRemove;
4097}
4098
4099/**
4100 * Removes the given UUID from the list of media registry UUIDs, for this
4101 * medium and all its children recursively.
4102 *
4103 * @note the caller must hold the media tree lock for reading.
4104 *
4105 * @param id
4106 * @return true if the UUID was found or false if not.
4107 */
4108bool Medium::i_removeRegistryRecursive(const Guid &id)
4109{
4110 AutoCaller autoCaller(this);
4111 if (FAILED(autoCaller.rc()))
4112 return false;
4113
4114 bool fRemove = i_removeRegistry(id);
4115
4116 // protected by the medium tree lock held by our original caller
4117 for (MediaList::const_iterator it = i_getChildren().begin();
4118 it != i_getChildren().end();
4119 ++it)
4120 {
4121 Medium *pChild = *it;
4122 fRemove |= pChild->i_removeRegistryRecursive(id);
4123 }
4124
4125 return fRemove;
4126}
4127
4128/**
4129 * Returns true if id is in the list of media registries for this medium.
4130 *
4131 * Must have caller + read locking!
4132 *
4133 * @param id
4134 * @return
4135 */
4136bool Medium::i_isInRegistry(const Guid &id)
4137{
4138 /// @todo r=klaus eliminate this code, replace it by using find.
4139 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
4140 it != m->llRegistryIDs.end();
4141 ++it)
4142 {
4143 if (*it == id)
4144 return true;
4145 }
4146
4147 return false;
4148}
4149
4150/**
4151 * Internal method to return the medium's first registry machine (i.e. the machine in whose
4152 * machine XML this medium is listed).
4153 *
4154 * Every attached medium must now (4.0) reside in at least one media registry, which is identified
4155 * by a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
4156 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
4157 * object if the machine is old and still needs the global registry in VirtualBox.xml.
4158 *
4159 * By definition, hard disks may only be in one media registry, in which all its children
4160 * will be stored as well. Otherwise we run into problems with having keep multiple registries
4161 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
4162 * case, only VM2's registry is used for the disk in question.)
4163 *
4164 * If there is no medium registry, particularly if the medium has not been attached yet, this
4165 * does not modify uuid and returns false.
4166 *
4167 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
4168 * the user.
4169 *
4170 * Must have caller + locking!
4171 *
4172 * @param uuid Receives first registry machine UUID, if available.
4173 * @return true if uuid was set.
4174 */
4175bool Medium::i_getFirstRegistryMachineId(Guid &uuid) const
4176{
4177 if (m->llRegistryIDs.size())
4178 {
4179 uuid = m->llRegistryIDs.front();
4180 return true;
4181 }
4182 return false;
4183}
4184
4185/**
4186 * Marks all the registries in which this medium is registered as modified.
4187 */
4188void Medium::i_markRegistriesModified()
4189{
4190 AutoCaller autoCaller(this);
4191 if (FAILED(autoCaller.rc())) return;
4192
4193 // Get local copy, as keeping the lock over VirtualBox::markRegistryModified
4194 // causes trouble with the lock order
4195 GuidList llRegistryIDs;
4196 {
4197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4198 llRegistryIDs = m->llRegistryIDs;
4199 }
4200
4201 autoCaller.release();
4202
4203 /* Save the error information now, the implicit restore when this goes
4204 * out of scope will throw away spurious additional errors created below. */
4205 ErrorInfoKeeper eik;
4206 for (GuidList::const_iterator it = llRegistryIDs.begin();
4207 it != llRegistryIDs.end();
4208 ++it)
4209 {
4210 m->pVirtualBox->i_markRegistryModified(*it);
4211 }
4212}
4213
4214/**
4215 * Adds the given machine and optionally the snapshot to the list of the objects
4216 * this medium is attached to.
4217 *
4218 * @param aMachineId Machine ID.
4219 * @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
4220 */
4221HRESULT Medium::i_addBackReference(const Guid &aMachineId,
4222 const Guid &aSnapshotId /*= Guid::Empty*/)
4223{
4224 AssertReturn(aMachineId.isValid(), E_FAIL);
4225
4226 LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
4227
4228 AutoCaller autoCaller(this);
4229 AssertComRCReturnRC(autoCaller.rc());
4230
4231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4232
4233 switch (m->state)
4234 {
4235 case MediumState_Created:
4236 case MediumState_Inaccessible:
4237 case MediumState_LockedRead:
4238 case MediumState_LockedWrite:
4239 break;
4240
4241 default:
4242 return i_setStateError();
4243 }
4244
4245 if (m->numCreateDiffTasks > 0)
4246 return setError(VBOX_E_OBJECT_IN_USE,
4247 tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created"),
4248 m->strLocationFull.c_str(),
4249 m->id.raw(),
4250 m->numCreateDiffTasks);
4251
4252 BackRefList::iterator it = std::find_if(m->backRefs.begin(),
4253 m->backRefs.end(),
4254 BackRef::EqualsTo(aMachineId));
4255 if (it == m->backRefs.end())
4256 {
4257 BackRef ref(aMachineId, aSnapshotId);
4258 m->backRefs.push_back(ref);
4259
4260 return S_OK;
4261 }
4262
4263 // if the caller has not supplied a snapshot ID, then we're attaching
4264 // to a machine a medium which represents the machine's current state,
4265 // so set the flag
4266
4267 if (aSnapshotId.isZero())
4268 {
4269 /* sanity: no duplicate attachments */
4270 if (it->fInCurState)
4271 return setError(VBOX_E_OBJECT_IN_USE,
4272 tr("Cannot attach medium '%s' {%RTuuid}: medium is already associated with the current state of machine uuid {%RTuuid}!"),
4273 m->strLocationFull.c_str(),
4274 m->id.raw(),
4275 aMachineId.raw());
4276 it->fInCurState = true;
4277
4278 return S_OK;
4279 }
4280
4281 // otherwise: a snapshot medium is being attached
4282
4283 /* sanity: no duplicate attachments */
4284 for (GuidList::const_iterator jt = it->llSnapshotIds.begin();
4285 jt != it->llSnapshotIds.end();
4286 ++jt)
4287 {
4288 const Guid &idOldSnapshot = *jt;
4289
4290 if (idOldSnapshot == aSnapshotId)
4291 {
4292#ifdef DEBUG
4293 i_dumpBackRefs();
4294#endif
4295 return setError(VBOX_E_OBJECT_IN_USE,
4296 tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
4297 m->strLocationFull.c_str(),
4298 m->id.raw(),
4299 aSnapshotId.raw());
4300 }
4301 }
4302
4303 it->llSnapshotIds.push_back(aSnapshotId);
4304 // Do not touch fInCurState, as the image may be attached to the current
4305 // state *and* a snapshot, otherwise we lose the current state association!
4306
4307 LogFlowThisFuncLeave();
4308
4309 return S_OK;
4310}
4311
4312/**
4313 * Removes the given machine and optionally the snapshot from the list of the
4314 * objects this medium is attached to.
4315 *
4316 * @param aMachineId Machine ID.
4317 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
4318 * attachment.
4319 */
4320HRESULT Medium::i_removeBackReference(const Guid &aMachineId,
4321 const Guid &aSnapshotId /*= Guid::Empty*/)
4322{
4323 AssertReturn(aMachineId.isValid(), E_FAIL);
4324
4325 AutoCaller autoCaller(this);
4326 AssertComRCReturnRC(autoCaller.rc());
4327
4328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4329
4330 BackRefList::iterator it =
4331 std::find_if(m->backRefs.begin(), m->backRefs.end(),
4332 BackRef::EqualsTo(aMachineId));
4333 AssertReturn(it != m->backRefs.end(), E_FAIL);
4334
4335 if (aSnapshotId.isZero())
4336 {
4337 /* remove the current state attachment */
4338 it->fInCurState = false;
4339 }
4340 else
4341 {
4342 /* remove the snapshot attachment */
4343 GuidList::iterator jt = std::find(it->llSnapshotIds.begin(),
4344 it->llSnapshotIds.end(),
4345 aSnapshotId);
4346
4347 AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
4348 it->llSnapshotIds.erase(jt);
4349 }
4350
4351 /* if the backref becomes empty, remove it */
4352 if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
4353 m->backRefs.erase(it);
4354
4355 return S_OK;
4356}
4357
4358/**
4359 * Internal method to return the medium's list of backrefs. Must have caller + locking!
4360 * @return
4361 */
4362const Guid* Medium::i_getFirstMachineBackrefId() const
4363{
4364 if (!m->backRefs.size())
4365 return NULL;
4366
4367 return &m->backRefs.front().machineId;
4368}
4369
4370/**
4371 * Internal method which returns a machine that either this medium or one of its children
4372 * is attached to. This is used for finding a replacement media registry when an existing
4373 * media registry is about to be deleted in VirtualBox::unregisterMachine().
4374 *
4375 * Must have caller + locking, *and* caller must hold the media tree lock!
4376 * @return
4377 */
4378const Guid* Medium::i_getAnyMachineBackref() const
4379{
4380 if (m->backRefs.size())
4381 return &m->backRefs.front().machineId;
4382
4383 for (MediaList::const_iterator it = i_getChildren().begin();
4384 it != i_getChildren().end();
4385 ++it)
4386 {
4387 Medium *pChild = *it;
4388 // recurse for this child
4389 const Guid* puuid;
4390 if ((puuid = pChild->i_getAnyMachineBackref()))
4391 return puuid;
4392 }
4393
4394 return NULL;
4395}
4396
4397const Guid* Medium::i_getFirstMachineBackrefSnapshotId() const
4398{
4399 if (!m->backRefs.size())
4400 return NULL;
4401
4402 const BackRef &ref = m->backRefs.front();
4403 if (ref.llSnapshotIds.empty())
4404 return NULL;
4405
4406 return &ref.llSnapshotIds.front();
4407}
4408
4409size_t Medium::i_getMachineBackRefCount() const
4410{
4411 return m->backRefs.size();
4412}
4413
4414#ifdef DEBUG
4415/**
4416 * Debugging helper that gets called after VirtualBox initialization that writes all
4417 * machine backreferences to the debug log.
4418 */
4419void Medium::i_dumpBackRefs()
4420{
4421 AutoCaller autoCaller(this);
4422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4423
4424 LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
4425
4426 for (BackRefList::iterator it2 = m->backRefs.begin();
4427 it2 != m->backRefs.end();
4428 ++it2)
4429 {
4430 const BackRef &ref = *it2;
4431 LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d)\n", ref.machineId.raw(), ref.fInCurState));
4432
4433 for (GuidList::const_iterator jt2 = it2->llSnapshotIds.begin();
4434 jt2 != it2->llSnapshotIds.end();
4435 ++jt2)
4436 {
4437 const Guid &id = *jt2;
4438 LogFlowThisFunc((" Backref from snapshot {%RTuuid}\n", id.raw()));
4439 }
4440 }
4441}
4442#endif
4443
4444/**
4445 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
4446 * of this media and updates it if necessary to reflect the new location.
4447 *
4448 * @param strOldPath Old path (full).
4449 * @param strNewPath New path (full).
4450 *
4451 * @note Locks this object for writing.
4452 */
4453HRESULT Medium::i_updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
4454{
4455 AssertReturn(!strOldPath.isEmpty(), E_FAIL);
4456 AssertReturn(!strNewPath.isEmpty(), E_FAIL);
4457
4458 AutoCaller autoCaller(this);
4459 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4460
4461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4462
4463 LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
4464
4465 const char *pcszMediumPath = m->strLocationFull.c_str();
4466
4467 if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
4468 {
4469 Utf8Str newPath(strNewPath);
4470 newPath.append(pcszMediumPath + strOldPath.length());
4471 unconst(m->strLocationFull) = newPath;
4472
4473 m->pVirtualBox->i_onMediumConfigChanged(this);
4474
4475 LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
4476 // we changed something
4477 return S_OK;
4478 }
4479
4480 // no change was necessary, signal error which the caller needs to interpret
4481 return VBOX_E_FILE_ERROR;
4482}
4483
4484/**
4485 * Returns the base medium of the media chain this medium is part of.
4486 *
4487 * The base medium is found by walking up the parent-child relationship axis.
4488 * If the medium doesn't have a parent (i.e. it's a base medium), it
4489 * returns itself in response to this method.
4490 *
4491 * @param aLevel Where to store the number of ancestors of this medium
4492 * (zero for the base), may be @c NULL.
4493 *
4494 * @note Locks medium tree for reading.
4495 */
4496ComObjPtr<Medium> Medium::i_getBase(uint32_t *aLevel /*= NULL*/)
4497{
4498 ComObjPtr<Medium> pBase;
4499
4500 /* it is possible that some previous/concurrent uninit has already cleared
4501 * the pVirtualBox reference, and in this case we don't need to continue */
4502 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4503 if (!pVirtualBox)
4504 return pBase;
4505
4506 /* we access m->pParent */
4507 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4508
4509 AutoCaller autoCaller(this);
4510 AssertReturn(autoCaller.isOk(), pBase);
4511
4512 pBase = this;
4513 uint32_t level = 0;
4514
4515 if (m->pParent)
4516 {
4517 for (;;)
4518 {
4519 AutoCaller baseCaller(pBase);
4520 AssertReturn(baseCaller.isOk(), pBase);
4521
4522 if (pBase->m->pParent.isNull())
4523 break;
4524
4525 pBase = pBase->m->pParent;
4526 ++level;
4527 }
4528 }
4529
4530 if (aLevel != NULL)
4531 *aLevel = level;
4532
4533 return pBase;
4534}
4535
4536/**
4537 * Returns the depth of this medium in the media chain.
4538 *
4539 * @note Locks medium tree for reading.
4540 */
4541uint32_t Medium::i_getDepth()
4542{
4543 /* it is possible that some previous/concurrent uninit has already cleared
4544 * the pVirtualBox reference, and in this case we don't need to continue */
4545 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4546 if (!pVirtualBox)
4547 return 1;
4548
4549 /* we access m->pParent */
4550 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4551
4552 uint32_t cDepth = 0;
4553 ComObjPtr<Medium> pMedium(this);
4554 while (!pMedium.isNull())
4555 {
4556 AutoCaller autoCaller(this);
4557 AssertReturn(autoCaller.isOk(), cDepth + 1);
4558
4559 pMedium = pMedium->m->pParent;
4560 cDepth++;
4561 }
4562
4563 return cDepth;
4564}
4565
4566/**
4567 * Returns @c true if this medium cannot be modified because it has
4568 * dependents (children) or is part of the snapshot. Related to the medium
4569 * type and posterity, not to the current media state.
4570 *
4571 * @note Locks this object and medium tree for reading.
4572 */
4573bool Medium::i_isReadOnly()
4574{
4575 /* it is possible that some previous/concurrent uninit has already cleared
4576 * the pVirtualBox reference, and in this case we don't need to continue */
4577 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4578 if (!pVirtualBox)
4579 return false;
4580
4581 /* we access children */
4582 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4583
4584 AutoCaller autoCaller(this);
4585 AssertComRCReturn(autoCaller.rc(), false);
4586
4587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4588
4589 switch (m->type)
4590 {
4591 case MediumType_Normal:
4592 {
4593 if (i_getChildren().size() != 0)
4594 return true;
4595
4596 for (BackRefList::const_iterator it = m->backRefs.begin();
4597 it != m->backRefs.end(); ++it)
4598 if (it->llSnapshotIds.size() != 0)
4599 return true;
4600
4601 if (m->variant & MediumVariant_VmdkStreamOptimized)
4602 return true;
4603
4604 return false;
4605 }
4606 case MediumType_Immutable:
4607 case MediumType_MultiAttach:
4608 return true;
4609 case MediumType_Writethrough:
4610 case MediumType_Shareable:
4611 case MediumType_Readonly: /* explicit readonly media has no diffs */
4612 return false;
4613 default:
4614 break;
4615 }
4616
4617 AssertFailedReturn(false);
4618}
4619
4620/**
4621 * Internal method to update the medium's id. Must have caller + locking!
4622 * @return
4623 */
4624void Medium::i_updateId(const Guid &id)
4625{
4626 unconst(m->id) = id;
4627}
4628
4629/**
4630 * Saves the settings of one medium.
4631 *
4632 * @note Caller MUST take care of the medium tree lock and caller.
4633 *
4634 * @param data Settings struct to be updated.
4635 * @param strHardDiskFolder Folder for which paths should be relative.
4636 */
4637void Medium::i_saveSettingsOne(settings::Medium &data, const Utf8Str &strHardDiskFolder)
4638{
4639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4640
4641 data.uuid = m->id;
4642
4643 // make path relative if needed
4644 if ( !strHardDiskFolder.isEmpty()
4645 && RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
4646 )
4647 data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
4648 else
4649 data.strLocation = m->strLocationFull;
4650 data.strFormat = m->strFormat;
4651
4652 /* optional, only for diffs, default is false */
4653 if (m->pParent)
4654 data.fAutoReset = m->autoReset;
4655 else
4656 data.fAutoReset = false;
4657
4658 /* optional */
4659 data.strDescription = m->strDescription;
4660
4661 /* optional properties */
4662 data.properties.clear();
4663
4664 /* handle iSCSI initiator secrets transparently */
4665 bool fHaveInitiatorSecretEncrypted = false;
4666 Utf8Str strCiphertext;
4667 settings::StringsMap::const_iterator itPln = m->mapProperties.find("InitiatorSecret");
4668 if ( itPln != m->mapProperties.end()
4669 && !itPln->second.isEmpty())
4670 {
4671 /* Encrypt the plain secret. If that does not work (i.e. no or wrong settings key
4672 * specified), just use the encrypted secret (if there is any). */
4673 int rc = m->pVirtualBox->i_encryptSetting(itPln->second, &strCiphertext);
4674 if (RT_SUCCESS(rc))
4675 fHaveInitiatorSecretEncrypted = true;
4676 }
4677 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
4678 it != m->mapProperties.end();
4679 ++it)
4680 {
4681 /* only save properties that have non-default values */
4682 if (!it->second.isEmpty())
4683 {
4684 const Utf8Str &name = it->first;
4685 const Utf8Str &value = it->second;
4686 bool fCreateOnly = false;
4687 for (MediumFormat::PropertyArray::const_iterator itf = m->formatObj->i_getProperties().begin();
4688 itf != m->formatObj->i_getProperties().end();
4689 ++itf)
4690 {
4691 if (itf->strName.equals(name) &&
4692 (itf->flags & VD_CFGKEY_CREATEONLY))
4693 {
4694 fCreateOnly = true;
4695 break;
4696 }
4697 }
4698 if (!fCreateOnly)
4699 /* do NOT store the plain InitiatorSecret */
4700 if ( !fHaveInitiatorSecretEncrypted
4701 || !name.equals("InitiatorSecret"))
4702 data.properties[name] = value; }
4703 }
4704 if (fHaveInitiatorSecretEncrypted)
4705 data.properties["InitiatorSecretEncrypted"] = strCiphertext;
4706
4707 /* only for base media */
4708 if (m->pParent.isNull())
4709 data.hdType = m->type;
4710}
4711
4712/**
4713 * Saves medium data by putting it into the provided data structure.
4714 * Recurses over all children to save their settings, too.
4715 *
4716 * @param data Settings struct to be updated.
4717 * @param strHardDiskFolder Folder for which paths should be relative.
4718 *
4719 * @note Locks this object, medium tree and children for reading.
4720 */
4721HRESULT Medium::i_saveSettings(settings::Medium &data,
4722 const Utf8Str &strHardDiskFolder)
4723{
4724 /* we access m->pParent */
4725 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4726
4727 AutoCaller autoCaller(this);
4728 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4729
4730 i_saveSettingsOne(data, strHardDiskFolder);
4731
4732 /* save all children */
4733 settings::MediaList &llSettingsChildren = data.llChildren;
4734 for (MediaList::const_iterator it = i_getChildren().begin();
4735 it != i_getChildren().end();
4736 ++it)
4737 {
4738 // Use the element straight in the list to reduce both unnecessary
4739 // deep copying (when unwinding the recursion the entire medium
4740 // settings sub-tree is copied) and the stack footprint (the settings
4741 // need almost 1K, and there can be VMs with long image chains.
4742 llSettingsChildren.push_back(settings::Medium::Empty);
4743 HRESULT rc = (*it)->i_saveSettings(llSettingsChildren.back(), strHardDiskFolder);
4744 if (FAILED(rc))
4745 {
4746 llSettingsChildren.pop_back();
4747 return rc;
4748 }
4749 }
4750
4751 return S_OK;
4752}
4753
4754/**
4755 * Constructs a medium lock list for this medium. The lock is not taken.
4756 *
4757 * @note Caller MUST NOT hold the media tree or medium lock.
4758 *
4759 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
4760 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
4761 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
4762 * @param pToLockWrite If not NULL, associate a write lock with this medium object.
4763 * @param fMediumLockWriteAll Whether to associate a write lock to all other media too.
4764 * @param pToBeParent Medium which will become the parent of this medium.
4765 * @param mediumLockList Where to store the resulting list.
4766 */
4767HRESULT Medium::i_createMediumLockList(bool fFailIfInaccessible,
4768 Medium *pToLockWrite,
4769 bool fMediumLockWriteAll,
4770 Medium *pToBeParent,
4771 MediumLockList &mediumLockList)
4772{
4773 /** @todo r=klaus this needs to be reworked, as the code below uses
4774 * i_getParent without holding the tree lock, and changing this is
4775 * a significant amount of effort. */
4776 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4777 Assert(!isWriteLockOnCurrentThread());
4778
4779 AutoCaller autoCaller(this);
4780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4781
4782 HRESULT rc = S_OK;
4783
4784 /* paranoid sanity checking if the medium has a to-be parent medium */
4785 if (pToBeParent)
4786 {
4787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4788 ComAssertRet(i_getParent().isNull(), E_FAIL);
4789 ComAssertRet(i_getChildren().size() == 0, E_FAIL);
4790 }
4791
4792 ErrorInfoKeeper eik;
4793 MultiResult mrc(S_OK);
4794
4795 ComObjPtr<Medium> pMedium = this;
4796 while (!pMedium.isNull())
4797 {
4798 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4799
4800 /* Accessibility check must be first, otherwise locking interferes
4801 * with getting the medium state. Lock lists are not created for
4802 * fun, and thus getting the medium status is no luxury. */
4803 MediumState_T mediumState = pMedium->i_getState();
4804 if (mediumState == MediumState_Inaccessible)
4805 {
4806 alock.release();
4807 rc = pMedium->i_queryInfo(false /* fSetImageId */, false /* fSetParentId */,
4808 autoCaller);
4809 alock.acquire();
4810 if (FAILED(rc)) return rc;
4811
4812 mediumState = pMedium->i_getState();
4813 if (mediumState == MediumState_Inaccessible)
4814 {
4815 // ignore inaccessible ISO media and silently return S_OK,
4816 // otherwise VM startup (esp. restore) may fail without good reason
4817 if (!fFailIfInaccessible)
4818 return S_OK;
4819
4820 // otherwise report an error
4821 Bstr error;
4822 rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
4823 if (FAILED(rc)) return rc;
4824
4825 /* collect multiple errors */
4826 eik.restore();
4827 Assert(!error.isEmpty());
4828 mrc = setError(E_FAIL,
4829 "%ls",
4830 error.raw());
4831 // error message will be something like
4832 // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
4833 eik.fetch();
4834 }
4835 }
4836
4837 if (pMedium == pToLockWrite)
4838 mediumLockList.Prepend(pMedium, true);
4839 else
4840 mediumLockList.Prepend(pMedium, fMediumLockWriteAll);
4841
4842 pMedium = pMedium->i_getParent();
4843 if (pMedium.isNull() && pToBeParent)
4844 {
4845 pMedium = pToBeParent;
4846 pToBeParent = NULL;
4847 }
4848 }
4849
4850 return mrc;
4851}
4852
4853/**
4854 * Creates a new differencing storage unit using the format of the given target
4855 * medium and the location. Note that @c aTarget must be NotCreated.
4856 *
4857 * The @a aMediumLockList parameter contains the associated medium lock list,
4858 * which must be in locked state. If @a aWait is @c true then the caller is
4859 * responsible for unlocking.
4860 *
4861 * If @a aProgress is not NULL but the object it points to is @c null then a
4862 * new progress object will be created and assigned to @a *aProgress on
4863 * success, otherwise the existing progress object is used. If @a aProgress is
4864 * NULL, then no progress object is created/used at all.
4865 *
4866 * When @a aWait is @c false, this method will create a thread to perform the
4867 * create operation asynchronously and will return immediately. Otherwise, it
4868 * will perform the operation on the calling thread and will not return to the
4869 * caller until the operation is completed. Note that @a aProgress cannot be
4870 * NULL when @a aWait is @c false (this method will assert in this case).
4871 *
4872 * @param aTarget Target medium.
4873 * @param aVariant Precise medium variant to create.
4874 * @param aMediumLockList List of media which should be locked.
4875 * @param aProgress Where to find/store a Progress object to track
4876 * operation completion.
4877 * @param aWait @c true if this method should block instead of
4878 * creating an asynchronous thread.
4879 * @param aNotify Notify about mediums which metadatа are changed
4880 * during execution of the function.
4881 *
4882 * @note Locks this object and @a aTarget for writing.
4883 */
4884HRESULT Medium::i_createDiffStorage(ComObjPtr<Medium> &aTarget,
4885 MediumVariant_T aVariant,
4886 MediumLockList *aMediumLockList,
4887 ComObjPtr<Progress> *aProgress,
4888 bool aWait,
4889 bool aNotify)
4890{
4891 AssertReturn(!aTarget.isNull(), E_FAIL);
4892 AssertReturn(aMediumLockList, E_FAIL);
4893 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4894
4895 AutoCaller autoCaller(this);
4896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4897
4898 AutoCaller targetCaller(aTarget);
4899 if (FAILED(targetCaller.rc())) return targetCaller.rc();
4900
4901 HRESULT rc = S_OK;
4902 ComObjPtr<Progress> pProgress;
4903 Medium::Task *pTask = NULL;
4904
4905 try
4906 {
4907 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
4908
4909 ComAssertThrow( m->type != MediumType_Writethrough
4910 && m->type != MediumType_Shareable
4911 && m->type != MediumType_Readonly, E_FAIL);
4912 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
4913
4914 if (aTarget->m->state != MediumState_NotCreated)
4915 throw aTarget->i_setStateError();
4916
4917 /* Check that the medium is not attached to the current state of
4918 * any VM referring to it. */
4919 for (BackRefList::const_iterator it = m->backRefs.begin();
4920 it != m->backRefs.end();
4921 ++it)
4922 {
4923 if (it->fInCurState)
4924 {
4925 /* Note: when a VM snapshot is being taken, all normal media
4926 * attached to the VM in the current state will be, as an
4927 * exception, also associated with the snapshot which is about
4928 * to create (see SnapshotMachine::init()) before deassociating
4929 * them from the current state (which takes place only on
4930 * success in Machine::fixupHardDisks()), so that the size of
4931 * snapshotIds will be 1 in this case. The extra condition is
4932 * used to filter out this legal situation. */
4933 if (it->llSnapshotIds.size() == 0)
4934 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4935 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"),
4936 m->strLocationFull.c_str(), it->machineId.raw());
4937
4938 Assert(it->llSnapshotIds.size() == 1);
4939 }
4940 }
4941
4942 if (aProgress != NULL)
4943 {
4944 /* use the existing progress object... */
4945 pProgress = *aProgress;
4946
4947 /* ...but create a new one if it is null */
4948 if (pProgress.isNull())
4949 {
4950 pProgress.createObject();
4951 rc = pProgress->init(m->pVirtualBox,
4952 static_cast<IMedium*>(this),
4953 BstrFmt(tr("Creating differencing medium storage unit '%s'"),
4954 aTarget->m->strLocationFull.c_str()).raw(),
4955 TRUE /* aCancelable */);
4956 if (FAILED(rc))
4957 throw rc;
4958 }
4959 }
4960
4961 /* setup task object to carry out the operation sync/async */
4962 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
4963 aMediumLockList,
4964 aWait /* fKeepMediumLockList */,
4965 aNotify);
4966 rc = pTask->rc();
4967 AssertComRC(rc);
4968 if (FAILED(rc))
4969 throw rc;
4970
4971 /* register a task (it will deregister itself when done) */
4972 ++m->numCreateDiffTasks;
4973 Assert(m->numCreateDiffTasks != 0); /* overflow? */
4974
4975 aTarget->m->state = MediumState_Creating;
4976 }
4977 catch (HRESULT aRC) { rc = aRC; }
4978
4979 if (SUCCEEDED(rc))
4980 {
4981 if (aWait)
4982 {
4983 rc = pTask->runNow();
4984 delete pTask;
4985 }
4986 else
4987 rc = pTask->createThread();
4988 pTask = NULL;
4989 if (SUCCEEDED(rc) && aProgress != NULL)
4990 *aProgress = pProgress;
4991 }
4992 else if (pTask != NULL)
4993 delete pTask;
4994
4995 return rc;
4996}
4997
4998/**
4999 * Returns a preferred format for differencing media.
5000 */
5001Utf8Str Medium::i_getPreferredDiffFormat()
5002{
5003 AutoCaller autoCaller(this);
5004 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
5005
5006 /* check that our own format supports diffs */
5007 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5008 {
5009 /* use the default format if not */
5010 Utf8Str tmp;
5011 m->pVirtualBox->i_getDefaultHardDiskFormat(tmp);
5012 return tmp;
5013 }
5014
5015 /* m->strFormat is const, no need to lock */
5016 return m->strFormat;
5017}
5018
5019/**
5020 * Returns a preferred variant for differencing media.
5021 */
5022MediumVariant_T Medium::i_getPreferredDiffVariant()
5023{
5024 AutoCaller autoCaller(this);
5025 AssertComRCReturn(autoCaller.rc(), MediumVariant_Standard);
5026
5027 /* check that our own format supports diffs */
5028 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5029 return MediumVariant_Standard;
5030
5031 /* m->variant is const, no need to lock */
5032 ULONG mediumVariantFlags = (ULONG)m->variant;
5033 mediumVariantFlags &= ~(MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized);
5034 mediumVariantFlags |= MediumVariant_Diff;
5035 return (MediumVariant_T)mediumVariantFlags;
5036}
5037
5038/**
5039 * Implementation for the public Medium::Close() with the exception of calling
5040 * VirtualBox::saveRegistries(), in case someone wants to call this for several
5041 * media.
5042 *
5043 * After this returns with success, uninit() has been called on the medium, and
5044 * the object is no longer usable ("not ready" state).
5045 *
5046 * @param autoCaller AutoCaller instance which must have been created on the caller's
5047 * stack for this medium. This gets released hereupon
5048 * which the Medium instance gets uninitialized.
5049 * @return
5050 */
5051HRESULT Medium::i_close(AutoCaller &autoCaller)
5052{
5053 // must temporarily drop the caller, need the tree lock first
5054 autoCaller.release();
5055
5056 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
5057 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
5058 this->lockHandle()
5059 COMMA_LOCKVAL_SRC_POS);
5060
5061 autoCaller.add();
5062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5063
5064 LogFlowFunc(("ENTER for %s\n", i_getLocationFull().c_str()));
5065
5066 bool wasCreated = true;
5067
5068 switch (m->state)
5069 {
5070 case MediumState_NotCreated:
5071 wasCreated = false;
5072 break;
5073 case MediumState_Created:
5074 case MediumState_Inaccessible:
5075 break;
5076 default:
5077 return i_setStateError();
5078 }
5079
5080 if (m->backRefs.size() != 0)
5081 return setError(VBOX_E_OBJECT_IN_USE,
5082 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
5083 m->strLocationFull.c_str(), m->backRefs.size());
5084
5085 // perform extra media-dependent close checks
5086 HRESULT rc = i_canClose();
5087 if (FAILED(rc)) return rc;
5088
5089 m->fClosing = true;
5090
5091 if (wasCreated)
5092 {
5093 // remove from the list of known media before performing actual
5094 // uninitialization (to keep the media registry consistent on
5095 // failure to do so)
5096 rc = i_unregisterWithVirtualBox();
5097 if (FAILED(rc)) return rc;
5098
5099 multilock.release();
5100 // Release the AutoCaller now, as otherwise uninit() will simply hang.
5101 // Needs to be done before mark the registries as modified and saving
5102 // the registry, as otherwise there may be a deadlock with someone else
5103 // closing this object while we're in i_saveModifiedRegistries(), which
5104 // needs the media tree lock, which the other thread holds until after
5105 // uninit() below.
5106 autoCaller.release();
5107 i_markRegistriesModified();
5108 m->pVirtualBox->i_saveModifiedRegistries();
5109 }
5110 else
5111 {
5112 multilock.release();
5113 // release the AutoCaller, as otherwise uninit() will simply hang
5114 autoCaller.release();
5115 }
5116
5117 // Keep the locks held until after uninit, as otherwise the consistency
5118 // of the medium tree cannot be guaranteed.
5119 uninit();
5120
5121 LogFlowFuncLeave();
5122
5123 return rc;
5124}
5125
5126/**
5127 * Deletes the medium storage unit.
5128 *
5129 * If @a aProgress is not NULL but the object it points to is @c null then a new
5130 * progress object will be created and assigned to @a *aProgress on success,
5131 * otherwise the existing progress object is used. If Progress is NULL, then no
5132 * progress object is created/used at all.
5133 *
5134 * When @a aWait is @c false, this method will create a thread to perform the
5135 * delete operation asynchronously and will return immediately. Otherwise, it
5136 * will perform the operation on the calling thread and will not return to the
5137 * caller until the operation is completed. Note that @a aProgress cannot be
5138 * NULL when @a aWait is @c false (this method will assert in this case).
5139 *
5140 * @param aProgress Where to find/store a Progress object to track operation
5141 * completion.
5142 * @param aWait @c true if this method should block instead of creating
5143 * an asynchronous thread.
5144 * @param aNotify Notify about mediums which metadatа are changed
5145 * during execution of the function.
5146 *
5147 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
5148 * writing.
5149 */
5150HRESULT Medium::i_deleteStorage(ComObjPtr<Progress> *aProgress,
5151 bool aWait, bool aNotify)
5152{
5153 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5154
5155 HRESULT rc = S_OK;
5156 ComObjPtr<Progress> pProgress;
5157 Medium::Task *pTask = NULL;
5158
5159 try
5160 {
5161 /* we're accessing the media tree, and canClose() needs it too */
5162 AutoWriteLock treelock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5163
5164 AutoCaller autoCaller(this);
5165 AssertComRCThrowRC(autoCaller.rc());
5166
5167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5168
5169 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, i_getLocationFull().c_str() ));
5170
5171 if ( !(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
5172 | MediumFormatCapabilities_CreateFixed)))
5173 throw setError(VBOX_E_NOT_SUPPORTED,
5174 tr("Medium format '%s' does not support storage deletion"),
5175 m->strFormat.c_str());
5176
5177 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5178 /** @todo r=klaus would be great if this could be moved to the async
5179 * part of the operation as it can take quite a while */
5180 if (m->queryInfoRunning)
5181 {
5182 while (m->queryInfoRunning)
5183 {
5184 alock.release();
5185 autoCaller.release();
5186 treelock.release();
5187 /* Must not hold the media tree lock or the object lock, as
5188 * Medium::i_queryInfo needs this lock and thus we would run
5189 * into a deadlock here. */
5190 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5191 Assert(!isWriteLockOnCurrentThread());
5192 {
5193 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5194 }
5195 treelock.acquire();
5196 autoCaller.add();
5197 AssertComRCThrowRC(autoCaller.rc());
5198 alock.acquire();
5199 }
5200 }
5201
5202 /* Note that we are fine with Inaccessible state too: a) for symmetry
5203 * with create calls and b) because it doesn't really harm to try, if
5204 * it is really inaccessible, the delete operation will fail anyway.
5205 * Accepting Inaccessible state is especially important because all
5206 * registered media are initially Inaccessible upon VBoxSVC startup
5207 * until COMGETTER(RefreshState) is called. Accept Deleting state
5208 * because some callers need to put the medium in this state early
5209 * to prevent races. */
5210 switch (m->state)
5211 {
5212 case MediumState_Created:
5213 case MediumState_Deleting:
5214 case MediumState_Inaccessible:
5215 break;
5216 default:
5217 throw i_setStateError();
5218 }
5219
5220 if (m->backRefs.size() != 0)
5221 {
5222 Utf8Str strMachines;
5223 for (BackRefList::const_iterator it = m->backRefs.begin();
5224 it != m->backRefs.end();
5225 ++it)
5226 {
5227 const BackRef &b = *it;
5228 if (strMachines.length())
5229 strMachines.append(", ");
5230 strMachines.append(b.machineId.toString().c_str());
5231 }
5232#ifdef DEBUG
5233 i_dumpBackRefs();
5234#endif
5235 throw setError(VBOX_E_OBJECT_IN_USE,
5236 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
5237 m->strLocationFull.c_str(),
5238 m->backRefs.size(),
5239 strMachines.c_str());
5240 }
5241
5242 rc = i_canClose();
5243 if (FAILED(rc))
5244 throw rc;
5245
5246 /* go to Deleting state, so that the medium is not actually locked */
5247 if (m->state != MediumState_Deleting)
5248 {
5249 rc = i_markForDeletion();
5250 if (FAILED(rc))
5251 throw rc;
5252 }
5253
5254 /* Build the medium lock list. */
5255 MediumLockList *pMediumLockList(new MediumLockList());
5256 alock.release();
5257 autoCaller.release();
5258 treelock.release();
5259 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5260 this /* pToLockWrite */,
5261 false /* fMediumLockWriteAll */,
5262 NULL,
5263 *pMediumLockList);
5264 treelock.acquire();
5265 autoCaller.add();
5266 AssertComRCThrowRC(autoCaller.rc());
5267 alock.acquire();
5268 if (FAILED(rc))
5269 {
5270 delete pMediumLockList;
5271 throw rc;
5272 }
5273
5274 alock.release();
5275 autoCaller.release();
5276 treelock.release();
5277 rc = pMediumLockList->Lock();
5278 treelock.acquire();
5279 autoCaller.add();
5280 AssertComRCThrowRC(autoCaller.rc());
5281 alock.acquire();
5282 if (FAILED(rc))
5283 {
5284 delete pMediumLockList;
5285 throw setError(rc,
5286 tr("Failed to lock media when deleting '%s'"),
5287 i_getLocationFull().c_str());
5288 }
5289
5290 /* try to remove from the list of known media before performing
5291 * actual deletion (we favor the consistency of the media registry
5292 * which would have been broken if unregisterWithVirtualBox() failed
5293 * after we successfully deleted the storage) */
5294 rc = i_unregisterWithVirtualBox();
5295 if (FAILED(rc))
5296 throw rc;
5297 // no longer need lock
5298 alock.release();
5299 autoCaller.release();
5300 treelock.release();
5301 i_markRegistriesModified();
5302
5303 if (aProgress != NULL)
5304 {
5305 /* use the existing progress object... */
5306 pProgress = *aProgress;
5307
5308 /* ...but create a new one if it is null */
5309 if (pProgress.isNull())
5310 {
5311 pProgress.createObject();
5312 rc = pProgress->init(m->pVirtualBox,
5313 static_cast<IMedium*>(this),
5314 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
5315 FALSE /* aCancelable */);
5316 if (FAILED(rc))
5317 throw rc;
5318 }
5319 }
5320
5321 /* setup task object to carry out the operation sync/async */
5322 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList, false, aNotify);
5323 rc = pTask->rc();
5324 AssertComRC(rc);
5325 if (FAILED(rc))
5326 throw rc;
5327 }
5328 catch (HRESULT aRC) { rc = aRC; }
5329
5330 if (SUCCEEDED(rc))
5331 {
5332 if (aWait)
5333 {
5334 rc = pTask->runNow();
5335 delete pTask;
5336 }
5337 else
5338 rc = pTask->createThread();
5339 pTask = NULL;
5340 if (SUCCEEDED(rc) && aProgress != NULL)
5341 *aProgress = pProgress;
5342 }
5343 else
5344 {
5345 if (pTask)
5346 delete pTask;
5347
5348 /* Undo deleting state if necessary. */
5349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5350 /* Make sure that any error signalled by unmarkForDeletion() is not
5351 * ending up in the error list (if the caller uses MultiResult). It
5352 * usually is spurious, as in most cases the medium hasn't been marked
5353 * for deletion when the error was thrown above. */
5354 ErrorInfoKeeper eik;
5355 i_unmarkForDeletion();
5356 }
5357
5358 return rc;
5359}
5360
5361/**
5362 * Mark a medium for deletion.
5363 *
5364 * @note Caller must hold the write lock on this medium!
5365 */
5366HRESULT Medium::i_markForDeletion()
5367{
5368 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5369 switch (m->state)
5370 {
5371 case MediumState_Created:
5372 case MediumState_Inaccessible:
5373 m->preLockState = m->state;
5374 m->state = MediumState_Deleting;
5375 return S_OK;
5376 default:
5377 return i_setStateError();
5378 }
5379}
5380
5381/**
5382 * Removes the "mark for deletion".
5383 *
5384 * @note Caller must hold the write lock on this medium!
5385 */
5386HRESULT Medium::i_unmarkForDeletion()
5387{
5388 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5389 switch (m->state)
5390 {
5391 case MediumState_Deleting:
5392 m->state = m->preLockState;
5393 return S_OK;
5394 default:
5395 return i_setStateError();
5396 }
5397}
5398
5399/**
5400 * Mark a medium for deletion which is in locked state.
5401 *
5402 * @note Caller must hold the write lock on this medium!
5403 */
5404HRESULT Medium::i_markLockedForDeletion()
5405{
5406 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5407 if ( ( m->state == MediumState_LockedRead
5408 || m->state == MediumState_LockedWrite)
5409 && m->preLockState == MediumState_Created)
5410 {
5411 m->preLockState = MediumState_Deleting;
5412 return S_OK;
5413 }
5414 else
5415 return i_setStateError();
5416}
5417
5418/**
5419 * Removes the "mark for deletion" for a medium in locked state.
5420 *
5421 * @note Caller must hold the write lock on this medium!
5422 */
5423HRESULT Medium::i_unmarkLockedForDeletion()
5424{
5425 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5426 if ( ( m->state == MediumState_LockedRead
5427 || m->state == MediumState_LockedWrite)
5428 && m->preLockState == MediumState_Deleting)
5429 {
5430 m->preLockState = MediumState_Created;
5431 return S_OK;
5432 }
5433 else
5434 return i_setStateError();
5435}
5436
5437/**
5438 * Queries the preferred merge direction from this to the other medium, i.e.
5439 * the one which requires the least amount of I/O and therefore time and
5440 * disk consumption.
5441 *
5442 * @returns Status code.
5443 * @retval E_FAIL in case determining the merge direction fails for some reason,
5444 * for example if getting the size of the media fails. There is no
5445 * error set though and the caller is free to continue to find out
5446 * what was going wrong later. Leaves fMergeForward unset.
5447 * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
5448 * An error is set.
5449 * @param pOther The other medium to merge with.
5450 * @param fMergeForward Resulting preferred merge direction (out).
5451 */
5452HRESULT Medium::i_queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
5453 bool &fMergeForward)
5454{
5455 AssertReturn(pOther != NULL, E_FAIL);
5456 AssertReturn(pOther != this, E_FAIL);
5457
5458 HRESULT rc = S_OK;
5459 bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
5460
5461 try
5462 {
5463 // locking: we need the tree lock first because we access parent pointers
5464 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5465
5466 AutoCaller autoCaller(this);
5467 AssertComRCThrowRC(autoCaller.rc());
5468
5469 AutoCaller otherCaller(pOther);
5470 AssertComRCThrowRC(otherCaller.rc());
5471
5472 /* more sanity checking and figuring out the current merge direction */
5473 ComObjPtr<Medium> pMedium = i_getParent();
5474 while (!pMedium.isNull() && pMedium != pOther)
5475 pMedium = pMedium->i_getParent();
5476 if (pMedium == pOther)
5477 fThisParent = false;
5478 else
5479 {
5480 pMedium = pOther->i_getParent();
5481 while (!pMedium.isNull() && pMedium != this)
5482 pMedium = pMedium->i_getParent();
5483 if (pMedium == this)
5484 fThisParent = true;
5485 else
5486 {
5487 Utf8Str tgtLoc;
5488 {
5489 AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
5490 tgtLoc = pOther->i_getLocationFull();
5491 }
5492
5493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5494 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5495 tr("Media '%s' and '%s' are unrelated"),
5496 m->strLocationFull.c_str(), tgtLoc.c_str());
5497 }
5498 }
5499
5500 /*
5501 * Figure out the preferred merge direction. The current way is to
5502 * get the current sizes of file based images and select the merge
5503 * direction depending on the size.
5504 *
5505 * Can't use the VD API to get current size here as the media might
5506 * be write locked by a running VM. Resort to RTFileQuerySize().
5507 */
5508 int vrc = VINF_SUCCESS;
5509 uint64_t cbMediumThis = 0;
5510 uint64_t cbMediumOther = 0;
5511
5512 if (i_isMediumFormatFile() && pOther->i_isMediumFormatFile())
5513 {
5514 vrc = RTFileQuerySize(this->i_getLocationFull().c_str(), &cbMediumThis);
5515 if (RT_SUCCESS(vrc))
5516 {
5517 vrc = RTFileQuerySize(pOther->i_getLocationFull().c_str(),
5518 &cbMediumOther);
5519 }
5520
5521 if (RT_FAILURE(vrc))
5522 rc = E_FAIL;
5523 else
5524 {
5525 /*
5526 * Check which merge direction might be more optimal.
5527 * This method is not bullet proof of course as there might
5528 * be overlapping blocks in the images so the file size is
5529 * not the best indicator but it is good enough for our purpose
5530 * and everything else is too complicated, especially when the
5531 * media are used by a running VM.
5532 */
5533
5534 uint32_t mediumVariants = MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized;
5535 uint32_t mediumCaps = MediumFormatCapabilities_CreateDynamic | MediumFormatCapabilities_File;
5536
5537 bool fDynamicOther = pOther->i_getMediumFormat()->i_getCapabilities() & mediumCaps
5538 && pOther->i_getVariant() & ~mediumVariants;
5539 bool fDynamicThis = i_getMediumFormat()->i_getCapabilities() & mediumCaps
5540 && i_getVariant() & ~mediumVariants;
5541 bool fMergeIntoThis = (fDynamicThis && !fDynamicOther)
5542 || (fDynamicThis == fDynamicOther && cbMediumThis > cbMediumOther);
5543 fMergeForward = fMergeIntoThis != fThisParent;
5544 }
5545 }
5546 }
5547 catch (HRESULT aRC) { rc = aRC; }
5548
5549 return rc;
5550}
5551
5552/**
5553 * Prepares this (source) medium, target medium and all intermediate media
5554 * for the merge operation.
5555 *
5556 * This method is to be called prior to calling the #mergeTo() to perform
5557 * necessary consistency checks and place involved media to appropriate
5558 * states. If #mergeTo() is not called or fails, the state modifications
5559 * performed by this method must be undone by #i_cancelMergeTo().
5560 *
5561 * See #mergeTo() for more information about merging.
5562 *
5563 * @param pTarget Target medium.
5564 * @param aMachineId Allowed machine attachment. NULL means do not check.
5565 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
5566 * do not check.
5567 * @param fLockMedia Flag whether to lock the medium lock list or not.
5568 * If set to false and the medium lock list locking fails
5569 * later you must call #i_cancelMergeTo().
5570 * @param fMergeForward Resulting merge direction (out).
5571 * @param pParentForTarget New parent for target medium after merge (out).
5572 * @param aChildrenToReparent Medium lock list containing all children of the
5573 * source which will have to be reparented to the target
5574 * after merge (out).
5575 * @param aMediumLockList Medium locking information (out).
5576 *
5577 * @note Locks medium tree for reading. Locks this object, aTarget and all
5578 * intermediate media for writing.
5579 */
5580HRESULT Medium::i_prepareMergeTo(const ComObjPtr<Medium> &pTarget,
5581 const Guid *aMachineId,
5582 const Guid *aSnapshotId,
5583 bool fLockMedia,
5584 bool &fMergeForward,
5585 ComObjPtr<Medium> &pParentForTarget,
5586 MediumLockList * &aChildrenToReparent,
5587 MediumLockList * &aMediumLockList)
5588{
5589 AssertReturn(pTarget != NULL, E_FAIL);
5590 AssertReturn(pTarget != this, E_FAIL);
5591
5592 HRESULT rc = S_OK;
5593 fMergeForward = false;
5594 pParentForTarget.setNull();
5595 Assert(aChildrenToReparent == NULL);
5596 aChildrenToReparent = NULL;
5597 Assert(aMediumLockList == NULL);
5598 aMediumLockList = NULL;
5599
5600 try
5601 {
5602 // locking: we need the tree lock first because we access parent pointers
5603 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5604
5605 AutoCaller autoCaller(this);
5606 AssertComRCThrowRC(autoCaller.rc());
5607
5608 AutoCaller targetCaller(pTarget);
5609 AssertComRCThrowRC(targetCaller.rc());
5610
5611 /* more sanity checking and figuring out the merge direction */
5612 ComObjPtr<Medium> pMedium = i_getParent();
5613 while (!pMedium.isNull() && pMedium != pTarget)
5614 pMedium = pMedium->i_getParent();
5615 if (pMedium == pTarget)
5616 fMergeForward = false;
5617 else
5618 {
5619 pMedium = pTarget->i_getParent();
5620 while (!pMedium.isNull() && pMedium != this)
5621 pMedium = pMedium->i_getParent();
5622 if (pMedium == this)
5623 fMergeForward = true;
5624 else
5625 {
5626 Utf8Str tgtLoc;
5627 {
5628 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5629 tgtLoc = pTarget->i_getLocationFull();
5630 }
5631
5632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5633 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5634 tr("Media '%s' and '%s' are unrelated"),
5635 m->strLocationFull.c_str(), tgtLoc.c_str());
5636 }
5637 }
5638
5639 /* Build the lock list. */
5640 aMediumLockList = new MediumLockList();
5641 targetCaller.release();
5642 autoCaller.release();
5643 treeLock.release();
5644 if (fMergeForward)
5645 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
5646 pTarget /* pToLockWrite */,
5647 false /* fMediumLockWriteAll */,
5648 NULL,
5649 *aMediumLockList);
5650 else
5651 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5652 pTarget /* pToLockWrite */,
5653 false /* fMediumLockWriteAll */,
5654 NULL,
5655 *aMediumLockList);
5656 treeLock.acquire();
5657 autoCaller.add();
5658 AssertComRCThrowRC(autoCaller.rc());
5659 targetCaller.add();
5660 AssertComRCThrowRC(targetCaller.rc());
5661 if (FAILED(rc))
5662 throw rc;
5663
5664 /* Sanity checking, must be after lock list creation as it depends on
5665 * valid medium states. The medium objects must be accessible. Only
5666 * do this if immediate locking is requested, otherwise it fails when
5667 * we construct a medium lock list for an already running VM. Snapshot
5668 * deletion uses this to simplify its life. */
5669 if (fLockMedia)
5670 {
5671 {
5672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5673 if (m->state != MediumState_Created)
5674 throw i_setStateError();
5675 }
5676 {
5677 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5678 if (pTarget->m->state != MediumState_Created)
5679 throw pTarget->i_setStateError();
5680 }
5681 }
5682
5683 /* check medium attachment and other sanity conditions */
5684 if (fMergeForward)
5685 {
5686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5687 if (i_getChildren().size() > 1)
5688 {
5689 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5690 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5691 m->strLocationFull.c_str(), i_getChildren().size());
5692 }
5693 /* One backreference is only allowed if the machine ID is not empty
5694 * and it matches the machine the medium is attached to (including
5695 * the snapshot ID if not empty). */
5696 if ( m->backRefs.size() != 0
5697 && ( !aMachineId
5698 || m->backRefs.size() != 1
5699 || aMachineId->isZero()
5700 || *i_getFirstMachineBackrefId() != *aMachineId
5701 || ( (!aSnapshotId || !aSnapshotId->isZero())
5702 && *i_getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
5703 throw setError(VBOX_E_OBJECT_IN_USE,
5704 tr("Medium '%s' is attached to %d virtual machines"),
5705 m->strLocationFull.c_str(), m->backRefs.size());
5706 if (m->type == MediumType_Immutable)
5707 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5708 tr("Medium '%s' is immutable"),
5709 m->strLocationFull.c_str());
5710 if (m->type == MediumType_MultiAttach)
5711 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5712 tr("Medium '%s' is multi-attach"),
5713 m->strLocationFull.c_str());
5714 }
5715 else
5716 {
5717 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5718 if (pTarget->i_getChildren().size() > 1)
5719 {
5720 throw setError(VBOX_E_OBJECT_IN_USE,
5721 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5722 pTarget->m->strLocationFull.c_str(),
5723 pTarget->i_getChildren().size());
5724 }
5725 if (pTarget->m->type == MediumType_Immutable)
5726 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5727 tr("Medium '%s' is immutable"),
5728 pTarget->m->strLocationFull.c_str());
5729 if (pTarget->m->type == MediumType_MultiAttach)
5730 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5731 tr("Medium '%s' is multi-attach"),
5732 pTarget->m->strLocationFull.c_str());
5733 }
5734 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
5735 ComObjPtr<Medium> pLastIntermediate = pLast->i_getParent();
5736 for (pLast = pLastIntermediate;
5737 !pLast.isNull() && pLast != pTarget && pLast != this;
5738 pLast = pLast->i_getParent())
5739 {
5740 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
5741 if (pLast->i_getChildren().size() > 1)
5742 {
5743 throw setError(VBOX_E_OBJECT_IN_USE,
5744 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5745 pLast->m->strLocationFull.c_str(),
5746 pLast->i_getChildren().size());
5747 }
5748 if (pLast->m->backRefs.size() != 0)
5749 throw setError(VBOX_E_OBJECT_IN_USE,
5750 tr("Medium '%s' is attached to %d virtual machines"),
5751 pLast->m->strLocationFull.c_str(),
5752 pLast->m->backRefs.size());
5753
5754 }
5755
5756 /* Update medium states appropriately */
5757 {
5758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5759
5760 if (m->state == MediumState_Created)
5761 {
5762 rc = i_markForDeletion();
5763 if (FAILED(rc))
5764 throw rc;
5765 }
5766 else
5767 {
5768 if (fLockMedia)
5769 throw i_setStateError();
5770 else if ( m->state == MediumState_LockedWrite
5771 || m->state == MediumState_LockedRead)
5772 {
5773 /* Either mark it for deletion in locked state or allow
5774 * others to have done so. */
5775 if (m->preLockState == MediumState_Created)
5776 i_markLockedForDeletion();
5777 else if (m->preLockState != MediumState_Deleting)
5778 throw i_setStateError();
5779 }
5780 else
5781 throw i_setStateError();
5782 }
5783 }
5784
5785 if (fMergeForward)
5786 {
5787 /* we will need parent to reparent target */
5788 pParentForTarget = i_getParent();
5789 }
5790 else
5791 {
5792 /* we will need to reparent children of the source */
5793 aChildrenToReparent = new MediumLockList();
5794 for (MediaList::const_iterator it = i_getChildren().begin();
5795 it != i_getChildren().end();
5796 ++it)
5797 {
5798 pMedium = *it;
5799 aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
5800 }
5801 if (fLockMedia && aChildrenToReparent)
5802 {
5803 targetCaller.release();
5804 autoCaller.release();
5805 treeLock.release();
5806 rc = aChildrenToReparent->Lock();
5807 treeLock.acquire();
5808 autoCaller.add();
5809 AssertComRCThrowRC(autoCaller.rc());
5810 targetCaller.add();
5811 AssertComRCThrowRC(targetCaller.rc());
5812 if (FAILED(rc))
5813 throw rc;
5814 }
5815 }
5816 for (pLast = pLastIntermediate;
5817 !pLast.isNull() && pLast != pTarget && pLast != this;
5818 pLast = pLast->i_getParent())
5819 {
5820 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
5821 if (pLast->m->state == MediumState_Created)
5822 {
5823 rc = pLast->i_markForDeletion();
5824 if (FAILED(rc))
5825 throw rc;
5826 }
5827 else
5828 throw pLast->i_setStateError();
5829 }
5830
5831 /* Tweak the lock list in the backward merge case, as the target
5832 * isn't marked to be locked for writing yet. */
5833 if (!fMergeForward)
5834 {
5835 MediumLockList::Base::iterator lockListBegin =
5836 aMediumLockList->GetBegin();
5837 MediumLockList::Base::iterator lockListEnd =
5838 aMediumLockList->GetEnd();
5839 ++lockListEnd;
5840 for (MediumLockList::Base::iterator it = lockListBegin;
5841 it != lockListEnd;
5842 ++it)
5843 {
5844 MediumLock &mediumLock = *it;
5845 if (mediumLock.GetMedium() == pTarget)
5846 {
5847 HRESULT rc2 = mediumLock.UpdateLock(true);
5848 AssertComRC(rc2);
5849 break;
5850 }
5851 }
5852 }
5853
5854 if (fLockMedia)
5855 {
5856 targetCaller.release();
5857 autoCaller.release();
5858 treeLock.release();
5859 rc = aMediumLockList->Lock();
5860 treeLock.acquire();
5861 autoCaller.add();
5862 AssertComRCThrowRC(autoCaller.rc());
5863 targetCaller.add();
5864 AssertComRCThrowRC(targetCaller.rc());
5865 if (FAILED(rc))
5866 {
5867 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5868 throw setError(rc,
5869 tr("Failed to lock media when merging to '%s'"),
5870 pTarget->i_getLocationFull().c_str());
5871 }
5872 }
5873 }
5874 catch (HRESULT aRC) { rc = aRC; }
5875
5876 if (FAILED(rc))
5877 {
5878 if (aMediumLockList)
5879 {
5880 delete aMediumLockList;
5881 aMediumLockList = NULL;
5882 }
5883 if (aChildrenToReparent)
5884 {
5885 delete aChildrenToReparent;
5886 aChildrenToReparent = NULL;
5887 }
5888 }
5889
5890 return rc;
5891}
5892
5893/**
5894 * Merges this medium to the specified medium which must be either its
5895 * direct ancestor or descendant.
5896 *
5897 * Given this medium is SOURCE and the specified medium is TARGET, we will
5898 * get two variants of the merge operation:
5899 *
5900 * forward merge
5901 * ------------------------->
5902 * [Extra] <- SOURCE <- Intermediate <- TARGET
5903 * Any Del Del LockWr
5904 *
5905 *
5906 * backward merge
5907 * <-------------------------
5908 * TARGET <- Intermediate <- SOURCE <- [Extra]
5909 * LockWr Del Del LockWr
5910 *
5911 * Each diagram shows the involved media on the media chain where
5912 * SOURCE and TARGET belong. Under each medium there is a state value which
5913 * the medium must have at a time of the mergeTo() call.
5914 *
5915 * The media in the square braces may be absent (e.g. when the forward
5916 * operation takes place and SOURCE is the base medium, or when the backward
5917 * merge operation takes place and TARGET is the last child in the chain) but if
5918 * they present they are involved too as shown.
5919 *
5920 * Neither the source medium nor intermediate media may be attached to
5921 * any VM directly or in the snapshot, otherwise this method will assert.
5922 *
5923 * The #i_prepareMergeTo() method must be called prior to this method to place
5924 * all involved to necessary states and perform other consistency checks.
5925 *
5926 * If @a aWait is @c true then this method will perform the operation on the
5927 * calling thread and will not return to the caller until the operation is
5928 * completed. When this method succeeds, all intermediate medium objects in
5929 * the chain will be uninitialized, the state of the target medium (and all
5930 * involved extra media) will be restored. @a aMediumLockList will not be
5931 * deleted, whether the operation is successful or not. The caller has to do
5932 * this if appropriate. Note that this (source) medium is not uninitialized
5933 * because of possible AutoCaller instances held by the caller of this method
5934 * on the current thread. It's therefore the responsibility of the caller to
5935 * call Medium::uninit() after releasing all callers.
5936 *
5937 * If @a aWait is @c false then this method will create a thread to perform the
5938 * operation asynchronously and will return immediately. If the operation
5939 * succeeds, the thread will uninitialize the source medium object and all
5940 * intermediate medium objects in the chain, reset the state of the target
5941 * medium (and all involved extra media) and delete @a aMediumLockList.
5942 * If the operation fails, the thread will only reset the states of all
5943 * involved media and delete @a aMediumLockList.
5944 *
5945 * When this method fails (regardless of the @a aWait mode), it is a caller's
5946 * responsibility to undo state changes and delete @a aMediumLockList using
5947 * #i_cancelMergeTo().
5948 *
5949 * If @a aProgress is not NULL but the object it points to is @c null then a new
5950 * progress object will be created and assigned to @a *aProgress on success,
5951 * otherwise the existing progress object is used. If Progress is NULL, then no
5952 * progress object is created/used at all. Note that @a aProgress cannot be
5953 * NULL when @a aWait is @c false (this method will assert in this case).
5954 *
5955 * @param pTarget Target medium.
5956 * @param fMergeForward Merge direction.
5957 * @param pParentForTarget New parent for target medium after merge.
5958 * @param aChildrenToReparent List of children of the source which will have
5959 * to be reparented to the target after merge.
5960 * @param aMediumLockList Medium locking information.
5961 * @param aProgress Where to find/store a Progress object to track operation
5962 * completion.
5963 * @param aWait @c true if this method should block instead of creating
5964 * an asynchronous thread.
5965 * @param aNotify Notify about mediums which metadatа are changed
5966 * during execution of the function.
5967 *
5968 * @note Locks the tree lock for writing. Locks the media from the chain
5969 * for writing.
5970 */
5971HRESULT Medium::i_mergeTo(const ComObjPtr<Medium> &pTarget,
5972 bool fMergeForward,
5973 const ComObjPtr<Medium> &pParentForTarget,
5974 MediumLockList *aChildrenToReparent,
5975 MediumLockList *aMediumLockList,
5976 ComObjPtr<Progress> *aProgress,
5977 bool aWait, bool aNotify)
5978{
5979 AssertReturn(pTarget != NULL, E_FAIL);
5980 AssertReturn(pTarget != this, E_FAIL);
5981 AssertReturn(aMediumLockList != NULL, E_FAIL);
5982 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5983
5984 AutoCaller autoCaller(this);
5985 AssertComRCReturnRC(autoCaller.rc());
5986
5987 AutoCaller targetCaller(pTarget);
5988 AssertComRCReturnRC(targetCaller.rc());
5989
5990 HRESULT rc = S_OK;
5991 ComObjPtr<Progress> pProgress;
5992 Medium::Task *pTask = NULL;
5993
5994 try
5995 {
5996 if (aProgress != NULL)
5997 {
5998 /* use the existing progress object... */
5999 pProgress = *aProgress;
6000
6001 /* ...but create a new one if it is null */
6002 if (pProgress.isNull())
6003 {
6004 Utf8Str tgtName;
6005 {
6006 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6007 tgtName = pTarget->i_getName();
6008 }
6009
6010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6011
6012 pProgress.createObject();
6013 rc = pProgress->init(m->pVirtualBox,
6014 static_cast<IMedium*>(this),
6015 BstrFmt(tr("Merging medium '%s' to '%s'"),
6016 i_getName().c_str(),
6017 tgtName.c_str()).raw(),
6018 TRUE, /* aCancelable */
6019 2, /* Number of opearations */
6020 BstrFmt(tr("Resizing medium '%s' before merge"),
6021 tgtName.c_str()).raw()
6022 );
6023 if (FAILED(rc))
6024 throw rc;
6025 }
6026 }
6027
6028 /* setup task object to carry out the operation sync/async */
6029 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
6030 pParentForTarget, aChildrenToReparent,
6031 pProgress, aMediumLockList,
6032 aWait /* fKeepMediumLockList */,
6033 aNotify);
6034 rc = pTask->rc();
6035 AssertComRC(rc);
6036 if (FAILED(rc))
6037 throw rc;
6038 }
6039 catch (HRESULT aRC) { rc = aRC; }
6040
6041 if (SUCCEEDED(rc))
6042 {
6043 if (aWait)
6044 {
6045 rc = pTask->runNow();
6046 delete pTask;
6047 }
6048 else
6049 rc = pTask->createThread();
6050 pTask = NULL;
6051 if (SUCCEEDED(rc) && aProgress != NULL)
6052 *aProgress = pProgress;
6053 }
6054 else if (pTask != NULL)
6055 delete pTask;
6056
6057 return rc;
6058}
6059
6060/**
6061 * Undoes what #i_prepareMergeTo() did. Must be called if #mergeTo() is not
6062 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
6063 * the medium objects in @a aChildrenToReparent.
6064 *
6065 * @param aChildrenToReparent List of children of the source which will have
6066 * to be reparented to the target after merge.
6067 * @param aMediumLockList Medium locking information.
6068 *
6069 * @note Locks the tree lock for writing. Locks the media from the chain
6070 * for writing.
6071 */
6072void Medium::i_cancelMergeTo(MediumLockList *aChildrenToReparent,
6073 MediumLockList *aMediumLockList)
6074{
6075 AutoCaller autoCaller(this);
6076 AssertComRCReturnVoid(autoCaller.rc());
6077
6078 AssertReturnVoid(aMediumLockList != NULL);
6079
6080 /* Revert media marked for deletion to previous state. */
6081 HRESULT rc;
6082 MediumLockList::Base::const_iterator mediumListBegin =
6083 aMediumLockList->GetBegin();
6084 MediumLockList::Base::const_iterator mediumListEnd =
6085 aMediumLockList->GetEnd();
6086 for (MediumLockList::Base::const_iterator it = mediumListBegin;
6087 it != mediumListEnd;
6088 ++it)
6089 {
6090 const MediumLock &mediumLock = *it;
6091 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6092 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6093
6094 if (pMedium->m->state == MediumState_Deleting)
6095 {
6096 rc = pMedium->i_unmarkForDeletion();
6097 AssertComRC(rc);
6098 }
6099 else if ( ( pMedium->m->state == MediumState_LockedWrite
6100 || pMedium->m->state == MediumState_LockedRead)
6101 && pMedium->m->preLockState == MediumState_Deleting)
6102 {
6103 rc = pMedium->i_unmarkLockedForDeletion();
6104 AssertComRC(rc);
6105 }
6106 }
6107
6108 /* the destructor will do the work */
6109 delete aMediumLockList;
6110
6111 /* unlock the children which had to be reparented, the destructor will do
6112 * the work */
6113 if (aChildrenToReparent)
6114 delete aChildrenToReparent;
6115}
6116
6117/**
6118 * Resizes the media.
6119 *
6120 * If @a aWait is @c true then this method will perform the operation on the
6121 * calling thread and will not return to the caller until the operation is
6122 * completed. When this method succeeds, the state of the target medium (and all
6123 * involved extra media) will be restored. @a aMediumLockList will not be
6124 * deleted, whether the operation is successful or not. The caller has to do
6125 * this if appropriate.
6126 *
6127 * If @a aWait is @c false then this method will create a thread to perform the
6128 * operation asynchronously and will return immediately. The thread will reset
6129 * the state of the target medium (and all involved extra media) and delete
6130 * @a aMediumLockList.
6131 *
6132 * When this method fails (regardless of the @a aWait mode), it is a caller's
6133 * responsibility to undo state changes and delete @a aMediumLockList.
6134 *
6135 * If @a aProgress is not NULL but the object it points to is @c null then a new
6136 * progress object will be created and assigned to @a *aProgress on success,
6137 * otherwise the existing progress object is used. If Progress is NULL, then no
6138 * progress object is created/used at all. Note that @a aProgress cannot be
6139 * NULL when @a aWait is @c false (this method will assert in this case).
6140 *
6141 * @param aLogicalSize New nominal capacity of the medium in bytes.
6142 * @param aMediumLockList Medium locking information.
6143 * @param aProgress Where to find/store a Progress object to track operation
6144 * completion.
6145 * @param aWait @c true if this method should block instead of creating
6146 * an asynchronous thread.
6147 * @param aNotify Notify about mediums which metadatа are changed
6148 * during execution of the function.
6149 *
6150 * @note Locks the media from the chain for writing.
6151 */
6152
6153HRESULT Medium::i_resize(LONG64 aLogicalSize,
6154 MediumLockList *aMediumLockList,
6155 ComObjPtr<Progress> *aProgress,
6156 bool aWait,
6157 bool aNotify)
6158{
6159 AssertReturn(aMediumLockList != NULL, E_FAIL);
6160 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6161
6162 AutoCaller autoCaller(this);
6163 AssertComRCReturnRC(autoCaller.rc());
6164
6165 HRESULT rc = S_OK;
6166 ComObjPtr<Progress> pProgress;
6167 Medium::Task *pTask = NULL;
6168
6169 try
6170 {
6171 if (aProgress != NULL)
6172 {
6173 /* use the existing progress object... */
6174 pProgress = *aProgress;
6175
6176 /* ...but create a new one if it is null */
6177 if (pProgress.isNull())
6178 {
6179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6180
6181 pProgress.createObject();
6182 rc = pProgress->init(m->pVirtualBox,
6183 static_cast <IMedium *>(this),
6184 BstrFmt(tr("Resizing medium '%s'"), m->strLocationFull.c_str()).raw(),
6185 TRUE /* aCancelable */);
6186 if (FAILED(rc))
6187 throw rc;
6188 }
6189 }
6190
6191 /* setup task object to carry out the operation asynchronously */
6192 pTask = new Medium::ResizeTask(this,
6193 aLogicalSize,
6194 pProgress,
6195 aMediumLockList,
6196 aWait /* fKeepMediumLockList */,
6197 aNotify);
6198 rc = pTask->rc();
6199 AssertComRC(rc);
6200 if (FAILED(rc))
6201 throw rc;
6202 }
6203 catch (HRESULT aRC) { rc = aRC; }
6204
6205 if (SUCCEEDED(rc))
6206 {
6207 if (aWait)
6208 {
6209 rc = pTask->runNow();
6210 delete pTask;
6211 }
6212 else
6213 rc = pTask->createThread();
6214 pTask = NULL;
6215 if (SUCCEEDED(rc) && aProgress != NULL)
6216 *aProgress = pProgress;
6217 }
6218 else if (pTask != NULL)
6219 delete pTask;
6220
6221 return rc;
6222}
6223
6224/**
6225 * Fix the parent UUID of all children to point to this medium as their
6226 * parent.
6227 */
6228HRESULT Medium::i_fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
6229{
6230 /** @todo r=klaus The code below needs to be double checked with regard
6231 * to lock order violations, it probably causes lock order issues related
6232 * to the AutoCaller usage. Likewise the code using this method seems
6233 * problematic. */
6234 Assert(!isWriteLockOnCurrentThread());
6235 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6236 MediumLockList mediumLockList;
6237 HRESULT rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6238 NULL /* pToLockWrite */,
6239 false /* fMediumLockWriteAll */,
6240 this,
6241 mediumLockList);
6242 AssertComRCReturnRC(rc);
6243
6244 try
6245 {
6246 PVDISK hdd;
6247 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6248 ComAssertRCThrow(vrc, E_FAIL);
6249
6250 try
6251 {
6252 MediumLockList::Base::iterator lockListBegin =
6253 mediumLockList.GetBegin();
6254 MediumLockList::Base::iterator lockListEnd =
6255 mediumLockList.GetEnd();
6256 for (MediumLockList::Base::iterator it = lockListBegin;
6257 it != lockListEnd;
6258 ++it)
6259 {
6260 MediumLock &mediumLock = *it;
6261 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6262 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6263
6264 // open the medium
6265 vrc = VDOpen(hdd,
6266 pMedium->m->strFormat.c_str(),
6267 pMedium->m->strLocationFull.c_str(),
6268 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
6269 pMedium->m->vdImageIfaces);
6270 if (RT_FAILURE(vrc))
6271 throw vrc;
6272 }
6273
6274 MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
6275 MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
6276 for (MediumLockList::Base::iterator it = childrenBegin;
6277 it != childrenEnd;
6278 ++it)
6279 {
6280 Medium *pMedium = it->GetMedium();
6281 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
6282 vrc = VDOpen(hdd,
6283 pMedium->m->strFormat.c_str(),
6284 pMedium->m->strLocationFull.c_str(),
6285 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
6286 pMedium->m->vdImageIfaces);
6287 if (RT_FAILURE(vrc))
6288 throw vrc;
6289
6290 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
6291 if (RT_FAILURE(vrc))
6292 throw vrc;
6293
6294 vrc = VDClose(hdd, false /* fDelete */);
6295 if (RT_FAILURE(vrc))
6296 throw vrc;
6297 }
6298 }
6299 catch (HRESULT aRC) { rc = aRC; }
6300 catch (int aVRC)
6301 {
6302 rc = setErrorBoth(E_FAIL, aVRC,
6303 tr("Could not update medium UUID references to parent '%s' (%s)"),
6304 m->strLocationFull.c_str(),
6305 i_vdError(aVRC).c_str());
6306 }
6307
6308 VDDestroy(hdd);
6309 }
6310 catch (HRESULT aRC) { rc = aRC; }
6311
6312 return rc;
6313}
6314
6315/**
6316 *
6317 * @note Similar code exists in i_taskExportHandler.
6318 */
6319HRESULT Medium::i_addRawToFss(const char *aFilename, SecretKeyStore *pKeyStore, RTVFSFSSTREAM hVfsFssDst,
6320 const ComObjPtr<Progress> &aProgress, bool fSparse)
6321{
6322 AutoCaller autoCaller(this);
6323 HRESULT hrc = autoCaller.rc();
6324 if (SUCCEEDED(hrc))
6325 {
6326 /*
6327 * Get a readonly hdd for this medium.
6328 */
6329 MediumCryptoFilterSettings CryptoSettingsRead;
6330 MediumLockList SourceMediumLockList;
6331 PVDISK pHdd;
6332 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pHdd, &SourceMediumLockList, &CryptoSettingsRead);
6333 if (SUCCEEDED(hrc))
6334 {
6335 /*
6336 * Create a VFS file interface to the HDD and attach a progress wrapper
6337 * that monitors the progress reading of the raw image. The image will
6338 * be read twice if hVfsFssDst does sparse processing.
6339 */
6340 RTVFSFILE hVfsFileDisk = NIL_RTVFSFILE;
6341 int vrc = VDCreateVfsFileFromDisk(pHdd, 0 /*fFlags*/, &hVfsFileDisk);
6342 if (RT_SUCCESS(vrc))
6343 {
6344 RTVFSFILE hVfsFileProgress = NIL_RTVFSFILE;
6345 vrc = RTVfsCreateProgressForFile(hVfsFileDisk, aProgress->i_iprtProgressCallback, &*aProgress,
6346 RTVFSPROGRESS_F_CANCELABLE | RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ,
6347 VDGetSize(pHdd, VD_LAST_IMAGE) * (fSparse ? 2 : 1) /*cbExpectedRead*/,
6348 0 /*cbExpectedWritten*/, &hVfsFileProgress);
6349 RTVfsFileRelease(hVfsFileDisk);
6350 if (RT_SUCCESS(vrc))
6351 {
6352 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFileProgress);
6353 RTVfsFileRelease(hVfsFileProgress);
6354
6355 vrc = RTVfsFsStrmAdd(hVfsFssDst, aFilename, hVfsObj, 0 /*fFlags*/);
6356 RTVfsObjRelease(hVfsObj);
6357 if (RT_FAILURE(vrc))
6358 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to add '%s' to output (%Rrc)"), aFilename, vrc);
6359 }
6360 else
6361 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
6362 tr("RTVfsCreateProgressForFile failed when processing '%s' (%Rrc)"), aFilename, vrc);
6363 }
6364 else
6365 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("VDCreateVfsFileFromDisk failed for '%s' (%Rrc)"), aFilename, vrc);
6366 VDDestroy(pHdd);
6367 }
6368 }
6369 return hrc;
6370}
6371
6372/**
6373 * Used by IAppliance to export disk images.
6374 *
6375 * @param aFilename Filename to create (UTF8).
6376 * @param aFormat Medium format for creating @a aFilename.
6377 * @param aVariant Which exact image format variant to use for the
6378 * destination image.
6379 * @param pKeyStore The optional key store for decrypting the data for
6380 * encrypted media during the export.
6381 * @param hVfsIosDst The destination I/O stream object.
6382 * @param aProgress Progress object to use.
6383 * @return
6384 *
6385 * @note The source format is defined by the Medium instance.
6386 */
6387HRESULT Medium::i_exportFile(const char *aFilename,
6388 const ComObjPtr<MediumFormat> &aFormat,
6389 MediumVariant_T aVariant,
6390 SecretKeyStore *pKeyStore,
6391 RTVFSIOSTREAM hVfsIosDst,
6392 const ComObjPtr<Progress> &aProgress)
6393{
6394 AssertPtrReturn(aFilename, E_INVALIDARG);
6395 AssertReturn(aFormat.isNotNull(), E_INVALIDARG);
6396 AssertReturn(aProgress.isNotNull(), E_INVALIDARG);
6397
6398 AutoCaller autoCaller(this);
6399 HRESULT hrc = autoCaller.rc();
6400 if (SUCCEEDED(hrc))
6401 {
6402 /*
6403 * Setup VD interfaces.
6404 */
6405 PVDINTERFACE pVDImageIfaces = m->vdImageIfaces;
6406 PVDINTERFACEIO pVfsIoIf;
6407 int vrc = VDIfCreateFromVfsStream(hVfsIosDst, RTFILE_O_WRITE, &pVfsIoIf);
6408 if (RT_SUCCESS(vrc))
6409 {
6410 vrc = VDInterfaceAdd(&pVfsIoIf->Core, "Medium::ExportTaskVfsIos", VDINTERFACETYPE_IO,
6411 pVfsIoIf, sizeof(VDINTERFACEIO), &pVDImageIfaces);
6412 if (RT_SUCCESS(vrc))
6413 {
6414 /*
6415 * Get a readonly hdd for this medium (source).
6416 */
6417 MediumCryptoFilterSettings CryptoSettingsRead;
6418 MediumLockList SourceMediumLockList;
6419 PVDISK pSrcHdd;
6420 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pSrcHdd, &SourceMediumLockList, &CryptoSettingsRead);
6421 if (SUCCEEDED(hrc))
6422 {
6423 /*
6424 * Create the target medium.
6425 */
6426 Utf8Str strDstFormat(aFormat->i_getId());
6427
6428 /* ensure the target directory exists */
6429 uint64_t fDstCapabilities = aFormat->i_getCapabilities();
6430 if (fDstCapabilities & MediumFormatCapabilities_File)
6431 {
6432 Utf8Str strDstLocation(aFilename);
6433 hrc = VirtualBox::i_ensureFilePathExists(strDstLocation.c_str(),
6434 !(aVariant & MediumVariant_NoCreateDir) /* fCreate */);
6435 }
6436 if (SUCCEEDED(hrc))
6437 {
6438 PVDISK pDstHdd;
6439 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDstHdd);
6440 if (RT_SUCCESS(vrc))
6441 {
6442 /*
6443 * Create an interface for getting progress callbacks.
6444 */
6445 VDINTERFACEPROGRESS ProgressIf = VDINTERFACEPROGRESS_INITALIZER(aProgress->i_vdProgressCallback);
6446 PVDINTERFACE pProgress = NULL;
6447 vrc = VDInterfaceAdd(&ProgressIf.Core, "export-progress", VDINTERFACETYPE_PROGRESS,
6448 &*aProgress, sizeof(ProgressIf), &pProgress);
6449 AssertRC(vrc);
6450
6451 /*
6452 * Do the exporting.
6453 */
6454 vrc = VDCopy(pSrcHdd,
6455 VD_LAST_IMAGE,
6456 pDstHdd,
6457 strDstFormat.c_str(),
6458 aFilename,
6459 false /* fMoveByRename */,
6460 0 /* cbSize */,
6461 aVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
6462 NULL /* pDstUuid */,
6463 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
6464 pProgress,
6465 pVDImageIfaces,
6466 NULL);
6467 if (RT_SUCCESS(vrc))
6468 hrc = S_OK;
6469 else
6470 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Could not create the exported medium '%s'%s"),
6471 aFilename, i_vdError(vrc).c_str());
6472 VDDestroy(pDstHdd);
6473 }
6474 else
6475 hrc = setErrorVrc(vrc);
6476 }
6477 }
6478 VDDestroy(pSrcHdd);
6479 }
6480 else
6481 hrc = setErrorVrc(vrc, "VDInterfaceAdd -> %Rrc", vrc);
6482 VDIfDestroyFromVfsStream(pVfsIoIf);
6483 }
6484 else
6485 hrc = setErrorVrc(vrc, "VDIfCreateFromVfsStream -> %Rrc", vrc);
6486 }
6487 return hrc;
6488}
6489
6490/**
6491 * Used by IAppliance to import disk images.
6492 *
6493 * @param aFilename Filename to read (UTF8).
6494 * @param aFormat Medium format for reading @a aFilename.
6495 * @param aVariant Which exact image format variant to use
6496 * for the destination image.
6497 * @param aVfsIosSrc Handle to the source I/O stream.
6498 * @param aParent Parent medium. May be NULL.
6499 * @param aProgress Progress object to use.
6500 * @param aNotify Notify about mediums which metadatа are changed
6501 * during execution of the function.
6502 * @return
6503 * @note The destination format is defined by the Medium instance.
6504 *
6505 * @todo The only consumer of this method (Appliance::i_importOneDiskImage) is
6506 * already on a worker thread, so perhaps consider bypassing the thread
6507 * here and run in the task synchronously? VBoxSVC has enough threads as
6508 * it is...
6509 */
6510HRESULT Medium::i_importFile(const char *aFilename,
6511 const ComObjPtr<MediumFormat> &aFormat,
6512 MediumVariant_T aVariant,
6513 RTVFSIOSTREAM aVfsIosSrc,
6514 const ComObjPtr<Medium> &aParent,
6515 const ComObjPtr<Progress> &aProgress,
6516 bool aNotify)
6517{
6518 /** @todo r=klaus The code below needs to be double checked with regard
6519 * to lock order violations, it probably causes lock order issues related
6520 * to the AutoCaller usage. */
6521 AssertPtrReturn(aFilename, E_INVALIDARG);
6522 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
6523 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
6524
6525 AutoCaller autoCaller(this);
6526 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6527
6528 HRESULT rc = S_OK;
6529 Medium::Task *pTask = NULL;
6530
6531 try
6532 {
6533 // locking: we need the tree lock first because we access parent pointers
6534 // and we need to write-lock the media involved
6535 uint32_t cHandles = 2;
6536 LockHandle* pHandles[3] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
6537 this->lockHandle() };
6538 /* Only add parent to the lock if it is not null */
6539 if (!aParent.isNull())
6540 pHandles[cHandles++] = aParent->lockHandle();
6541 AutoWriteLock alock(cHandles,
6542 pHandles
6543 COMMA_LOCKVAL_SRC_POS);
6544
6545 if ( m->state != MediumState_NotCreated
6546 && m->state != MediumState_Created)
6547 throw i_setStateError();
6548
6549 /* Build the target lock list. */
6550 MediumLockList *pTargetMediumLockList(new MediumLockList());
6551 alock.release();
6552 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6553 this /* pToLockWrite */,
6554 false /* fMediumLockWriteAll */,
6555 aParent,
6556 *pTargetMediumLockList);
6557 alock.acquire();
6558 if (FAILED(rc))
6559 {
6560 delete pTargetMediumLockList;
6561 throw rc;
6562 }
6563
6564 alock.release();
6565 rc = pTargetMediumLockList->Lock();
6566 alock.acquire();
6567 if (FAILED(rc))
6568 {
6569 delete pTargetMediumLockList;
6570 throw setError(rc,
6571 tr("Failed to lock target media '%s'"),
6572 i_getLocationFull().c_str());
6573 }
6574
6575 /* setup task object to carry out the operation asynchronously */
6576 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat, aVariant,
6577 aVfsIosSrc, aParent, pTargetMediumLockList, false, aNotify);
6578 rc = pTask->rc();
6579 AssertComRC(rc);
6580 if (FAILED(rc))
6581 throw rc;
6582
6583 if (m->state == MediumState_NotCreated)
6584 m->state = MediumState_Creating;
6585 }
6586 catch (HRESULT aRC) { rc = aRC; }
6587
6588 if (SUCCEEDED(rc))
6589 {
6590 rc = pTask->createThread();
6591 pTask = NULL;
6592 }
6593 else if (pTask != NULL)
6594 delete pTask;
6595
6596 return rc;
6597}
6598
6599/**
6600 * Internal version of the public CloneTo API which allows to enable certain
6601 * optimizations to improve speed during VM cloning.
6602 *
6603 * @param aTarget Target medium
6604 * @param aVariant Which exact image format variant to use
6605 * for the destination image.
6606 * @param aParent Parent medium. May be NULL.
6607 * @param aProgress Progress object to use.
6608 * @param idxSrcImageSame The last image in the source chain which has the
6609 * same content as the given image in the destination
6610 * chain. Use UINT32_MAX to disable this optimization.
6611 * @param idxDstImageSame The last image in the destination chain which has the
6612 * same content as the given image in the source chain.
6613 * Use UINT32_MAX to disable this optimization.
6614 * @param aNotify Notify about mediums which metadatа are changed
6615 * during execution of the function.
6616 * @return
6617 */
6618HRESULT Medium::i_cloneToEx(const ComObjPtr<Medium> &aTarget, MediumVariant_T aVariant,
6619 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
6620 uint32_t idxSrcImageSame, uint32_t idxDstImageSame, bool aNotify)
6621{
6622 /** @todo r=klaus The code below needs to be double checked with regard
6623 * to lock order violations, it probably causes lock order issues related
6624 * to the AutoCaller usage. */
6625 CheckComArgNotNull(aTarget);
6626 CheckComArgOutPointerValid(aProgress);
6627 ComAssertRet(aTarget != this, E_INVALIDARG);
6628
6629 AutoCaller autoCaller(this);
6630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6631
6632 HRESULT rc = S_OK;
6633 ComObjPtr<Progress> pProgress;
6634 Medium::Task *pTask = NULL;
6635
6636 try
6637 {
6638 // locking: we need the tree lock first because we access parent pointers
6639 // and we need to write-lock the media involved
6640 uint32_t cHandles = 3;
6641 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
6642 this->lockHandle(),
6643 aTarget->lockHandle() };
6644 /* Only add parent to the lock if it is not null */
6645 if (!aParent.isNull())
6646 pHandles[cHandles++] = aParent->lockHandle();
6647 AutoWriteLock alock(cHandles,
6648 pHandles
6649 COMMA_LOCKVAL_SRC_POS);
6650
6651 if ( aTarget->m->state != MediumState_NotCreated
6652 && aTarget->m->state != MediumState_Created)
6653 throw aTarget->i_setStateError();
6654
6655 /* Build the source lock list. */
6656 MediumLockList *pSourceMediumLockList(new MediumLockList());
6657 alock.release();
6658 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6659 NULL /* pToLockWrite */,
6660 false /* fMediumLockWriteAll */,
6661 NULL,
6662 *pSourceMediumLockList);
6663 alock.acquire();
6664 if (FAILED(rc))
6665 {
6666 delete pSourceMediumLockList;
6667 throw rc;
6668 }
6669
6670 /* Build the target lock list (including the to-be parent chain). */
6671 MediumLockList *pTargetMediumLockList(new MediumLockList());
6672 alock.release();
6673 rc = aTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
6674 aTarget /* pToLockWrite */,
6675 false /* fMediumLockWriteAll */,
6676 aParent,
6677 *pTargetMediumLockList);
6678 alock.acquire();
6679 if (FAILED(rc))
6680 {
6681 delete pSourceMediumLockList;
6682 delete pTargetMediumLockList;
6683 throw rc;
6684 }
6685
6686 alock.release();
6687 rc = pSourceMediumLockList->Lock();
6688 alock.acquire();
6689 if (FAILED(rc))
6690 {
6691 delete pSourceMediumLockList;
6692 delete pTargetMediumLockList;
6693 throw setError(rc,
6694 tr("Failed to lock source media '%s'"),
6695 i_getLocationFull().c_str());
6696 }
6697 alock.release();
6698 rc = pTargetMediumLockList->Lock();
6699 alock.acquire();
6700 if (FAILED(rc))
6701 {
6702 delete pSourceMediumLockList;
6703 delete pTargetMediumLockList;
6704 throw setError(rc,
6705 tr("Failed to lock target media '%s'"),
6706 aTarget->i_getLocationFull().c_str());
6707 }
6708
6709 pProgress.createObject();
6710 rc = pProgress->init(m->pVirtualBox,
6711 static_cast <IMedium *>(this),
6712 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
6713 TRUE /* aCancelable */);
6714 if (FAILED(rc))
6715 {
6716 delete pSourceMediumLockList;
6717 delete pTargetMediumLockList;
6718 throw rc;
6719 }
6720
6721 /* setup task object to carry out the operation asynchronously */
6722 pTask = new Medium::CloneTask(this, pProgress, aTarget, aVariant,
6723 aParent, idxSrcImageSame,
6724 idxDstImageSame, pSourceMediumLockList,
6725 pTargetMediumLockList, false, false, aNotify);
6726 rc = pTask->rc();
6727 AssertComRC(rc);
6728 if (FAILED(rc))
6729 throw rc;
6730
6731 if (aTarget->m->state == MediumState_NotCreated)
6732 aTarget->m->state = MediumState_Creating;
6733 }
6734 catch (HRESULT aRC) { rc = aRC; }
6735
6736 if (SUCCEEDED(rc))
6737 {
6738 rc = pTask->createThread();
6739 pTask = NULL;
6740 if (SUCCEEDED(rc))
6741 pProgress.queryInterfaceTo(aProgress);
6742 }
6743 else if (pTask != NULL)
6744 delete pTask;
6745
6746 return rc;
6747}
6748
6749/**
6750 * Returns the key identifier for this medium if encryption is configured.
6751 *
6752 * @returns Key identifier or empty string if no encryption is configured.
6753 */
6754const Utf8Str& Medium::i_getKeyId()
6755{
6756 ComObjPtr<Medium> pBase = i_getBase();
6757
6758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6759
6760 settings::StringsMap::const_iterator it = pBase->m->mapProperties.find("CRYPT/KeyId");
6761 if (it == pBase->m->mapProperties.end())
6762 return Utf8Str::Empty;
6763
6764 return it->second;
6765}
6766
6767
6768/**
6769 * Returns all filter related properties.
6770 *
6771 * @returns COM status code.
6772 * @param aReturnNames Where to store the properties names on success.
6773 * @param aReturnValues Where to store the properties values on success.
6774 */
6775HRESULT Medium::i_getFilterProperties(std::vector<com::Utf8Str> &aReturnNames,
6776 std::vector<com::Utf8Str> &aReturnValues)
6777{
6778 std::vector<com::Utf8Str> aPropNames;
6779 std::vector<com::Utf8Str> aPropValues;
6780 HRESULT hrc = getProperties(Utf8Str(""), aPropNames, aPropValues);
6781
6782 if (SUCCEEDED(hrc))
6783 {
6784 unsigned cReturnSize = 0;
6785 aReturnNames.resize(0);
6786 aReturnValues.resize(0);
6787 for (unsigned idx = 0; idx < aPropNames.size(); idx++)
6788 {
6789 if (i_isPropertyForFilter(aPropNames[idx]))
6790 {
6791 aReturnNames.resize(cReturnSize + 1);
6792 aReturnValues.resize(cReturnSize + 1);
6793 aReturnNames[cReturnSize] = aPropNames[idx];
6794 aReturnValues[cReturnSize] = aPropValues[idx];
6795 cReturnSize++;
6796 }
6797 }
6798 }
6799
6800 return hrc;
6801}
6802
6803/**
6804 * Preparation to move this medium to a new location
6805 *
6806 * @param aLocation Location of the storage unit. If the location is a FS-path,
6807 * then it can be relative to the VirtualBox home directory.
6808 *
6809 * @note Must be called from under this object's write lock.
6810 */
6811HRESULT Medium::i_preparationForMoving(const Utf8Str &aLocation)
6812{
6813 HRESULT rc = E_FAIL;
6814
6815 if (i_getLocationFull() != aLocation)
6816 {
6817 m->strNewLocationFull = aLocation;
6818 m->fMoveThisMedium = true;
6819 rc = S_OK;
6820 }
6821
6822 return rc;
6823}
6824
6825/**
6826 * Checking whether current operation "moving" or not
6827 */
6828bool Medium::i_isMoveOperation(const ComObjPtr<Medium> &aTarget) const
6829{
6830 RT_NOREF(aTarget);
6831 return (m->fMoveThisMedium == true) ? true:false; /** @todo r=bird: this is not an obfuscation contest! */
6832}
6833
6834bool Medium::i_resetMoveOperationData()
6835{
6836 m->strNewLocationFull.setNull();
6837 m->fMoveThisMedium = false;
6838 return true;
6839}
6840
6841Utf8Str Medium::i_getNewLocationForMoving() const
6842{
6843 if (m->fMoveThisMedium == true)
6844 return m->strNewLocationFull;
6845 else
6846 return Utf8Str();
6847}
6848////////////////////////////////////////////////////////////////////////////////
6849//
6850// Private methods
6851//
6852////////////////////////////////////////////////////////////////////////////////
6853
6854/**
6855 * Queries information from the medium.
6856 *
6857 * As a result of this call, the accessibility state and data members such as
6858 * size and description will be updated with the current information.
6859 *
6860 * @note This method may block during a system I/O call that checks storage
6861 * accessibility.
6862 *
6863 * @note Caller MUST NOT hold the media tree or medium lock.
6864 *
6865 * @note Locks m->pParent for reading. Locks this object for writing.
6866 *
6867 * @param fSetImageId Whether to reset the UUID contained in the image file
6868 * to the UUID in the medium instance data (see SetIDs())
6869 * @param fSetParentId Whether to reset the parent UUID contained in the image
6870 * file to the parent UUID in the medium instance data (see
6871 * SetIDs())
6872 * @param autoCaller
6873 * @return
6874 */
6875HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
6876{
6877 Assert(!isWriteLockOnCurrentThread());
6878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6879
6880 if ( ( m->state != MediumState_Created
6881 && m->state != MediumState_Inaccessible
6882 && m->state != MediumState_LockedRead)
6883 || m->fClosing)
6884 return E_FAIL;
6885
6886 HRESULT rc = S_OK;
6887
6888 int vrc = VINF_SUCCESS;
6889
6890 /* check if a blocking i_queryInfo() call is in progress on some other thread,
6891 * and wait for it to finish if so instead of querying data ourselves */
6892 if (m->queryInfoRunning)
6893 {
6894 Assert( m->state == MediumState_LockedRead
6895 || m->state == MediumState_LockedWrite);
6896
6897 while (m->queryInfoRunning)
6898 {
6899 alock.release();
6900 /* must not hold the object lock now */
6901 Assert(!isWriteLockOnCurrentThread());
6902 {
6903 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
6904 }
6905 alock.acquire();
6906 }
6907
6908 return S_OK;
6909 }
6910
6911 bool success = false;
6912 Utf8Str lastAccessError;
6913
6914 /* are we dealing with a new medium constructed using the existing
6915 * location? */
6916 bool isImport = m->id.isZero();
6917 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
6918
6919 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
6920 * media because that would prevent necessary modifications
6921 * when opening media of some third-party formats for the first
6922 * time in VirtualBox (such as VMDK for which VDOpen() needs to
6923 * generate an UUID if it is missing) */
6924 if ( m->hddOpenMode == OpenReadOnly
6925 || m->type == MediumType_Readonly
6926 || (!isImport && !fSetImageId && !fSetParentId)
6927 )
6928 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
6929
6930 /* Open shareable medium with the appropriate flags */
6931 if (m->type == MediumType_Shareable)
6932 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6933
6934 /* Lock the medium, which makes the behavior much more consistent, must be
6935 * done before dropping the object lock and setting queryInfoRunning. */
6936 ComPtr<IToken> pToken;
6937 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
6938 rc = LockRead(pToken.asOutParam());
6939 else
6940 rc = LockWrite(pToken.asOutParam());
6941 if (FAILED(rc)) return rc;
6942
6943 /* Copies of the input state fields which are not read-only,
6944 * as we're dropping the lock. CAUTION: be extremely careful what
6945 * you do with the contents of this medium object, as you will
6946 * create races if there are concurrent changes. */
6947 Utf8Str format(m->strFormat);
6948 Utf8Str location(m->strLocationFull);
6949 ComObjPtr<MediumFormat> formatObj = m->formatObj;
6950
6951 /* "Output" values which can't be set because the lock isn't held
6952 * at the time the values are determined. */
6953 Guid mediumId = m->id;
6954 uint64_t mediumSize = 0;
6955 uint64_t mediumLogicalSize = 0;
6956
6957 /* Flag whether a base image has a non-zero parent UUID and thus
6958 * need repairing after it was closed again. */
6959 bool fRepairImageZeroParentUuid = false;
6960
6961 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
6962
6963 /* must be set before leaving the object lock the first time */
6964 m->queryInfoRunning = true;
6965
6966 /* must leave object lock now, because a lock from a higher lock class
6967 * is needed and also a lengthy operation is coming */
6968 alock.release();
6969 autoCaller.release();
6970
6971 /* Note that taking the queryInfoSem after leaving the object lock above
6972 * can lead to short spinning of the loops waiting for i_queryInfo() to
6973 * complete. This is unavoidable since the other order causes a lock order
6974 * violation: here it would be requesting the object lock (at the beginning
6975 * of the method), then queryInfoSem, and below the other way round. */
6976 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
6977
6978 /* take the opportunity to have a media tree lock, released initially */
6979 Assert(!isWriteLockOnCurrentThread());
6980 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6981 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6982 treeLock.release();
6983
6984 /* re-take the caller, but not the object lock, to keep uninit away */
6985 autoCaller.add();
6986 if (FAILED(autoCaller.rc()))
6987 {
6988 m->queryInfoRunning = false;
6989 return autoCaller.rc();
6990 }
6991
6992 try
6993 {
6994 /* skip accessibility checks for host drives */
6995 if (m->hostDrive)
6996 {
6997 success = true;
6998 throw S_OK;
6999 }
7000
7001 PVDISK hdd;
7002 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7003 ComAssertRCThrow(vrc, E_FAIL);
7004
7005 try
7006 {
7007 /** @todo This kind of opening of media is assuming that diff
7008 * media can be opened as base media. Should be documented that
7009 * it must work for all medium format backends. */
7010 vrc = VDOpen(hdd,
7011 format.c_str(),
7012 location.c_str(),
7013 uOpenFlags | m->uOpenFlagsDef,
7014 m->vdImageIfaces);
7015 if (RT_FAILURE(vrc))
7016 {
7017 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
7018 location.c_str(), i_vdError(vrc).c_str());
7019 throw S_OK;
7020 }
7021
7022 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
7023 {
7024 /* Modify the UUIDs if necessary. The associated fields are
7025 * not modified by other code, so no need to copy. */
7026 if (fSetImageId)
7027 {
7028 alock.acquire();
7029 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
7030 alock.release();
7031 if (RT_FAILURE(vrc))
7032 {
7033 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
7034 location.c_str(), i_vdError(vrc).c_str());
7035 throw S_OK;
7036 }
7037 mediumId = m->uuidImage;
7038 }
7039 if (fSetParentId)
7040 {
7041 alock.acquire();
7042 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
7043 alock.release();
7044 if (RT_FAILURE(vrc))
7045 {
7046 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
7047 location.c_str(), i_vdError(vrc).c_str());
7048 throw S_OK;
7049 }
7050 }
7051 /* zap the information, these are no long-term members */
7052 alock.acquire();
7053 unconst(m->uuidImage).clear();
7054 unconst(m->uuidParentImage).clear();
7055 alock.release();
7056
7057 /* check the UUID */
7058 RTUUID uuid;
7059 vrc = VDGetUuid(hdd, 0, &uuid);
7060 ComAssertRCThrow(vrc, E_FAIL);
7061
7062 if (isImport)
7063 {
7064 mediumId = uuid;
7065
7066 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
7067 // only when importing a VDMK that has no UUID, create one in memory
7068 mediumId.create();
7069 }
7070 else
7071 {
7072 Assert(!mediumId.isZero());
7073
7074 if (mediumId != uuid)
7075 {
7076 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7077 lastAccessError = Utf8StrFmt(
7078 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
7079 &uuid,
7080 location.c_str(),
7081 mediumId.raw(),
7082 pVirtualBox->i_settingsFilePath().c_str());
7083 throw S_OK;
7084 }
7085 }
7086 }
7087 else
7088 {
7089 /* the backend does not support storing UUIDs within the
7090 * underlying storage so use what we store in XML */
7091
7092 if (fSetImageId)
7093 {
7094 /* set the UUID if an API client wants to change it */
7095 alock.acquire();
7096 mediumId = m->uuidImage;
7097 alock.release();
7098 }
7099 else if (isImport)
7100 {
7101 /* generate an UUID for an imported UUID-less medium */
7102 mediumId.create();
7103 }
7104 }
7105
7106 /* set the image uuid before the below parent uuid handling code
7107 * might place it somewhere in the media tree, so that the medium
7108 * UUID is valid at this point */
7109 alock.acquire();
7110 if (isImport || fSetImageId)
7111 unconst(m->id) = mediumId;
7112 alock.release();
7113
7114 /* get the medium variant */
7115 unsigned uImageFlags;
7116 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7117 ComAssertRCThrow(vrc, E_FAIL);
7118 alock.acquire();
7119 m->variant = (MediumVariant_T)uImageFlags;
7120 alock.release();
7121
7122 /* check/get the parent uuid and update corresponding state */
7123 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
7124 {
7125 RTUUID parentId;
7126 vrc = VDGetParentUuid(hdd, 0, &parentId);
7127 ComAssertRCThrow(vrc, E_FAIL);
7128
7129 /* streamOptimized VMDK images are only accepted as base
7130 * images, as this allows automatic repair of OVF appliances.
7131 * Since such images don't support random writes they will not
7132 * be created for diff images. Only an overly smart user might
7133 * manually create this case. Too bad for him. */
7134 if ( (isImport || fSetParentId)
7135 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
7136 {
7137 /* the parent must be known to us. Note that we freely
7138 * call locking methods of mVirtualBox and parent, as all
7139 * relevant locks must be already held. There may be no
7140 * concurrent access to the just opened medium on other
7141 * threads yet (and init() will fail if this method reports
7142 * MediumState_Inaccessible) */
7143
7144 ComObjPtr<Medium> pParent;
7145 if (RTUuidIsNull(&parentId))
7146 rc = VBOX_E_OBJECT_NOT_FOUND;
7147 else
7148 rc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
7149 if (FAILED(rc))
7150 {
7151 if (fSetImageId && !fSetParentId)
7152 {
7153 /* If the image UUID gets changed for an existing
7154 * image then the parent UUID can be stale. In such
7155 * cases clear the parent information. The parent
7156 * information may/will be re-set later if the
7157 * API client wants to adjust a complete medium
7158 * hierarchy one by one. */
7159 rc = S_OK;
7160 alock.acquire();
7161 RTUuidClear(&parentId);
7162 vrc = VDSetParentUuid(hdd, 0, &parentId);
7163 alock.release();
7164 ComAssertRCThrow(vrc, E_FAIL);
7165 }
7166 else
7167 {
7168 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
7169 &parentId, location.c_str(),
7170 pVirtualBox->i_settingsFilePath().c_str());
7171 throw S_OK;
7172 }
7173 }
7174
7175 /* must drop the caller before taking the tree lock */
7176 autoCaller.release();
7177 /* we set m->pParent & children() */
7178 treeLock.acquire();
7179 autoCaller.add();
7180 if (FAILED(autoCaller.rc()))
7181 throw autoCaller.rc();
7182
7183 if (m->pParent)
7184 i_deparent();
7185
7186 if (!pParent.isNull())
7187 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7188 {
7189 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
7190 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7191 tr("Cannot open differencing image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
7192 pParent->m->strLocationFull.c_str());
7193 }
7194 i_setParent(pParent);
7195
7196 treeLock.release();
7197 }
7198 else
7199 {
7200 /* must drop the caller before taking the tree lock */
7201 autoCaller.release();
7202 /* we access m->pParent */
7203 treeLock.acquire();
7204 autoCaller.add();
7205 if (FAILED(autoCaller.rc()))
7206 throw autoCaller.rc();
7207
7208 /* check that parent UUIDs match. Note that there's no need
7209 * for the parent's AutoCaller (our lifetime is bound to
7210 * it) */
7211
7212 if (m->pParent.isNull())
7213 {
7214 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
7215 * and 3.1.0-3.1.8 there are base images out there
7216 * which have a non-zero parent UUID. No point in
7217 * complaining about them, instead automatically
7218 * repair the problem. Later we can bring back the
7219 * error message, but we should wait until really
7220 * most users have repaired their images, either with
7221 * VBoxFixHdd or this way. */
7222#if 1
7223 fRepairImageZeroParentUuid = true;
7224#else /* 0 */
7225 lastAccessError = Utf8StrFmt(
7226 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
7227 location.c_str(),
7228 pVirtualBox->settingsFilePath().c_str());
7229 treeLock.release();
7230 throw S_OK;
7231#endif /* 0 */
7232 }
7233
7234 {
7235 autoCaller.release();
7236 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
7237 autoCaller.add();
7238 if (FAILED(autoCaller.rc()))
7239 throw autoCaller.rc();
7240
7241 if ( !fRepairImageZeroParentUuid
7242 && m->pParent->i_getState() != MediumState_Inaccessible
7243 && m->pParent->i_getId() != parentId)
7244 {
7245 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7246 lastAccessError = Utf8StrFmt(
7247 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
7248 &parentId, location.c_str(),
7249 m->pParent->i_getId().raw(),
7250 pVirtualBox->i_settingsFilePath().c_str());
7251 parentLock.release();
7252 treeLock.release();
7253 throw S_OK;
7254 }
7255 }
7256
7257 /// @todo NEWMEDIA what to do if the parent is not
7258 /// accessible while the diff is? Probably nothing. The
7259 /// real code will detect the mismatch anyway.
7260
7261 treeLock.release();
7262 }
7263 }
7264
7265 mediumSize = VDGetFileSize(hdd, 0);
7266 mediumLogicalSize = VDGetSize(hdd, 0);
7267
7268 success = true;
7269 }
7270 catch (HRESULT aRC)
7271 {
7272 rc = aRC;
7273 }
7274
7275 vrc = VDDestroy(hdd);
7276 if (RT_FAILURE(vrc))
7277 {
7278 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
7279 location.c_str(), i_vdError(vrc).c_str());
7280 success = false;
7281 throw S_OK;
7282 }
7283 }
7284 catch (HRESULT aRC)
7285 {
7286 rc = aRC;
7287 }
7288
7289 autoCaller.release();
7290 treeLock.acquire();
7291 autoCaller.add();
7292 if (FAILED(autoCaller.rc()))
7293 {
7294 m->queryInfoRunning = false;
7295 return autoCaller.rc();
7296 }
7297 alock.acquire();
7298
7299 if (success)
7300 {
7301 m->size = mediumSize;
7302 m->logicalSize = mediumLogicalSize;
7303 m->strLastAccessError.setNull();
7304 }
7305 else
7306 {
7307 m->strLastAccessError = lastAccessError;
7308 Log1WarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
7309 location.c_str(), m->strLastAccessError.c_str(), rc, vrc));
7310 }
7311
7312 /* Set the proper state according to the result of the check */
7313 if (success)
7314 m->preLockState = MediumState_Created;
7315 else
7316 m->preLockState = MediumState_Inaccessible;
7317
7318 /* unblock anyone waiting for the i_queryInfo results */
7319 qlock.release();
7320 m->queryInfoRunning = false;
7321
7322 pToken->Abandon();
7323 pToken.setNull();
7324
7325 if (FAILED(rc))
7326 return rc;
7327
7328 /* If this is a base image which incorrectly has a parent UUID set,
7329 * repair the image now by zeroing the parent UUID. This is only done
7330 * when we have structural information from a config file, on import
7331 * this is not possible. If someone would accidentally call openMedium
7332 * with a diff image before the base is registered this would destroy
7333 * the diff. Not acceptable. */
7334 do
7335 {
7336 if (fRepairImageZeroParentUuid)
7337 {
7338 rc = LockWrite(pToken.asOutParam());
7339 if (FAILED(rc))
7340 break;
7341
7342 alock.release();
7343
7344 try
7345 {
7346 PVDISK hdd;
7347 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7348 ComAssertRCThrow(vrc, E_FAIL);
7349
7350 try
7351 {
7352 vrc = VDOpen(hdd,
7353 format.c_str(),
7354 location.c_str(),
7355 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
7356 m->vdImageIfaces);
7357 if (RT_FAILURE(vrc))
7358 throw S_OK;
7359
7360 RTUUID zeroParentUuid;
7361 RTUuidClear(&zeroParentUuid);
7362 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
7363 ComAssertRCThrow(vrc, E_FAIL);
7364 }
7365 catch (HRESULT aRC)
7366 {
7367 rc = aRC;
7368 }
7369
7370 VDDestroy(hdd);
7371 }
7372 catch (HRESULT aRC)
7373 {
7374 rc = aRC;
7375 }
7376
7377 pToken->Abandon();
7378 pToken.setNull();
7379 if (FAILED(rc))
7380 break;
7381 }
7382 } while(0);
7383
7384 return rc;
7385}
7386
7387/**
7388 * Performs extra checks if the medium can be closed and returns S_OK in
7389 * this case. Otherwise, returns a respective error message. Called by
7390 * Close() under the medium tree lock and the medium lock.
7391 *
7392 * @note Also reused by Medium::Reset().
7393 *
7394 * @note Caller must hold the media tree write lock!
7395 */
7396HRESULT Medium::i_canClose()
7397{
7398 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7399
7400 if (i_getChildren().size() != 0)
7401 return setError(VBOX_E_OBJECT_IN_USE,
7402 tr("Cannot close medium '%s' because it has %d child media"),
7403 m->strLocationFull.c_str(), i_getChildren().size());
7404
7405 return S_OK;
7406}
7407
7408/**
7409 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
7410 *
7411 * @note Caller must have locked the media tree lock for writing!
7412 */
7413HRESULT Medium::i_unregisterWithVirtualBox()
7414{
7415 /* Note that we need to de-associate ourselves from the parent to let
7416 * VirtualBox::i_unregisterMedium() properly save the registry */
7417
7418 /* we modify m->pParent and access children */
7419 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7420
7421 Medium *pParentBackup = m->pParent;
7422 AssertReturn(i_getChildren().size() == 0, E_FAIL);
7423 if (m->pParent)
7424 i_deparent();
7425
7426 HRESULT rc = m->pVirtualBox->i_unregisterMedium(this);
7427 if (FAILED(rc))
7428 {
7429 if (pParentBackup)
7430 {
7431 // re-associate with the parent as we are still relatives in the registry
7432 i_setParent(pParentBackup);
7433 }
7434 }
7435
7436 return rc;
7437}
7438
7439/**
7440 * Like SetProperty but do not trigger a settings store. Only for internal use!
7441 */
7442HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
7443{
7444 AutoCaller autoCaller(this);
7445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7446
7447 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
7448
7449 switch (m->state)
7450 {
7451 case MediumState_Created:
7452 case MediumState_Inaccessible:
7453 break;
7454 default:
7455 return i_setStateError();
7456 }
7457
7458 m->mapProperties[aName] = aValue;
7459
7460 return S_OK;
7461}
7462
7463/**
7464 * Sets the extended error info according to the current media state.
7465 *
7466 * @note Must be called from under this object's write or read lock.
7467 */
7468HRESULT Medium::i_setStateError()
7469{
7470 HRESULT rc = E_FAIL;
7471
7472 switch (m->state)
7473 {
7474 case MediumState_NotCreated:
7475 {
7476 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7477 tr("Storage for the medium '%s' is not created"),
7478 m->strLocationFull.c_str());
7479 break;
7480 }
7481 case MediumState_Created:
7482 {
7483 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7484 tr("Storage for the medium '%s' is already created"),
7485 m->strLocationFull.c_str());
7486 break;
7487 }
7488 case MediumState_LockedRead:
7489 {
7490 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7491 tr("Medium '%s' is locked for reading by another task"),
7492 m->strLocationFull.c_str());
7493 break;
7494 }
7495 case MediumState_LockedWrite:
7496 {
7497 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7498 tr("Medium '%s' is locked for writing by another task"),
7499 m->strLocationFull.c_str());
7500 break;
7501 }
7502 case MediumState_Inaccessible:
7503 {
7504 /* be in sync with Console::powerUpThread() */
7505 if (!m->strLastAccessError.isEmpty())
7506 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7507 tr("Medium '%s' is not accessible. %s"),
7508 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
7509 else
7510 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7511 tr("Medium '%s' is not accessible"),
7512 m->strLocationFull.c_str());
7513 break;
7514 }
7515 case MediumState_Creating:
7516 {
7517 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7518 tr("Storage for the medium '%s' is being created"),
7519 m->strLocationFull.c_str());
7520 break;
7521 }
7522 case MediumState_Deleting:
7523 {
7524 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7525 tr("Storage for the medium '%s' is being deleted"),
7526 m->strLocationFull.c_str());
7527 break;
7528 }
7529 default:
7530 {
7531 AssertFailed();
7532 break;
7533 }
7534 }
7535
7536 return rc;
7537}
7538
7539/**
7540 * Sets the value of m->strLocationFull. The given location must be a fully
7541 * qualified path; relative paths are not supported here.
7542 *
7543 * As a special exception, if the specified location is a file path that ends with '/'
7544 * then the file name part will be generated by this method automatically in the format
7545 * '{\<uuid\>}.\<ext\>' where \<uuid\> is a fresh UUID that this method will generate
7546 * and assign to this medium, and \<ext\> is the default extension for this
7547 * medium's storage format. Note that this procedure requires the media state to
7548 * be NotCreated and will return a failure otherwise.
7549 *
7550 * @param aLocation Location of the storage unit. If the location is a FS-path,
7551 * then it can be relative to the VirtualBox home directory.
7552 * @param aFormat Optional fallback format if it is an import and the format
7553 * cannot be determined.
7554 *
7555 * @note Must be called from under this object's write lock.
7556 */
7557HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
7558 const Utf8Str &aFormat /* = Utf8Str::Empty */)
7559{
7560 AssertReturn(!aLocation.isEmpty(), E_FAIL);
7561
7562 AutoCaller autoCaller(this);
7563 AssertComRCReturnRC(autoCaller.rc());
7564
7565 /* formatObj may be null only when initializing from an existing path and
7566 * no format is known yet */
7567 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
7568 || ( getObjectState().getState() == ObjectState::InInit
7569 && m->state != MediumState_NotCreated
7570 && m->id.isZero()
7571 && m->strFormat.isEmpty()
7572 && m->formatObj.isNull()),
7573 E_FAIL);
7574
7575 /* are we dealing with a new medium constructed using the existing
7576 * location? */
7577 bool isImport = m->strFormat.isEmpty();
7578
7579 if ( isImport
7580 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7581 && !m->hostDrive))
7582 {
7583 Guid id;
7584
7585 Utf8Str locationFull(aLocation);
7586
7587 if (m->state == MediumState_NotCreated)
7588 {
7589 /* must be a file (formatObj must be already known) */
7590 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
7591
7592 if (RTPathFilename(aLocation.c_str()) == NULL)
7593 {
7594 /* no file name is given (either an empty string or ends with a
7595 * slash), generate a new UUID + file name if the state allows
7596 * this */
7597
7598 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
7599 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
7600 E_FAIL);
7601
7602 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
7603 ComAssertMsgRet(!strExt.isEmpty(),
7604 ("Default extension must not be empty\n"),
7605 E_FAIL);
7606
7607 id.create();
7608
7609 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
7610 aLocation.c_str(), id.raw(), strExt.c_str());
7611 }
7612 }
7613
7614 // we must always have full paths now (if it refers to a file)
7615 if ( ( m->formatObj.isNull()
7616 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7617 && !RTPathStartsWithRoot(locationFull.c_str()))
7618 return setError(VBOX_E_FILE_ERROR,
7619 tr("The given path '%s' is not fully qualified"),
7620 locationFull.c_str());
7621
7622 /* detect the backend from the storage unit if importing */
7623 if (isImport)
7624 {
7625 VDTYPE enmType = VDTYPE_INVALID;
7626 char *backendName = NULL;
7627
7628 int vrc = VINF_SUCCESS;
7629
7630 /* is it a file? */
7631 {
7632 RTFILE file;
7633 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
7634 if (RT_SUCCESS(vrc))
7635 RTFileClose(file);
7636 }
7637 if (RT_SUCCESS(vrc))
7638 {
7639 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7640 locationFull.c_str(), &backendName, &enmType);
7641 }
7642 else if ( vrc != VERR_FILE_NOT_FOUND
7643 && vrc != VERR_PATH_NOT_FOUND
7644 && vrc != VERR_ACCESS_DENIED
7645 && locationFull != aLocation)
7646 {
7647 /* assume it's not a file, restore the original location */
7648 locationFull = aLocation;
7649 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7650 locationFull.c_str(), &backendName, &enmType);
7651 }
7652
7653 if (RT_FAILURE(vrc))
7654 {
7655 if (vrc == VERR_ACCESS_DENIED)
7656 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
7657 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
7658 locationFull.c_str(), vrc);
7659 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
7660 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
7661 tr("Could not find file for the medium '%s' (%Rrc)"),
7662 locationFull.c_str(), vrc);
7663 else if (aFormat.isEmpty())
7664 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7665 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
7666 locationFull.c_str(), vrc);
7667 else
7668 {
7669 HRESULT rc = i_setFormat(aFormat);
7670 /* setFormat() must not fail since we've just used the backend so
7671 * the format object must be there */
7672 AssertComRCReturnRC(rc);
7673 }
7674 }
7675 else if ( enmType == VDTYPE_INVALID
7676 || m->devType != i_convertToDeviceType(enmType))
7677 {
7678 /*
7679 * The user tried to use a image as a device which is not supported
7680 * by the backend.
7681 */
7682 return setError(E_FAIL,
7683 tr("The medium '%s' can't be used as the requested device type"),
7684 locationFull.c_str());
7685 }
7686 else
7687 {
7688 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
7689
7690 HRESULT rc = i_setFormat(backendName);
7691 RTStrFree(backendName);
7692
7693 /* setFormat() must not fail since we've just used the backend so
7694 * the format object must be there */
7695 AssertComRCReturnRC(rc);
7696 }
7697 }
7698
7699 m->strLocationFull = locationFull;
7700
7701 /* is it still a file? */
7702 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7703 && (m->state == MediumState_NotCreated)
7704 )
7705 /* assign a new UUID (this UUID will be used when calling
7706 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
7707 * also do that if we didn't generate it to make sure it is
7708 * either generated by us or reset to null */
7709 unconst(m->id) = id;
7710 }
7711 else
7712 m->strLocationFull = aLocation;
7713
7714 return S_OK;
7715}
7716
7717/**
7718 * Checks that the format ID is valid and sets it on success.
7719 *
7720 * Note that this method will caller-reference the format object on success!
7721 * This reference must be released somewhere to let the MediumFormat object be
7722 * uninitialized.
7723 *
7724 * @note Must be called from under this object's write lock.
7725 */
7726HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
7727{
7728 /* get the format object first */
7729 {
7730 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
7731 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
7732
7733 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
7734 if (m->formatObj.isNull())
7735 return setError(E_INVALIDARG,
7736 tr("Invalid medium storage format '%s'"),
7737 aFormat.c_str());
7738
7739 /* get properties (preinsert them as keys in the map). Note that the
7740 * map doesn't grow over the object life time since the set of
7741 * properties is meant to be constant. */
7742
7743 Assert(m->mapProperties.empty());
7744
7745 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
7746 it != m->formatObj->i_getProperties().end();
7747 ++it)
7748 {
7749 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
7750 }
7751 }
7752
7753 unconst(m->strFormat) = aFormat;
7754
7755 return S_OK;
7756}
7757
7758/**
7759 * Converts the Medium device type to the VD type.
7760 */
7761VDTYPE Medium::i_convertDeviceType()
7762{
7763 VDTYPE enmType;
7764
7765 switch (m->devType)
7766 {
7767 case DeviceType_HardDisk:
7768 enmType = VDTYPE_HDD;
7769 break;
7770 case DeviceType_DVD:
7771 enmType = VDTYPE_OPTICAL_DISC;
7772 break;
7773 case DeviceType_Floppy:
7774 enmType = VDTYPE_FLOPPY;
7775 break;
7776 default:
7777 ComAssertFailedRet(VDTYPE_INVALID);
7778 }
7779
7780 return enmType;
7781}
7782
7783/**
7784 * Converts from the VD type to the medium type.
7785 */
7786DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
7787{
7788 DeviceType_T devType;
7789
7790 switch (enmType)
7791 {
7792 case VDTYPE_HDD:
7793 devType = DeviceType_HardDisk;
7794 break;
7795 case VDTYPE_OPTICAL_DISC:
7796 devType = DeviceType_DVD;
7797 break;
7798 case VDTYPE_FLOPPY:
7799 devType = DeviceType_Floppy;
7800 break;
7801 default:
7802 ComAssertFailedRet(DeviceType_Null);
7803 }
7804
7805 return devType;
7806}
7807
7808/**
7809 * Internal method which checks whether a property name is for a filter plugin.
7810 */
7811bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
7812{
7813 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
7814 size_t offSlash;
7815 if ((offSlash = aName.find("/", 0)) != aName.npos)
7816 {
7817 com::Utf8Str strFilter;
7818 com::Utf8Str strKey;
7819
7820 HRESULT rc = strFilter.assignEx(aName, 0, offSlash);
7821 if (FAILED(rc))
7822 return false;
7823
7824 rc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
7825 if (FAILED(rc))
7826 return false;
7827
7828 VDFILTERINFO FilterInfo;
7829 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
7830 if (RT_SUCCESS(vrc))
7831 {
7832 /* Check that the property exists. */
7833 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
7834 while (paConfig->pszKey)
7835 {
7836 if (strKey.equals(paConfig->pszKey))
7837 return true;
7838 paConfig++;
7839 }
7840 }
7841 }
7842
7843 return false;
7844}
7845
7846/**
7847 * Returns the last error message collected by the i_vdErrorCall callback and
7848 * resets it.
7849 *
7850 * The error message is returned prepended with a dot and a space, like this:
7851 * <code>
7852 * ". <error_text> (%Rrc)"
7853 * </code>
7854 * to make it easily appendable to a more general error message. The @c %Rrc
7855 * format string is given @a aVRC as an argument.
7856 *
7857 * If there is no last error message collected by i_vdErrorCall or if it is a
7858 * null or empty string, then this function returns the following text:
7859 * <code>
7860 * " (%Rrc)"
7861 * </code>
7862 *
7863 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7864 * the callback isn't called by more than one thread at a time.
7865 *
7866 * @param aVRC VBox error code to use when no error message is provided.
7867 */
7868Utf8Str Medium::i_vdError(int aVRC)
7869{
7870 Utf8Str error;
7871
7872 if (m->vdError.isEmpty())
7873 error = Utf8StrFmt(" (%Rrc)", aVRC);
7874 else
7875 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
7876
7877 m->vdError.setNull();
7878
7879 return error;
7880}
7881
7882/**
7883 * Error message callback.
7884 *
7885 * Puts the reported error message to the m->vdError field.
7886 *
7887 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7888 * the callback isn't called by more than one thread at a time.
7889 *
7890 * @param pvUser The opaque data passed on container creation.
7891 * @param rc The VBox error code.
7892 * @param SRC_POS Use RT_SRC_POS.
7893 * @param pszFormat Error message format string.
7894 * @param va Error message arguments.
7895 */
7896/*static*/
7897DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
7898 const char *pszFormat, va_list va)
7899{
7900 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
7901
7902 Medium *that = static_cast<Medium*>(pvUser);
7903 AssertReturnVoid(that != NULL);
7904
7905 if (that->m->vdError.isEmpty())
7906 that->m->vdError =
7907 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
7908 else
7909 that->m->vdError =
7910 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
7911 Utf8Str(pszFormat, va).c_str(), rc);
7912}
7913
7914/* static */
7915DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
7916 const char * /* pszzValid */)
7917{
7918 Medium *that = static_cast<Medium*>(pvUser);
7919 AssertReturn(that != NULL, false);
7920
7921 /* we always return true since the only keys we have are those found in
7922 * VDBACKENDINFO */
7923 return true;
7924}
7925
7926/* static */
7927DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
7928 const char *pszName,
7929 size_t *pcbValue)
7930{
7931 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
7932
7933 Medium *that = static_cast<Medium*>(pvUser);
7934 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7935
7936 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
7937 if (it == that->m->mapProperties.end())
7938 return VERR_CFGM_VALUE_NOT_FOUND;
7939
7940 /* we interpret null values as "no value" in Medium */
7941 if (it->second.isEmpty())
7942 return VERR_CFGM_VALUE_NOT_FOUND;
7943
7944 *pcbValue = it->second.length() + 1 /* include terminator */;
7945
7946 return VINF_SUCCESS;
7947}
7948
7949/* static */
7950DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
7951 const char *pszName,
7952 char *pszValue,
7953 size_t cchValue)
7954{
7955 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
7956
7957 Medium *that = static_cast<Medium*>(pvUser);
7958 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7959
7960 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
7961 if (it == that->m->mapProperties.end())
7962 return VERR_CFGM_VALUE_NOT_FOUND;
7963
7964 /* we interpret null values as "no value" in Medium */
7965 if (it->second.isEmpty())
7966 return VERR_CFGM_VALUE_NOT_FOUND;
7967
7968 const Utf8Str &value = it->second;
7969 if (value.length() >= cchValue)
7970 return VERR_CFGM_NOT_ENOUGH_SPACE;
7971
7972 memcpy(pszValue, value.c_str(), value.length() + 1);
7973
7974 return VINF_SUCCESS;
7975}
7976
7977DECLCALLBACK(bool) Medium::i_vdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid)
7978{
7979 /* Just return always true here. */
7980 NOREF(pvUser);
7981 NOREF(pszzValid);
7982 return true;
7983}
7984
7985DECLCALLBACK(int) Medium::i_vdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
7986{
7987 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
7988 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7989 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
7990
7991 size_t cbValue = 0;
7992 if (!strcmp(pszName, "Algorithm"))
7993 cbValue = strlen(pSettings->pszCipher) + 1;
7994 else if (!strcmp(pszName, "KeyId"))
7995 cbValue = sizeof("irrelevant");
7996 else if (!strcmp(pszName, "KeyStore"))
7997 {
7998 if (!pSettings->pszKeyStoreLoad)
7999 return VERR_CFGM_VALUE_NOT_FOUND;
8000 cbValue = strlen(pSettings->pszKeyStoreLoad) + 1;
8001 }
8002 else if (!strcmp(pszName, "CreateKeyStore"))
8003 cbValue = 2; /* Single digit + terminator. */
8004 else
8005 return VERR_CFGM_VALUE_NOT_FOUND;
8006
8007 *pcbValue = cbValue + 1 /* include terminator */;
8008
8009 return VINF_SUCCESS;
8010}
8011
8012DECLCALLBACK(int) Medium::i_vdCryptoConfigQuery(void *pvUser, const char *pszName,
8013 char *pszValue, size_t cchValue)
8014{
8015 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8016 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8017 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
8018
8019 const char *psz = NULL;
8020 if (!strcmp(pszName, "Algorithm"))
8021 psz = pSettings->pszCipher;
8022 else if (!strcmp(pszName, "KeyId"))
8023 psz = "irrelevant";
8024 else if (!strcmp(pszName, "KeyStore"))
8025 psz = pSettings->pszKeyStoreLoad;
8026 else if (!strcmp(pszName, "CreateKeyStore"))
8027 {
8028 if (pSettings->fCreateKeyStore)
8029 psz = "1";
8030 else
8031 psz = "0";
8032 }
8033 else
8034 return VERR_CFGM_VALUE_NOT_FOUND;
8035
8036 size_t cch = strlen(psz);
8037 if (cch >= cchValue)
8038 return VERR_CFGM_NOT_ENOUGH_SPACE;
8039
8040 memcpy(pszValue, psz, cch + 1);
8041 return VINF_SUCCESS;
8042}
8043
8044DECLCALLBACK(int) Medium::i_vdConfigUpdate(void *pvUser,
8045 bool fCreate,
8046 const char *pszName,
8047 const char *pszValue)
8048{
8049 Medium *that = (Medium *)pvUser;
8050
8051 // Detect if this runs inside i_queryInfo() on the current thread.
8052 // Skip if not. Check does not need synchronization.
8053 if (!that->m || !that->m->queryInfoRunning || !that->m->queryInfoSem.isWriteLockOnCurrentThread())
8054 return VINF_SUCCESS;
8055
8056 // It's guaranteed that this code is executing inside Medium::i_queryInfo,
8057 // can assume it took care of synchronization.
8058 int rv = VINF_SUCCESS;
8059 Utf8Str strName(pszName);
8060 settings::StringsMap::const_iterator it = that->m->mapProperties.find(strName);
8061 if (it == that->m->mapProperties.end() && !fCreate)
8062 rv = VERR_CFGM_VALUE_NOT_FOUND;
8063 else
8064 that->m->mapProperties[strName] = Utf8Str(pszValue);
8065 return rv;
8066}
8067
8068DECLCALLBACK(int) Medium::i_vdCryptoKeyRetain(void *pvUser, const char *pszId,
8069 const uint8_t **ppbKey, size_t *pcbKey)
8070{
8071 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8072 NOREF(pszId);
8073 NOREF(ppbKey);
8074 NOREF(pcbKey);
8075 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8076 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8077}
8078
8079DECLCALLBACK(int) Medium::i_vdCryptoKeyRelease(void *pvUser, const char *pszId)
8080{
8081 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8082 NOREF(pszId);
8083 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8084 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8085}
8086
8087DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
8088{
8089 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8090 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8091
8092 NOREF(pszId);
8093 *ppszPassword = pSettings->pszPassword;
8094 return VINF_SUCCESS;
8095}
8096
8097DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
8098{
8099 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8100 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8101 NOREF(pszId);
8102 return VINF_SUCCESS;
8103}
8104
8105DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore)
8106{
8107 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8108 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8109
8110 pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore);
8111 if (!pSettings->pszKeyStore)
8112 return VERR_NO_MEMORY;
8113
8114 memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore);
8115 return VINF_SUCCESS;
8116}
8117
8118DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher,
8119 const uint8_t *pbDek, size_t cbDek)
8120{
8121 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8122 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8123
8124 pSettings->pszCipherReturned = RTStrDup(pszCipher);
8125 pSettings->pbDek = pbDek;
8126 pSettings->cbDek = cbDek;
8127
8128 return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY;
8129}
8130
8131/**
8132 * Creates a VDISK instance for this medium.
8133 *
8134 * @note Caller should not hold any medium related locks as this method will
8135 * acquire the medium lock for writing and others (VirtualBox).
8136 *
8137 * @returns COM status code.
8138 * @param fWritable Whether to return a writable VDISK instance
8139 * (true) or a read-only one (false).
8140 * @param pKeyStore The key store.
8141 * @param ppHdd Where to return the pointer to the VDISK on
8142 * success.
8143 * @param pMediumLockList The lock list to populate and lock. Caller
8144 * is responsible for calling the destructor or
8145 * MediumLockList::Clear() after destroying
8146 * @a *ppHdd
8147 * @param pCryptoSettings The crypto settings to use for setting up
8148 * decryption/encryption of the VDISK. This object
8149 * must be alive until the VDISK is destroyed!
8150 */
8151HRESULT Medium::i_openForIO(bool fWritable, SecretKeyStore *pKeyStore, PVDISK *ppHdd, MediumLockList *pMediumLockList,
8152 MediumCryptoFilterSettings *pCryptoSettings)
8153{
8154 /*
8155 * Create the media lock list and lock the media.
8156 */
8157 HRESULT hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
8158 fWritable ? this : NULL /* pToLockWrite */,
8159 false /* fMediumLockWriteAll */,
8160 NULL,
8161 *pMediumLockList);
8162 if (SUCCEEDED(hrc))
8163 hrc = pMediumLockList->Lock();
8164 if (FAILED(hrc))
8165 return hrc;
8166
8167 /*
8168 * Get the base medium before write locking this medium.
8169 */
8170 ComObjPtr<Medium> pBase = i_getBase();
8171 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8172
8173 /*
8174 * Create the VDISK instance.
8175 */
8176 PVDISK pHdd;
8177 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pHdd);
8178 AssertRCReturn(vrc, E_FAIL);
8179
8180 /*
8181 * Goto avoidance using try/catch/throw(HRESULT).
8182 */
8183 try
8184 {
8185 settings::StringsMap::iterator itKeyStore = pBase->m->mapProperties.find("CRYPT/KeyStore");
8186 if (itKeyStore != pBase->m->mapProperties.end())
8187 {
8188 settings::StringsMap::iterator itKeyId = pBase->m->mapProperties.find("CRYPT/KeyId");
8189
8190#ifdef VBOX_WITH_EXTPACK
8191 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
8192 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
8193 {
8194 /* Load the plugin */
8195 Utf8Str strPlugin;
8196 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
8197 if (SUCCEEDED(hrc))
8198 {
8199 vrc = VDPluginLoadFromFilename(strPlugin.c_str());
8200 if (RT_FAILURE(vrc))
8201 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
8202 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
8203 i_vdError(vrc).c_str());
8204 }
8205 else
8206 throw setError(VBOX_E_NOT_SUPPORTED,
8207 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
8208 ORACLE_PUEL_EXTPACK_NAME);
8209 }
8210 else
8211 throw setError(VBOX_E_NOT_SUPPORTED,
8212 tr("Encryption is not supported because the extension pack '%s' is missing"),
8213 ORACLE_PUEL_EXTPACK_NAME);
8214#else
8215 throw setError(VBOX_E_NOT_SUPPORTED,
8216 tr("Encryption is not supported because extension pack support is not built in"));
8217#endif
8218
8219 if (itKeyId == pBase->m->mapProperties.end())
8220 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8221 tr("Image '%s' is configured for encryption but doesn't has a key identifier set"),
8222 pBase->m->strLocationFull.c_str());
8223
8224 /* Find the proper secret key in the key store. */
8225 if (!pKeyStore)
8226 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8227 tr("Image '%s' is configured for encryption but there is no key store to retrieve the password from"),
8228 pBase->m->strLocationFull.c_str());
8229
8230 SecretKey *pKey = NULL;
8231 vrc = pKeyStore->retainSecretKey(itKeyId->second, &pKey);
8232 if (RT_FAILURE(vrc))
8233 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
8234 tr("Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)"),
8235 itKeyId->second.c_str(), vrc);
8236
8237 i_taskEncryptSettingsSetup(pCryptoSettings, NULL, itKeyStore->second.c_str(), (const char *)pKey->getKeyBuffer(),
8238 false /* fCreateKeyStore */);
8239 vrc = VDFilterAdd(pHdd, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pCryptoSettings->vdFilterIfaces);
8240 pKeyStore->releaseSecretKey(itKeyId->second);
8241 if (vrc == VERR_VD_PASSWORD_INCORRECT)
8242 throw setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc, tr("The password to decrypt the image is incorrect"));
8243 if (RT_FAILURE(vrc))
8244 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc, tr("Failed to load the decryption filter: %s"),
8245 i_vdError(vrc).c_str());
8246 }
8247
8248 /*
8249 * Open all media in the source chain.
8250 */
8251 MediumLockList::Base::const_iterator sourceListBegin = pMediumLockList->GetBegin();
8252 MediumLockList::Base::const_iterator sourceListEnd = pMediumLockList->GetEnd();
8253 MediumLockList::Base::const_iterator mediumListLast = sourceListEnd;
8254 --mediumListLast;
8255
8256 for (MediumLockList::Base::const_iterator it = sourceListBegin; it != sourceListEnd; ++it)
8257 {
8258 const MediumLock &mediumLock = *it;
8259 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8260 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8261
8262 /* sanity check */
8263 Assert(pMedium->m->state == (fWritable && it == mediumListLast ? MediumState_LockedWrite : MediumState_LockedRead));
8264
8265 /* Open all media in read-only mode. */
8266 vrc = VDOpen(pHdd,
8267 pMedium->m->strFormat.c_str(),
8268 pMedium->m->strLocationFull.c_str(),
8269 m->uOpenFlagsDef | (fWritable && it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
8270 pMedium->m->vdImageIfaces);
8271 if (RT_FAILURE(vrc))
8272 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8273 tr("Could not open the medium storage unit '%s'%s"),
8274 pMedium->m->strLocationFull.c_str(),
8275 i_vdError(vrc).c_str());
8276 }
8277
8278 Assert(m->state == (fWritable ? MediumState_LockedWrite : MediumState_LockedRead));
8279
8280 /*
8281 * Done!
8282 */
8283 *ppHdd = pHdd;
8284 return S_OK;
8285 }
8286 catch (HRESULT hrc2)
8287 {
8288 hrc = hrc2;
8289 }
8290
8291 VDDestroy(pHdd);
8292 return hrc;
8293
8294}
8295
8296/**
8297 * Implementation code for the "create base" task.
8298 *
8299 * This only gets started from Medium::CreateBaseStorage() and always runs
8300 * asynchronously. As a result, we always save the VirtualBox.xml file when
8301 * we're done here.
8302 *
8303 * @param task
8304 * @return
8305 */
8306HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
8307{
8308 /** @todo r=klaus The code below needs to be double checked with regard
8309 * to lock order violations, it probably causes lock order issues related
8310 * to the AutoCaller usage. */
8311 HRESULT rc = S_OK;
8312
8313 /* these parameters we need after creation */
8314 uint64_t size = 0, logicalSize = 0;
8315 MediumVariant_T variant = MediumVariant_Standard;
8316 bool fGenerateUuid = false;
8317
8318 try
8319 {
8320 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8321
8322 /* The object may request a specific UUID (through a special form of
8323 * the moveTo() argument). Otherwise we have to generate it */
8324 Guid id = m->id;
8325
8326 fGenerateUuid = id.isZero();
8327 if (fGenerateUuid)
8328 {
8329 id.create();
8330 /* VirtualBox::i_registerMedium() will need UUID */
8331 unconst(m->id) = id;
8332 }
8333
8334 Utf8Str format(m->strFormat);
8335 Utf8Str location(m->strLocationFull);
8336 uint64_t capabilities = m->formatObj->i_getCapabilities();
8337 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
8338 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
8339 Assert(m->state == MediumState_Creating);
8340
8341 PVDISK hdd;
8342 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8343 ComAssertRCThrow(vrc, E_FAIL);
8344
8345 /* unlock before the potentially lengthy operation */
8346 thisLock.release();
8347
8348 try
8349 {
8350 /* ensure the directory exists */
8351 if (capabilities & MediumFormatCapabilities_File)
8352 {
8353 rc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8354 if (FAILED(rc))
8355 throw rc;
8356 }
8357
8358 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
8359
8360 vrc = VDCreateBase(hdd,
8361 format.c_str(),
8362 location.c_str(),
8363 task.mSize,
8364 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
8365 NULL,
8366 &geo,
8367 &geo,
8368 id.raw(),
8369 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8370 m->vdImageIfaces,
8371 task.mVDOperationIfaces);
8372 if (RT_FAILURE(vrc))
8373 {
8374 if (vrc == VERR_VD_INVALID_TYPE)
8375 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8376 tr("Parameters for creating the medium storage unit '%s' are invalid%s"),
8377 location.c_str(), i_vdError(vrc).c_str());
8378 else
8379 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8380 tr("Could not create the medium storage unit '%s'%s"),
8381 location.c_str(), i_vdError(vrc).c_str());
8382 }
8383
8384 if (task.mVariant & MediumVariant_Formatted)
8385 {
8386 RTVFSFILE hVfsFile;
8387 vrc = VDCreateVfsFileFromDisk(hdd, 0 /*fFlags*/, &hVfsFile);
8388 if (RT_FAILURE(vrc))
8389 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Opening medium storage unit '%s' failed%s"),
8390 location.c_str(), i_vdError(vrc).c_str());
8391 RTERRINFOSTATIC ErrInfo;
8392 vrc = RTFsFatVolFormat(hVfsFile, 0 /* offVol */, 0 /* cbVol */, RTFSFATVOL_FMT_F_FULL,
8393 0 /* cbSector */, 0 /* cbSectorPerCluster */, RTFSFATTYPE_INVALID,
8394 0 /* cHeads */, 0 /* cSectorsPerTrack*/, 0 /* bMedia */,
8395 0 /* cRootDirEntries */, 0 /* cHiddenSectors */,
8396 RTErrInfoInitStatic(&ErrInfo));
8397 RTVfsFileRelease(hVfsFile);
8398 if (RT_FAILURE(vrc) && RTErrInfoIsSet(&ErrInfo.Core))
8399 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed: %s"),
8400 location.c_str(), ErrInfo.Core.pszMsg);
8401 if (RT_FAILURE(vrc))
8402 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed%s"),
8403 location.c_str(), i_vdError(vrc).c_str());
8404 }
8405
8406 size = VDGetFileSize(hdd, 0);
8407 logicalSize = VDGetSize(hdd, 0);
8408 unsigned uImageFlags;
8409 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8410 if (RT_SUCCESS(vrc))
8411 variant = (MediumVariant_T)uImageFlags;
8412 }
8413 catch (HRESULT aRC) { rc = aRC; }
8414
8415 VDDestroy(hdd);
8416 }
8417 catch (HRESULT aRC) { rc = aRC; }
8418
8419 if (SUCCEEDED(rc))
8420 {
8421 /* register with mVirtualBox as the last step and move to
8422 * Created state only on success (leaving an orphan file is
8423 * better than breaking media registry consistency) */
8424 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8425 ComObjPtr<Medium> pMedium;
8426 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
8427 Assert(pMedium == NULL || this == pMedium);
8428 }
8429
8430 // re-acquire the lock before changing state
8431 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8432
8433 if (SUCCEEDED(rc))
8434 {
8435 m->state = MediumState_Created;
8436
8437 m->size = size;
8438 m->logicalSize = logicalSize;
8439 m->variant = variant;
8440
8441 thisLock.release();
8442 i_markRegistriesModified();
8443 if (task.isAsync())
8444 {
8445 // in asynchronous mode, save settings now
8446 m->pVirtualBox->i_saveModifiedRegistries();
8447 }
8448 }
8449 else
8450 {
8451 /* back to NotCreated on failure */
8452 m->state = MediumState_NotCreated;
8453
8454 /* reset UUID to prevent it from being reused next time */
8455 if (fGenerateUuid)
8456 unconst(m->id).clear();
8457 }
8458
8459 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
8460 {
8461 m->pVirtualBox->i_onMediumConfigChanged(this);
8462 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
8463 }
8464
8465 return rc;
8466}
8467
8468/**
8469 * Implementation code for the "create diff" task.
8470 *
8471 * This task always gets started from Medium::createDiffStorage() and can run
8472 * synchronously or asynchronously depending on the "wait" parameter passed to
8473 * that function. If we run synchronously, the caller expects the medium
8474 * registry modification to be set before returning; otherwise (in asynchronous
8475 * mode), we save the settings ourselves.
8476 *
8477 * @param task
8478 * @return
8479 */
8480HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
8481{
8482 /** @todo r=klaus The code below needs to be double checked with regard
8483 * to lock order violations, it probably causes lock order issues related
8484 * to the AutoCaller usage. */
8485 HRESULT rcTmp = S_OK;
8486
8487 const ComObjPtr<Medium> &pTarget = task.mTarget;
8488
8489 uint64_t size = 0, logicalSize = 0;
8490 MediumVariant_T variant = MediumVariant_Standard;
8491 bool fGenerateUuid = false;
8492
8493 try
8494 {
8495 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8496 {
8497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8498 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8499 tr("Cannot create differencing image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
8500 m->strLocationFull.c_str());
8501 }
8502
8503 /* Lock both in {parent,child} order. */
8504 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8505
8506 /* The object may request a specific UUID (through a special form of
8507 * the moveTo() argument). Otherwise we have to generate it */
8508 Guid targetId = pTarget->m->id;
8509
8510 fGenerateUuid = targetId.isZero();
8511 if (fGenerateUuid)
8512 {
8513 targetId.create();
8514 /* VirtualBox::i_registerMedium() will need UUID */
8515 unconst(pTarget->m->id) = targetId;
8516 }
8517
8518 Guid id = m->id;
8519
8520 Utf8Str targetFormat(pTarget->m->strFormat);
8521 Utf8Str targetLocation(pTarget->m->strLocationFull);
8522 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
8523 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
8524
8525 Assert(pTarget->m->state == MediumState_Creating);
8526 Assert(m->state == MediumState_LockedRead);
8527
8528 PVDISK hdd;
8529 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8530 ComAssertRCThrow(vrc, E_FAIL);
8531
8532 /* the two media are now protected by their non-default states;
8533 * unlock the media before the potentially lengthy operation */
8534 mediaLock.release();
8535
8536 try
8537 {
8538 /* Open all media in the target chain but the last. */
8539 MediumLockList::Base::const_iterator targetListBegin =
8540 task.mpMediumLockList->GetBegin();
8541 MediumLockList::Base::const_iterator targetListEnd =
8542 task.mpMediumLockList->GetEnd();
8543 for (MediumLockList::Base::const_iterator it = targetListBegin;
8544 it != targetListEnd;
8545 ++it)
8546 {
8547 const MediumLock &mediumLock = *it;
8548 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8549
8550 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8551
8552 /* Skip over the target diff medium */
8553 if (pMedium->m->state == MediumState_Creating)
8554 continue;
8555
8556 /* sanity check */
8557 Assert(pMedium->m->state == MediumState_LockedRead);
8558
8559 /* Open all media in appropriate mode. */
8560 vrc = VDOpen(hdd,
8561 pMedium->m->strFormat.c_str(),
8562 pMedium->m->strLocationFull.c_str(),
8563 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8564 pMedium->m->vdImageIfaces);
8565 if (RT_FAILURE(vrc))
8566 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8567 tr("Could not open the medium storage unit '%s'%s"),
8568 pMedium->m->strLocationFull.c_str(),
8569 i_vdError(vrc).c_str());
8570 }
8571
8572 /* ensure the target directory exists */
8573 if (capabilities & MediumFormatCapabilities_File)
8574 {
8575 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8576 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8577 if (FAILED(rc))
8578 throw rc;
8579 }
8580
8581 vrc = VDCreateDiff(hdd,
8582 targetFormat.c_str(),
8583 targetLocation.c_str(),
8584 (task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX))
8585 | VD_IMAGE_FLAGS_DIFF,
8586 NULL,
8587 targetId.raw(),
8588 id.raw(),
8589 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8590 pTarget->m->vdImageIfaces,
8591 task.mVDOperationIfaces);
8592 if (RT_FAILURE(vrc))
8593 {
8594 if (vrc == VERR_VD_INVALID_TYPE)
8595 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8596 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
8597 targetLocation.c_str(), i_vdError(vrc).c_str());
8598 else
8599 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8600 tr("Could not create the differencing medium storage unit '%s'%s"),
8601 targetLocation.c_str(), i_vdError(vrc).c_str());
8602 }
8603
8604 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
8605 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
8606 unsigned uImageFlags;
8607 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8608 if (RT_SUCCESS(vrc))
8609 variant = (MediumVariant_T)uImageFlags;
8610 }
8611 catch (HRESULT aRC) { rcTmp = aRC; }
8612
8613 VDDestroy(hdd);
8614 }
8615 catch (HRESULT aRC) { rcTmp = aRC; }
8616
8617 MultiResult mrc(rcTmp);
8618
8619 if (SUCCEEDED(mrc))
8620 {
8621 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8622
8623 Assert(pTarget->m->pParent.isNull());
8624
8625 /* associate child with the parent, maximum depth was checked above */
8626 pTarget->i_setParent(this);
8627
8628 /* diffs for immutable media are auto-reset by default */
8629 bool fAutoReset;
8630 {
8631 ComObjPtr<Medium> pBase = i_getBase();
8632 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
8633 fAutoReset = (pBase->m->type == MediumType_Immutable);
8634 }
8635 {
8636 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
8637 pTarget->m->autoReset = fAutoReset;
8638 }
8639
8640 /* register with mVirtualBox as the last step and move to
8641 * Created state only on success (leaving an orphan file is
8642 * better than breaking media registry consistency) */
8643 ComObjPtr<Medium> pMedium;
8644 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
8645 Assert(pTarget == pMedium);
8646
8647 if (FAILED(mrc))
8648 /* break the parent association on failure to register */
8649 i_deparent();
8650 }
8651
8652 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8653
8654 if (SUCCEEDED(mrc))
8655 {
8656 pTarget->m->state = MediumState_Created;
8657
8658 pTarget->m->size = size;
8659 pTarget->m->logicalSize = logicalSize;
8660 pTarget->m->variant = variant;
8661 }
8662 else
8663 {
8664 /* back to NotCreated on failure */
8665 pTarget->m->state = MediumState_NotCreated;
8666
8667 pTarget->m->autoReset = false;
8668
8669 /* reset UUID to prevent it from being reused next time */
8670 if (fGenerateUuid)
8671 unconst(pTarget->m->id).clear();
8672 }
8673
8674 // deregister the task registered in createDiffStorage()
8675 Assert(m->numCreateDiffTasks != 0);
8676 --m->numCreateDiffTasks;
8677
8678 mediaLock.release();
8679 i_markRegistriesModified();
8680 if (task.isAsync())
8681 {
8682 // in asynchronous mode, save settings now
8683 m->pVirtualBox->i_saveModifiedRegistries();
8684 }
8685
8686 /* Note that in sync mode, it's the caller's responsibility to
8687 * unlock the medium. */
8688
8689 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
8690 {
8691 m->pVirtualBox->i_onMediumConfigChanged(this);
8692 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
8693 }
8694
8695 return mrc;
8696}
8697
8698/**
8699 * Implementation code for the "merge" task.
8700 *
8701 * This task always gets started from Medium::mergeTo() and can run
8702 * synchronously or asynchronously depending on the "wait" parameter passed to
8703 * that function. If we run synchronously, the caller expects the medium
8704 * registry modification to be set before returning; otherwise (in asynchronous
8705 * mode), we save the settings ourselves.
8706 *
8707 * @param task
8708 * @return
8709 */
8710HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
8711{
8712 /** @todo r=klaus The code below needs to be double checked with regard
8713 * to lock order violations, it probably causes lock order issues related
8714 * to the AutoCaller usage. */
8715 HRESULT rcTmp = S_OK;
8716
8717 const ComObjPtr<Medium> &pTarget = task.mTarget;
8718
8719 try
8720 {
8721 if (!task.mParentForTarget.isNull())
8722 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8723 {
8724 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
8725 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8726 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
8727 task.mParentForTarget->m->strLocationFull.c_str());
8728 }
8729
8730 // Resize target to source size, if possible. Otherwise throw an error.
8731 // It's offline resizing. Online resizing will be called in the
8732 // SessionMachine::onlineMergeMedium.
8733
8734 uint64_t sourceSize = 0;
8735 Utf8Str sourceName;
8736 {
8737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8738 sourceSize = i_getLogicalSize();
8739 sourceName = i_getName();
8740 }
8741 uint64_t targetSize = 0;
8742 Utf8Str targetName;
8743 {
8744 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
8745 targetSize = pTarget->i_getLogicalSize();
8746 targetName = pTarget->i_getName();
8747 }
8748
8749 //reducing vm disks are not implemented yet
8750 if (sourceSize > targetSize)
8751 {
8752 if (i_isMediumFormatFile())
8753 {
8754 // Have to make own lock list, because "resize" method resizes only last image
8755 // in the lock chain. The lock chain already in the task.mpMediumLockList, so
8756 // just make new lock list based on it. In fact the own lock list neither makes
8757 // double locking of mediums nor unlocks them during delete, because medium
8758 // already locked by task.mpMediumLockList and own list is used just to specify
8759 // what "resize" method should resize.
8760
8761 MediumLockList* pMediumLockListForResize = new MediumLockList();
8762
8763 for (MediumLockList::Base::iterator it = task.mpMediumLockList->GetBegin();
8764 it != task.mpMediumLockList->GetEnd();
8765 ++it)
8766 {
8767 ComObjPtr<Medium> pMedium = it->GetMedium();
8768 pMediumLockListForResize->Append(pMedium, pMedium->m->state == MediumState_LockedWrite);
8769 if (pMedium == pTarget)
8770 break;
8771 }
8772
8773 // just to switch internal state of the lock list to avoid errors during list deletion,
8774 // because all meduims in the list already locked by task.mpMediumLockList
8775 HRESULT rc = pMediumLockListForResize->Lock(true /* fSkipOverLockedMedia */);
8776 if (FAILED(rc))
8777 {
8778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8779 rc = setError(rc,
8780 tr("Failed to lock the medium '%s' to resize before merge"),
8781 targetName.c_str());
8782 delete pMediumLockListForResize;
8783 throw rc;
8784 }
8785
8786 ComObjPtr<Progress> pProgress(task.GetProgressObject());
8787 rc = pTarget->i_resize(sourceSize, pMediumLockListForResize, &pProgress, true, false);
8788 if (FAILED(rc))
8789 {
8790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8791 rc = setError(rc,
8792 tr("Failed to set size of '%s' to size of '%s'"),
8793 targetName.c_str(), sourceName.c_str());
8794 delete pMediumLockListForResize;
8795 throw rc;
8796 }
8797 delete pMediumLockListForResize;
8798 }
8799 else
8800 {
8801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8802 HRESULT rc = setError(VBOX_E_NOT_SUPPORTED,
8803 tr("Sizes of '%s' and '%s' are different and medium format does not support resing"),
8804 sourceName.c_str(), targetName.c_str());
8805 throw rc;
8806 }
8807 }
8808
8809 task.GetProgressObject()->SetNextOperation(BstrFmt(tr("Merging medium '%s' to '%s'"),
8810 i_getName().c_str(),
8811 targetName.c_str()).raw(),
8812 1);
8813
8814 PVDISK hdd;
8815 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8816 ComAssertRCThrow(vrc, E_FAIL);
8817
8818 try
8819 {
8820 // Similar code appears in SessionMachine::onlineMergeMedium, so
8821 // if you make any changes below check whether they are applicable
8822 // in that context as well.
8823
8824 unsigned uTargetIdx = VD_LAST_IMAGE;
8825 unsigned uSourceIdx = VD_LAST_IMAGE;
8826 /* Open all media in the chain. */
8827 MediumLockList::Base::iterator lockListBegin =
8828 task.mpMediumLockList->GetBegin();
8829 MediumLockList::Base::iterator lockListEnd =
8830 task.mpMediumLockList->GetEnd();
8831 unsigned i = 0;
8832 for (MediumLockList::Base::iterator it = lockListBegin;
8833 it != lockListEnd;
8834 ++it)
8835 {
8836 MediumLock &mediumLock = *it;
8837 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8838
8839 if (pMedium == this)
8840 uSourceIdx = i;
8841 else if (pMedium == pTarget)
8842 uTargetIdx = i;
8843
8844 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8845
8846 /*
8847 * complex sanity (sane complexity)
8848 *
8849 * The current medium must be in the Deleting (medium is merged)
8850 * or LockedRead (parent medium) state if it is not the target.
8851 * If it is the target it must be in the LockedWrite state.
8852 */
8853 Assert( ( pMedium != pTarget
8854 && ( pMedium->m->state == MediumState_Deleting
8855 || pMedium->m->state == MediumState_LockedRead))
8856 || ( pMedium == pTarget
8857 && pMedium->m->state == MediumState_LockedWrite));
8858 /*
8859 * Medium must be the target, in the LockedRead state
8860 * or Deleting state where it is not allowed to be attached
8861 * to a virtual machine.
8862 */
8863 Assert( pMedium == pTarget
8864 || pMedium->m->state == MediumState_LockedRead
8865 || ( pMedium->m->backRefs.size() == 0
8866 && pMedium->m->state == MediumState_Deleting));
8867 /* The source medium must be in Deleting state. */
8868 Assert( pMedium != this
8869 || pMedium->m->state == MediumState_Deleting);
8870
8871 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8872
8873 if ( pMedium->m->state == MediumState_LockedRead
8874 || pMedium->m->state == MediumState_Deleting)
8875 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8876 if (pMedium->m->type == MediumType_Shareable)
8877 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8878
8879 /* Open the medium */
8880 vrc = VDOpen(hdd,
8881 pMedium->m->strFormat.c_str(),
8882 pMedium->m->strLocationFull.c_str(),
8883 uOpenFlags | m->uOpenFlagsDef,
8884 pMedium->m->vdImageIfaces);
8885 if (RT_FAILURE(vrc))
8886 throw vrc;
8887
8888 i++;
8889 }
8890
8891 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
8892 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
8893
8894 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
8895 task.mVDOperationIfaces);
8896 if (RT_FAILURE(vrc))
8897 throw vrc;
8898
8899 /* update parent UUIDs */
8900 if (!task.mfMergeForward)
8901 {
8902 /* we need to update UUIDs of all source's children
8903 * which cannot be part of the container at once so
8904 * add each one in there individually */
8905 if (task.mpChildrenToReparent)
8906 {
8907 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
8908 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
8909 for (MediumLockList::Base::iterator it = childrenBegin;
8910 it != childrenEnd;
8911 ++it)
8912 {
8913 Medium *pMedium = it->GetMedium();
8914 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
8915 vrc = VDOpen(hdd,
8916 pMedium->m->strFormat.c_str(),
8917 pMedium->m->strLocationFull.c_str(),
8918 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8919 pMedium->m->vdImageIfaces);
8920 if (RT_FAILURE(vrc))
8921 throw vrc;
8922
8923 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
8924 pTarget->m->id.raw());
8925 if (RT_FAILURE(vrc))
8926 throw vrc;
8927
8928 vrc = VDClose(hdd, false /* fDelete */);
8929 if (RT_FAILURE(vrc))
8930 throw vrc;
8931 }
8932 }
8933 }
8934 }
8935 catch (HRESULT aRC) { rcTmp = aRC; }
8936 catch (int aVRC)
8937 {
8938 rcTmp = setErrorBoth(VBOX_E_FILE_ERROR, aVRC,
8939 tr("Could not merge the medium '%s' to '%s'%s"),
8940 m->strLocationFull.c_str(),
8941 pTarget->m->strLocationFull.c_str(),
8942 i_vdError(aVRC).c_str());
8943 }
8944
8945 VDDestroy(hdd);
8946 }
8947 catch (HRESULT aRC) { rcTmp = aRC; }
8948
8949 ErrorInfoKeeper eik;
8950 MultiResult mrc(rcTmp);
8951 HRESULT rc2;
8952
8953 std::set<ComObjPtr<Medium> > pMediumsForNotify;
8954 std::map<Guid, DeviceType_T> uIdsForNotify;
8955
8956 if (SUCCEEDED(mrc))
8957 {
8958 /* all media but the target were successfully deleted by
8959 * VDMerge; reparent the last one and uninitialize deleted media. */
8960
8961 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8962
8963 if (task.mfMergeForward)
8964 {
8965 /* first, unregister the target since it may become a base
8966 * medium which needs re-registration */
8967 rc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
8968 AssertComRC(rc2);
8969
8970 /* then, reparent it and disconnect the deleted branch at both ends
8971 * (chain->parent() is source's parent). Depth check above. */
8972 pTarget->i_deparent();
8973 pTarget->i_setParent(task.mParentForTarget);
8974 if (task.mParentForTarget)
8975 {
8976 i_deparent();
8977 if (task.NotifyAboutChanges())
8978 pMediumsForNotify.insert(task.mParentForTarget);
8979 }
8980
8981 /* then, register again */
8982 ComObjPtr<Medium> pMedium;
8983 rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
8984 treeLock);
8985 AssertComRC(rc2);
8986 }
8987 else
8988 {
8989 Assert(pTarget->i_getChildren().size() == 1);
8990 Medium *targetChild = pTarget->i_getChildren().front();
8991
8992 /* disconnect the deleted branch at the elder end */
8993 targetChild->i_deparent();
8994
8995 /* reparent source's children and disconnect the deleted
8996 * branch at the younger end */
8997 if (task.mpChildrenToReparent)
8998 {
8999 /* obey {parent,child} lock order */
9000 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
9001
9002 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9003 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9004 for (MediumLockList::Base::iterator it = childrenBegin;
9005 it != childrenEnd;
9006 ++it)
9007 {
9008 Medium *pMedium = it->GetMedium();
9009 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
9010
9011 pMedium->i_deparent(); // removes pMedium from source
9012 // no depth check, reduces depth
9013 pMedium->i_setParent(pTarget);
9014
9015 if (task.NotifyAboutChanges())
9016 pMediumsForNotify.insert(pMedium);
9017 }
9018 }
9019 pMediumsForNotify.insert(pTarget);
9020 }
9021
9022 /* unregister and uninitialize all media removed by the merge */
9023 MediumLockList::Base::iterator lockListBegin =
9024 task.mpMediumLockList->GetBegin();
9025 MediumLockList::Base::iterator lockListEnd =
9026 task.mpMediumLockList->GetEnd();
9027 for (MediumLockList::Base::iterator it = lockListBegin;
9028 it != lockListEnd;
9029 )
9030 {
9031 MediumLock &mediumLock = *it;
9032 /* Create a real copy of the medium pointer, as the medium
9033 * lock deletion below would invalidate the referenced object. */
9034 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
9035
9036 /* The target and all media not merged (readonly) are skipped */
9037 if ( pMedium == pTarget
9038 || pMedium->m->state == MediumState_LockedRead)
9039 {
9040 ++it;
9041 continue;
9042 }
9043
9044 uIdsForNotify[pMedium->i_getId()] = pMedium->i_getDeviceType();
9045 rc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
9046 AssertComRC(rc2);
9047
9048 /* now, uninitialize the deleted medium (note that
9049 * due to the Deleting state, uninit() will not touch
9050 * the parent-child relationship so we need to
9051 * uninitialize each disk individually) */
9052
9053 /* note that the operation initiator medium (which is
9054 * normally also the source medium) is a special case
9055 * -- there is one more caller added by Task to it which
9056 * we must release. Also, if we are in sync mode, the
9057 * caller may still hold an AutoCaller instance for it
9058 * and therefore we cannot uninit() it (it's therefore
9059 * the caller's responsibility) */
9060 if (pMedium == this)
9061 {
9062 Assert(i_getChildren().size() == 0);
9063 Assert(m->backRefs.size() == 0);
9064 task.mMediumCaller.release();
9065 }
9066
9067 /* Delete the medium lock list entry, which also releases the
9068 * caller added by MergeChain before uninit() and updates the
9069 * iterator to point to the right place. */
9070 rc2 = task.mpMediumLockList->RemoveByIterator(it);
9071 AssertComRC(rc2);
9072
9073 if (task.isAsync() || pMedium != this)
9074 {
9075 treeLock.release();
9076 pMedium->uninit();
9077 treeLock.acquire();
9078 }
9079 }
9080 }
9081
9082 i_markRegistriesModified();
9083 if (task.isAsync())
9084 {
9085 // in asynchronous mode, save settings now
9086 eik.restore();
9087 m->pVirtualBox->i_saveModifiedRegistries();
9088 eik.fetch();
9089 }
9090
9091 if (FAILED(mrc))
9092 {
9093 /* Here we come if either VDMerge() failed (in which case we
9094 * assume that it tried to do everything to make a further
9095 * retry possible -- e.g. not deleted intermediate media
9096 * and so on) or VirtualBox::saveRegistries() failed (where we
9097 * should have the original tree but with intermediate storage
9098 * units deleted by VDMerge()). We have to only restore states
9099 * (through the MergeChain dtor) unless we are run synchronously
9100 * in which case it's the responsibility of the caller as stated
9101 * in the mergeTo() docs. The latter also implies that we
9102 * don't own the merge chain, so release it in this case. */
9103 if (task.isAsync())
9104 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
9105 }
9106 else if (task.NotifyAboutChanges())
9107 {
9108 for (std::set<ComObjPtr<Medium> >::const_iterator it = pMediumsForNotify.begin();
9109 it != pMediumsForNotify.end();
9110 ++it)
9111 {
9112 if (it->isNotNull())
9113 m->pVirtualBox->i_onMediumConfigChanged(*it);
9114 }
9115 for (std::map<Guid, DeviceType_T>::const_iterator it = uIdsForNotify.begin();
9116 it != uIdsForNotify.end();
9117 ++it)
9118 {
9119 m->pVirtualBox->i_onMediumRegistered(it->first, it->second, FALSE);
9120 }
9121 }
9122
9123 return mrc;
9124}
9125
9126/**
9127 * Implementation code for the "clone" task.
9128 *
9129 * This only gets started from Medium::CloneTo() and always runs asynchronously.
9130 * As a result, we always save the VirtualBox.xml file when we're done here.
9131 *
9132 * @param task
9133 * @return
9134 */
9135HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
9136{
9137 /** @todo r=klaus The code below needs to be double checked with regard
9138 * to lock order violations, it probably causes lock order issues related
9139 * to the AutoCaller usage. */
9140 HRESULT rcTmp = S_OK;
9141
9142 const ComObjPtr<Medium> &pTarget = task.mTarget;
9143 const ComObjPtr<Medium> &pParent = task.mParent;
9144
9145 bool fCreatingTarget = false;
9146
9147 uint64_t size = 0, logicalSize = 0;
9148 MediumVariant_T variant = MediumVariant_Standard;
9149 bool fGenerateUuid = false;
9150
9151 try
9152 {
9153 if (!pParent.isNull())
9154 {
9155
9156 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9157 {
9158 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
9159 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9160 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9161 pParent->m->strLocationFull.c_str());
9162 }
9163 }
9164
9165 /* Lock all in {parent,child} order. The lock is also used as a
9166 * signal from the task initiator (which releases it only after
9167 * RTThreadCreate()) that we can start the job. */
9168 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
9169
9170 fCreatingTarget = pTarget->m->state == MediumState_Creating;
9171
9172 /* The object may request a specific UUID (through a special form of
9173 * the moveTo() argument). Otherwise we have to generate it */
9174 Guid targetId = pTarget->m->id;
9175
9176 fGenerateUuid = targetId.isZero();
9177 if (fGenerateUuid)
9178 {
9179 targetId.create();
9180 /* VirtualBox::registerMedium() will need UUID */
9181 unconst(pTarget->m->id) = targetId;
9182 }
9183
9184 PVDISK hdd;
9185 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9186 ComAssertRCThrow(vrc, E_FAIL);
9187
9188 try
9189 {
9190 /* Open all media in the source chain. */
9191 MediumLockList::Base::const_iterator sourceListBegin =
9192 task.mpSourceMediumLockList->GetBegin();
9193 MediumLockList::Base::const_iterator sourceListEnd =
9194 task.mpSourceMediumLockList->GetEnd();
9195 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9196 it != sourceListEnd;
9197 ++it)
9198 {
9199 const MediumLock &mediumLock = *it;
9200 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9201 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9202
9203 /* sanity check */
9204 Assert(pMedium->m->state == MediumState_LockedRead);
9205
9206 /** Open all media in read-only mode. */
9207 vrc = VDOpen(hdd,
9208 pMedium->m->strFormat.c_str(),
9209 pMedium->m->strLocationFull.c_str(),
9210 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9211 pMedium->m->vdImageIfaces);
9212 if (RT_FAILURE(vrc))
9213 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9214 tr("Could not open the medium storage unit '%s'%s"),
9215 pMedium->m->strLocationFull.c_str(),
9216 i_vdError(vrc).c_str());
9217 }
9218
9219 Utf8Str targetFormat(pTarget->m->strFormat);
9220 Utf8Str targetLocation(pTarget->m->strLocationFull);
9221 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
9222
9223 Assert( pTarget->m->state == MediumState_Creating
9224 || pTarget->m->state == MediumState_LockedWrite);
9225 Assert(m->state == MediumState_LockedRead);
9226 Assert( pParent.isNull()
9227 || pParent->m->state == MediumState_LockedRead);
9228
9229 /* unlock before the potentially lengthy operation */
9230 thisLock.release();
9231
9232 /* ensure the target directory exists */
9233 if (capabilities & MediumFormatCapabilities_File)
9234 {
9235 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9236 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9237 if (FAILED(rc))
9238 throw rc;
9239 }
9240
9241 PVDISK targetHdd;
9242 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9243 ComAssertRCThrow(vrc, E_FAIL);
9244
9245 try
9246 {
9247 /* Open all media in the target chain. */
9248 MediumLockList::Base::const_iterator targetListBegin =
9249 task.mpTargetMediumLockList->GetBegin();
9250 MediumLockList::Base::const_iterator targetListEnd =
9251 task.mpTargetMediumLockList->GetEnd();
9252 for (MediumLockList::Base::const_iterator it = targetListBegin;
9253 it != targetListEnd;
9254 ++it)
9255 {
9256 const MediumLock &mediumLock = *it;
9257 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9258
9259 /* If the target medium is not created yet there's no
9260 * reason to open it. */
9261 if (pMedium == pTarget && fCreatingTarget)
9262 continue;
9263
9264 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9265
9266 /* sanity check */
9267 Assert( pMedium->m->state == MediumState_LockedRead
9268 || pMedium->m->state == MediumState_LockedWrite);
9269
9270 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9271 if (pMedium->m->state != MediumState_LockedWrite)
9272 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9273 if (pMedium->m->type == MediumType_Shareable)
9274 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9275
9276 /* Open all media in appropriate mode. */
9277 vrc = VDOpen(targetHdd,
9278 pMedium->m->strFormat.c_str(),
9279 pMedium->m->strLocationFull.c_str(),
9280 uOpenFlags | m->uOpenFlagsDef,
9281 pMedium->m->vdImageIfaces);
9282 if (RT_FAILURE(vrc))
9283 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9284 tr("Could not open the medium storage unit '%s'%s"),
9285 pMedium->m->strLocationFull.c_str(),
9286 i_vdError(vrc).c_str());
9287 }
9288
9289 /* target isn't locked, but no changing data is accessed */
9290 if (task.midxSrcImageSame == UINT32_MAX)
9291 {
9292 vrc = VDCopy(hdd,
9293 VD_LAST_IMAGE,
9294 targetHdd,
9295 targetFormat.c_str(),
9296 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9297 false /* fMoveByRename */,
9298 0 /* cbSize */,
9299 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
9300 targetId.raw(),
9301 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9302 NULL /* pVDIfsOperation */,
9303 pTarget->m->vdImageIfaces,
9304 task.mVDOperationIfaces);
9305 }
9306 else
9307 {
9308 vrc = VDCopyEx(hdd,
9309 VD_LAST_IMAGE,
9310 targetHdd,
9311 targetFormat.c_str(),
9312 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9313 false /* fMoveByRename */,
9314 0 /* cbSize */,
9315 task.midxSrcImageSame,
9316 task.midxDstImageSame,
9317 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
9318 targetId.raw(),
9319 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9320 NULL /* pVDIfsOperation */,
9321 pTarget->m->vdImageIfaces,
9322 task.mVDOperationIfaces);
9323 }
9324 if (RT_FAILURE(vrc))
9325 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9326 tr("Could not create the clone medium '%s'%s"),
9327 targetLocation.c_str(), i_vdError(vrc).c_str());
9328
9329 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
9330 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
9331 unsigned uImageFlags;
9332 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
9333 if (RT_SUCCESS(vrc))
9334 variant = (MediumVariant_T)uImageFlags;
9335 }
9336 catch (HRESULT aRC) { rcTmp = aRC; }
9337
9338 VDDestroy(targetHdd);
9339 }
9340 catch (HRESULT aRC) { rcTmp = aRC; }
9341
9342 VDDestroy(hdd);
9343 }
9344 catch (HRESULT aRC) { rcTmp = aRC; }
9345
9346 ErrorInfoKeeper eik;
9347 MultiResult mrc(rcTmp);
9348
9349 /* Only do the parent changes for newly created media. */
9350 if (SUCCEEDED(mrc) && fCreatingTarget)
9351 {
9352 /* we set m->pParent & children() */
9353 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9354
9355 Assert(pTarget->m->pParent.isNull());
9356
9357 if (pParent)
9358 {
9359 /* Associate the clone with the parent and deassociate
9360 * from VirtualBox. Depth check above. */
9361 pTarget->i_setParent(pParent);
9362
9363 /* register with mVirtualBox as the last step and move to
9364 * Created state only on success (leaving an orphan file is
9365 * better than breaking media registry consistency) */
9366 eik.restore();
9367 ComObjPtr<Medium> pMedium;
9368 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9369 treeLock);
9370 Assert( FAILED(mrc)
9371 || pTarget == pMedium);
9372 eik.fetch();
9373
9374 if (FAILED(mrc))
9375 /* break parent association on failure to register */
9376 pTarget->i_deparent(); // removes target from parent
9377 }
9378 else
9379 {
9380 /* just register */
9381 eik.restore();
9382 ComObjPtr<Medium> pMedium;
9383 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9384 treeLock);
9385 Assert( FAILED(mrc)
9386 || pTarget == pMedium);
9387 eik.fetch();
9388 }
9389 }
9390
9391 if (fCreatingTarget)
9392 {
9393 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
9394
9395 if (SUCCEEDED(mrc))
9396 {
9397 pTarget->m->state = MediumState_Created;
9398
9399 pTarget->m->size = size;
9400 pTarget->m->logicalSize = logicalSize;
9401 pTarget->m->variant = variant;
9402 }
9403 else
9404 {
9405 /* back to NotCreated on failure */
9406 pTarget->m->state = MediumState_NotCreated;
9407
9408 /* reset UUID to prevent it from being reused next time */
9409 if (fGenerateUuid)
9410 unconst(pTarget->m->id).clear();
9411 }
9412 }
9413
9414 /* Copy any filter related settings over to the target. */
9415 if (SUCCEEDED(mrc))
9416 {
9417 /* Copy any filter related settings over. */
9418 ComObjPtr<Medium> pBase = i_getBase();
9419 ComObjPtr<Medium> pTargetBase = pTarget->i_getBase();
9420 std::vector<com::Utf8Str> aFilterPropNames;
9421 std::vector<com::Utf8Str> aFilterPropValues;
9422 mrc = pBase->i_getFilterProperties(aFilterPropNames, aFilterPropValues);
9423 if (SUCCEEDED(mrc))
9424 {
9425 /* Go through the properties and add them to the target medium. */
9426 for (unsigned idx = 0; idx < aFilterPropNames.size(); idx++)
9427 {
9428 mrc = pTargetBase->i_setPropertyDirect(aFilterPropNames[idx], aFilterPropValues[idx]);
9429 if (FAILED(mrc)) break;
9430 }
9431
9432 // now, at the end of this task (always asynchronous), save the settings
9433 if (SUCCEEDED(mrc))
9434 {
9435 // save the settings
9436 i_markRegistriesModified();
9437 /* collect multiple errors */
9438 eik.restore();
9439 m->pVirtualBox->i_saveModifiedRegistries();
9440 eik.fetch();
9441
9442 if (task.NotifyAboutChanges())
9443 {
9444 if (!fCreatingTarget)
9445 {
9446 if (!aFilterPropNames.empty())
9447 m->pVirtualBox->i_onMediumConfigChanged(pTargetBase);
9448 if (pParent)
9449 m->pVirtualBox->i_onMediumConfigChanged(pParent);
9450 }
9451 else
9452 {
9453 m->pVirtualBox->i_onMediumRegistered(pTarget->i_getId(), pTarget->i_getDeviceType(), TRUE);
9454 }
9455 }
9456 }
9457 }
9458 }
9459
9460 /* Everything is explicitly unlocked when the task exits,
9461 * as the task destruction also destroys the source chain. */
9462
9463 /* Make sure the source chain is released early. It could happen
9464 * that we get a deadlock in Appliance::Import when Medium::Close
9465 * is called & the source chain is released at the same time. */
9466 task.mpSourceMediumLockList->Clear();
9467
9468 return mrc;
9469}
9470
9471/**
9472 * Implementation code for the "move" task.
9473 *
9474 * This only gets started from Medium::MoveTo() and always
9475 * runs asynchronously.
9476 *
9477 * @param task
9478 * @return
9479 */
9480HRESULT Medium::i_taskMoveHandler(Medium::MoveTask &task)
9481{
9482 LogFlowFuncEnter();
9483 HRESULT rcOut = S_OK;
9484
9485 /* pTarget is equal "this" in our case */
9486 const ComObjPtr<Medium> &pTarget = task.mMedium;
9487
9488 uint64_t size = 0; NOREF(size);
9489 uint64_t logicalSize = 0; NOREF(logicalSize);
9490 MediumVariant_T variant = MediumVariant_Standard; NOREF(variant);
9491
9492 /*
9493 * it's exactly moving, not cloning
9494 */
9495 if (!i_isMoveOperation(pTarget))
9496 {
9497 HRESULT rc = setError(VBOX_E_FILE_ERROR,
9498 tr("Wrong preconditions for moving the medium %s"),
9499 pTarget->m->strLocationFull.c_str());
9500 LogFlowFunc(("LEAVE: rc=%Rhrc (early)\n", rc));
9501 return rc;
9502 }
9503
9504 try
9505 {
9506 /* Lock all in {parent,child} order. The lock is also used as a
9507 * signal from the task initiator (which releases it only after
9508 * RTThreadCreate()) that we can start the job. */
9509
9510 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9511
9512 PVDISK hdd;
9513 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9514 ComAssertRCThrow(vrc, E_FAIL);
9515
9516 try
9517 {
9518 /* Open all media in the source chain. */
9519 MediumLockList::Base::const_iterator sourceListBegin =
9520 task.mpMediumLockList->GetBegin();
9521 MediumLockList::Base::const_iterator sourceListEnd =
9522 task.mpMediumLockList->GetEnd();
9523 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9524 it != sourceListEnd;
9525 ++it)
9526 {
9527 const MediumLock &mediumLock = *it;
9528 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9529 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9530
9531 /* sanity check */
9532 Assert(pMedium->m->state == MediumState_LockedWrite);
9533
9534 vrc = VDOpen(hdd,
9535 pMedium->m->strFormat.c_str(),
9536 pMedium->m->strLocationFull.c_str(),
9537 VD_OPEN_FLAGS_NORMAL,
9538 pMedium->m->vdImageIfaces);
9539 if (RT_FAILURE(vrc))
9540 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9541 tr("Could not open the medium storage unit '%s'%s"),
9542 pMedium->m->strLocationFull.c_str(),
9543 i_vdError(vrc).c_str());
9544 }
9545
9546 /* we can directly use pTarget->m->"variables" but for better reading we use local copies */
9547 Guid targetId = pTarget->m->id;
9548 Utf8Str targetFormat(pTarget->m->strFormat);
9549 uint64_t targetCapabilities = pTarget->m->formatObj->i_getCapabilities();
9550
9551 /*
9552 * change target location
9553 * m->strNewLocationFull has been set already together with m->fMoveThisMedium in
9554 * i_preparationForMoving()
9555 */
9556 Utf8Str targetLocation = i_getNewLocationForMoving();
9557
9558 /* unlock before the potentially lengthy operation */
9559 thisLock.release();
9560
9561 /* ensure the target directory exists */
9562 if (targetCapabilities & MediumFormatCapabilities_File)
9563 {
9564 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9565 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9566 if (FAILED(rc))
9567 throw rc;
9568 }
9569
9570 try
9571 {
9572 vrc = VDCopy(hdd,
9573 VD_LAST_IMAGE,
9574 hdd,
9575 targetFormat.c_str(),
9576 targetLocation.c_str(),
9577 true /* fMoveByRename */,
9578 0 /* cbSize */,
9579 VD_IMAGE_FLAGS_NONE,
9580 targetId.raw(),
9581 VD_OPEN_FLAGS_NORMAL,
9582 NULL /* pVDIfsOperation */,
9583 NULL,
9584 NULL);
9585 if (RT_FAILURE(vrc))
9586 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9587 tr("Could not move medium '%s'%s"),
9588 targetLocation.c_str(), i_vdError(vrc).c_str());
9589 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9590 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9591 unsigned uImageFlags;
9592 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9593 if (RT_SUCCESS(vrc))
9594 variant = (MediumVariant_T)uImageFlags;
9595
9596 /*
9597 * set current location, because VDCopy\VDCopyEx doesn't do it.
9598 * also reset moving flag
9599 */
9600 i_resetMoveOperationData();
9601 m->strLocationFull = targetLocation;
9602
9603 }
9604 catch (HRESULT aRC) { rcOut = aRC; }
9605
9606 }
9607 catch (HRESULT aRC) { rcOut = aRC; }
9608
9609 VDDestroy(hdd);
9610 }
9611 catch (HRESULT aRC) { rcOut = aRC; }
9612
9613 ErrorInfoKeeper eik;
9614 MultiResult mrc(rcOut);
9615
9616 // now, at the end of this task (always asynchronous), save the settings
9617 if (SUCCEEDED(mrc))
9618 {
9619 // save the settings
9620 i_markRegistriesModified();
9621 /* collect multiple errors */
9622 eik.restore();
9623 m->pVirtualBox->i_saveModifiedRegistries();
9624 eik.fetch();
9625 }
9626
9627 /* Everything is explicitly unlocked when the task exits,
9628 * as the task destruction also destroys the source chain. */
9629
9630 task.mpMediumLockList->Clear();
9631
9632 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
9633 m->pVirtualBox->i_onMediumConfigChanged(this);
9634
9635 LogFlowFunc(("LEAVE: mrc=%Rhrc\n", (HRESULT)mrc));
9636 return mrc;
9637}
9638
9639/**
9640 * Implementation code for the "delete" task.
9641 *
9642 * This task always gets started from Medium::deleteStorage() and can run
9643 * synchronously or asynchronously depending on the "wait" parameter passed to
9644 * that function.
9645 *
9646 * @param task
9647 * @return
9648 */
9649HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
9650{
9651 NOREF(task);
9652 HRESULT rc = S_OK;
9653
9654 try
9655 {
9656 /* The lock is also used as a signal from the task initiator (which
9657 * releases it only after RTThreadCreate()) that we can start the job */
9658 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9659
9660 PVDISK hdd;
9661 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9662 ComAssertRCThrow(vrc, E_FAIL);
9663
9664 Utf8Str format(m->strFormat);
9665 Utf8Str location(m->strLocationFull);
9666
9667 /* unlock before the potentially lengthy operation */
9668 Assert(m->state == MediumState_Deleting);
9669 thisLock.release();
9670
9671 try
9672 {
9673 vrc = VDOpen(hdd,
9674 format.c_str(),
9675 location.c_str(),
9676 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9677 m->vdImageIfaces);
9678 if (RT_SUCCESS(vrc))
9679 vrc = VDClose(hdd, true /* fDelete */);
9680
9681 if (RT_FAILURE(vrc) && vrc != VERR_FILE_NOT_FOUND)
9682 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9683 tr("Could not delete the medium storage unit '%s'%s"),
9684 location.c_str(), i_vdError(vrc).c_str());
9685
9686 }
9687 catch (HRESULT aRC) { rc = aRC; }
9688
9689 VDDestroy(hdd);
9690 }
9691 catch (HRESULT aRC) { rc = aRC; }
9692
9693 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9694
9695 /* go to the NotCreated state even on failure since the storage
9696 * may have been already partially deleted and cannot be used any
9697 * more. One will be able to manually re-open the storage if really
9698 * needed to re-register it. */
9699 m->state = MediumState_NotCreated;
9700
9701 /* Reset UUID to prevent Create* from reusing it again */
9702 com::Guid uOldId = m->id;
9703 unconst(m->id).clear();
9704
9705 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
9706 {
9707 if (m->pParent.isNotNull())
9708 m->pVirtualBox->i_onMediumConfigChanged(m->pParent);
9709 m->pVirtualBox->i_onMediumRegistered(uOldId, m->devType, FALSE);
9710 }
9711
9712 return rc;
9713}
9714
9715/**
9716 * Implementation code for the "reset" task.
9717 *
9718 * This always gets started asynchronously from Medium::Reset().
9719 *
9720 * @param task
9721 * @return
9722 */
9723HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
9724{
9725 HRESULT rc = S_OK;
9726
9727 uint64_t size = 0, logicalSize = 0;
9728 MediumVariant_T variant = MediumVariant_Standard;
9729
9730 try
9731 {
9732 /* The lock is also used as a signal from the task initiator (which
9733 * releases it only after RTThreadCreate()) that we can start the job */
9734 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9735
9736 /// @todo Below we use a pair of delete/create operations to reset
9737 /// the diff contents but the most efficient way will of course be
9738 /// to add a VDResetDiff() API call
9739
9740 PVDISK hdd;
9741 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9742 ComAssertRCThrow(vrc, E_FAIL);
9743
9744 Guid id = m->id;
9745 Utf8Str format(m->strFormat);
9746 Utf8Str location(m->strLocationFull);
9747
9748 Medium *pParent = m->pParent;
9749 Guid parentId = pParent->m->id;
9750 Utf8Str parentFormat(pParent->m->strFormat);
9751 Utf8Str parentLocation(pParent->m->strLocationFull);
9752
9753 Assert(m->state == MediumState_LockedWrite);
9754
9755 /* unlock before the potentially lengthy operation */
9756 thisLock.release();
9757
9758 try
9759 {
9760 /* Open all media in the target chain but the last. */
9761 MediumLockList::Base::const_iterator targetListBegin =
9762 task.mpMediumLockList->GetBegin();
9763 MediumLockList::Base::const_iterator targetListEnd =
9764 task.mpMediumLockList->GetEnd();
9765 for (MediumLockList::Base::const_iterator it = targetListBegin;
9766 it != targetListEnd;
9767 ++it)
9768 {
9769 const MediumLock &mediumLock = *it;
9770 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9771
9772 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9773
9774 /* sanity check, "this" is checked above */
9775 Assert( pMedium == this
9776 || pMedium->m->state == MediumState_LockedRead);
9777
9778 /* Open all media in appropriate mode. */
9779 vrc = VDOpen(hdd,
9780 pMedium->m->strFormat.c_str(),
9781 pMedium->m->strLocationFull.c_str(),
9782 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9783 pMedium->m->vdImageIfaces);
9784 if (RT_FAILURE(vrc))
9785 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9786 tr("Could not open the medium storage unit '%s'%s"),
9787 pMedium->m->strLocationFull.c_str(),
9788 i_vdError(vrc).c_str());
9789
9790 /* Done when we hit the media which should be reset */
9791 if (pMedium == this)
9792 break;
9793 }
9794
9795 /* first, delete the storage unit */
9796 vrc = VDClose(hdd, true /* fDelete */);
9797 if (RT_FAILURE(vrc))
9798 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9799 tr("Could not delete the medium storage unit '%s'%s"),
9800 location.c_str(), i_vdError(vrc).c_str());
9801
9802 /* next, create it again */
9803 vrc = VDOpen(hdd,
9804 parentFormat.c_str(),
9805 parentLocation.c_str(),
9806 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9807 m->vdImageIfaces);
9808 if (RT_FAILURE(vrc))
9809 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9810 tr("Could not open the medium storage unit '%s'%s"),
9811 parentLocation.c_str(), i_vdError(vrc).c_str());
9812
9813 vrc = VDCreateDiff(hdd,
9814 format.c_str(),
9815 location.c_str(),
9816 /// @todo use the same medium variant as before
9817 VD_IMAGE_FLAGS_NONE,
9818 NULL,
9819 id.raw(),
9820 parentId.raw(),
9821 VD_OPEN_FLAGS_NORMAL,
9822 m->vdImageIfaces,
9823 task.mVDOperationIfaces);
9824 if (RT_FAILURE(vrc))
9825 {
9826 if (vrc == VERR_VD_INVALID_TYPE)
9827 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9828 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
9829 location.c_str(), i_vdError(vrc).c_str());
9830 else
9831 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9832 tr("Could not create the differencing medium storage unit '%s'%s"),
9833 location.c_str(), i_vdError(vrc).c_str());
9834 }
9835
9836 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9837 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9838 unsigned uImageFlags;
9839 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9840 if (RT_SUCCESS(vrc))
9841 variant = (MediumVariant_T)uImageFlags;
9842 }
9843 catch (HRESULT aRC) { rc = aRC; }
9844
9845 VDDestroy(hdd);
9846 }
9847 catch (HRESULT aRC) { rc = aRC; }
9848
9849 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9850
9851 m->size = size;
9852 m->logicalSize = logicalSize;
9853 m->variant = variant;
9854
9855 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
9856 m->pVirtualBox->i_onMediumConfigChanged(this);
9857
9858 /* Everything is explicitly unlocked when the task exits,
9859 * as the task destruction also destroys the media chain. */
9860
9861 return rc;
9862}
9863
9864/**
9865 * Implementation code for the "compact" task.
9866 *
9867 * @param task
9868 * @return
9869 */
9870HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
9871{
9872 HRESULT rc = S_OK;
9873
9874 /* Lock all in {parent,child} order. The lock is also used as a
9875 * signal from the task initiator (which releases it only after
9876 * RTThreadCreate()) that we can start the job. */
9877 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9878
9879 try
9880 {
9881 PVDISK hdd;
9882 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9883 ComAssertRCThrow(vrc, E_FAIL);
9884
9885 try
9886 {
9887 /* Open all media in the chain. */
9888 MediumLockList::Base::const_iterator mediumListBegin =
9889 task.mpMediumLockList->GetBegin();
9890 MediumLockList::Base::const_iterator mediumListEnd =
9891 task.mpMediumLockList->GetEnd();
9892 MediumLockList::Base::const_iterator mediumListLast =
9893 mediumListEnd;
9894 --mediumListLast;
9895 for (MediumLockList::Base::const_iterator it = mediumListBegin;
9896 it != mediumListEnd;
9897 ++it)
9898 {
9899 const MediumLock &mediumLock = *it;
9900 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9901 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9902
9903 /* sanity check */
9904 if (it == mediumListLast)
9905 Assert(pMedium->m->state == MediumState_LockedWrite);
9906 else
9907 Assert(pMedium->m->state == MediumState_LockedRead);
9908
9909 /* Open all media but last in read-only mode. Do not handle
9910 * shareable media, as compaction and sharing are mutually
9911 * exclusive. */
9912 vrc = VDOpen(hdd,
9913 pMedium->m->strFormat.c_str(),
9914 pMedium->m->strLocationFull.c_str(),
9915 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
9916 pMedium->m->vdImageIfaces);
9917 if (RT_FAILURE(vrc))
9918 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9919 tr("Could not open the medium storage unit '%s'%s"),
9920 pMedium->m->strLocationFull.c_str(),
9921 i_vdError(vrc).c_str());
9922 }
9923
9924 Assert(m->state == MediumState_LockedWrite);
9925
9926 Utf8Str location(m->strLocationFull);
9927
9928 /* unlock before the potentially lengthy operation */
9929 thisLock.release();
9930
9931 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
9932 if (RT_FAILURE(vrc))
9933 {
9934 if (vrc == VERR_NOT_SUPPORTED)
9935 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
9936 tr("Compacting is not yet supported for medium '%s'"),
9937 location.c_str());
9938 else if (vrc == VERR_NOT_IMPLEMENTED)
9939 throw setErrorBoth(E_NOTIMPL, vrc,
9940 tr("Compacting is not implemented, medium '%s'"),
9941 location.c_str());
9942 else
9943 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9944 tr("Could not compact medium '%s'%s"),
9945 location.c_str(),
9946 i_vdError(vrc).c_str());
9947 }
9948 }
9949 catch (HRESULT aRC) { rc = aRC; }
9950
9951 VDDestroy(hdd);
9952 }
9953 catch (HRESULT aRC) { rc = aRC; }
9954
9955 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
9956 m->pVirtualBox->i_onMediumConfigChanged(this);
9957
9958 /* Everything is explicitly unlocked when the task exits,
9959 * as the task destruction also destroys the media chain. */
9960
9961 return rc;
9962}
9963
9964/**
9965 * Implementation code for the "resize" task.
9966 *
9967 * @param task
9968 * @return
9969 */
9970HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
9971{
9972 HRESULT rc = S_OK;
9973
9974 uint64_t size = 0, logicalSize = 0;
9975
9976 try
9977 {
9978 /* The lock is also used as a signal from the task initiator (which
9979 * releases it only after RTThreadCreate()) that we can start the job */
9980 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9981
9982 PVDISK hdd;
9983 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9984 ComAssertRCThrow(vrc, E_FAIL);
9985
9986 try
9987 {
9988 /* Open all media in the chain. */
9989 MediumLockList::Base::const_iterator mediumListBegin =
9990 task.mpMediumLockList->GetBegin();
9991 MediumLockList::Base::const_iterator mediumListEnd =
9992 task.mpMediumLockList->GetEnd();
9993 MediumLockList::Base::const_iterator mediumListLast =
9994 mediumListEnd;
9995 --mediumListLast;
9996 for (MediumLockList::Base::const_iterator it = mediumListBegin;
9997 it != mediumListEnd;
9998 ++it)
9999 {
10000 const MediumLock &mediumLock = *it;
10001 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10002 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10003
10004 /* sanity check */
10005 if (it == mediumListLast)
10006 Assert(pMedium->m->state == MediumState_LockedWrite);
10007 else
10008 Assert(pMedium->m->state == MediumState_LockedRead ||
10009 // Allow resize the target image during mergeTo in case
10010 // of direction from parent to child because all intermediate
10011 // images are marked to MediumState_Deleting and will be
10012 // destroyed after successful merge
10013 pMedium->m->state == MediumState_Deleting);
10014
10015 /* Open all media but last in read-only mode. Do not handle
10016 * shareable media, as compaction and sharing are mutually
10017 * exclusive. */
10018 vrc = VDOpen(hdd,
10019 pMedium->m->strFormat.c_str(),
10020 pMedium->m->strLocationFull.c_str(),
10021 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10022 pMedium->m->vdImageIfaces);
10023 if (RT_FAILURE(vrc))
10024 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10025 tr("Could not open the medium storage unit '%s'%s"),
10026 pMedium->m->strLocationFull.c_str(),
10027 i_vdError(vrc).c_str());
10028 }
10029
10030 Assert(m->state == MediumState_LockedWrite);
10031
10032 Utf8Str location(m->strLocationFull);
10033
10034 /* unlock before the potentially lengthy operation */
10035 thisLock.release();
10036
10037 VDGEOMETRY geo = {0, 0, 0}; /* auto */
10038 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
10039 if (RT_FAILURE(vrc))
10040 {
10041 if (vrc == VERR_VD_SHRINK_NOT_SUPPORTED)
10042 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10043 tr("Shrinking is not yet supported for medium '%s'"),
10044 location.c_str());
10045 if (vrc == VERR_NOT_SUPPORTED)
10046 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10047 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
10048 task.mSize, location.c_str());
10049 else if (vrc == VERR_NOT_IMPLEMENTED)
10050 throw setErrorBoth(E_NOTIMPL, vrc,
10051 tr("Resiting is not implemented, medium '%s'"),
10052 location.c_str());
10053 else
10054 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10055 tr("Could not resize medium '%s'%s"),
10056 location.c_str(),
10057 i_vdError(vrc).c_str());
10058 }
10059 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10060 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10061 }
10062 catch (HRESULT aRC) { rc = aRC; }
10063
10064 VDDestroy(hdd);
10065 }
10066 catch (HRESULT aRC) { rc = aRC; }
10067
10068 if (SUCCEEDED(rc))
10069 {
10070 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10071 m->size = size;
10072 m->logicalSize = logicalSize;
10073
10074 if (task.NotifyAboutChanges())
10075 m->pVirtualBox->i_onMediumConfigChanged(this);
10076 }
10077
10078 /* Everything is explicitly unlocked when the task exits,
10079 * as the task destruction also destroys the media chain. */
10080
10081 return rc;
10082}
10083
10084/**
10085 * Implementation code for the "import" task.
10086 *
10087 * This only gets started from Medium::importFile() and always runs
10088 * asynchronously. It potentially touches the media registry, so we
10089 * always save the VirtualBox.xml file when we're done here.
10090 *
10091 * @param task
10092 * @return
10093 */
10094HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
10095{
10096 /** @todo r=klaus The code below needs to be double checked with regard
10097 * to lock order violations, it probably causes lock order issues related
10098 * to the AutoCaller usage. */
10099 HRESULT rcTmp = S_OK;
10100
10101 const ComObjPtr<Medium> &pParent = task.mParent;
10102
10103 bool fCreatingTarget = false;
10104
10105 uint64_t size = 0, logicalSize = 0;
10106 MediumVariant_T variant = MediumVariant_Standard;
10107 bool fGenerateUuid = false;
10108
10109 try
10110 {
10111 if (!pParent.isNull())
10112 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
10113 {
10114 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
10115 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10116 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
10117 pParent->m->strLocationFull.c_str());
10118 }
10119
10120 /* Lock all in {parent,child} order. The lock is also used as a
10121 * signal from the task initiator (which releases it only after
10122 * RTThreadCreate()) that we can start the job. */
10123 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
10124
10125 fCreatingTarget = m->state == MediumState_Creating;
10126
10127 /* The object may request a specific UUID (through a special form of
10128 * the moveTo() argument). Otherwise we have to generate it */
10129 Guid targetId = m->id;
10130
10131 fGenerateUuid = targetId.isZero();
10132 if (fGenerateUuid)
10133 {
10134 targetId.create();
10135 /* VirtualBox::i_registerMedium() will need UUID */
10136 unconst(m->id) = targetId;
10137 }
10138
10139
10140 PVDISK hdd;
10141 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10142 ComAssertRCThrow(vrc, E_FAIL);
10143
10144 try
10145 {
10146 /* Open source medium. */
10147 vrc = VDOpen(hdd,
10148 task.mFormat->i_getId().c_str(),
10149 task.mFilename.c_str(),
10150 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
10151 task.mVDImageIfaces);
10152 if (RT_FAILURE(vrc))
10153 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10154 tr("Could not open the medium storage unit '%s'%s"),
10155 task.mFilename.c_str(),
10156 i_vdError(vrc).c_str());
10157
10158 Utf8Str targetFormat(m->strFormat);
10159 Utf8Str targetLocation(m->strLocationFull);
10160 uint64_t capabilities = task.mFormat->i_getCapabilities();
10161
10162 Assert( m->state == MediumState_Creating
10163 || m->state == MediumState_LockedWrite);
10164 Assert( pParent.isNull()
10165 || pParent->m->state == MediumState_LockedRead);
10166
10167 /* unlock before the potentially lengthy operation */
10168 thisLock.release();
10169
10170 /* ensure the target directory exists */
10171 if (capabilities & MediumFormatCapabilities_File)
10172 {
10173 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
10174 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
10175 if (FAILED(rc))
10176 throw rc;
10177 }
10178
10179 PVDISK targetHdd;
10180 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
10181 ComAssertRCThrow(vrc, E_FAIL);
10182
10183 try
10184 {
10185 /* Open all media in the target chain. */
10186 MediumLockList::Base::const_iterator targetListBegin =
10187 task.mpTargetMediumLockList->GetBegin();
10188 MediumLockList::Base::const_iterator targetListEnd =
10189 task.mpTargetMediumLockList->GetEnd();
10190 for (MediumLockList::Base::const_iterator it = targetListBegin;
10191 it != targetListEnd;
10192 ++it)
10193 {
10194 const MediumLock &mediumLock = *it;
10195 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10196
10197 /* If the target medium is not created yet there's no
10198 * reason to open it. */
10199 if (pMedium == this && fCreatingTarget)
10200 continue;
10201
10202 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10203
10204 /* sanity check */
10205 Assert( pMedium->m->state == MediumState_LockedRead
10206 || pMedium->m->state == MediumState_LockedWrite);
10207
10208 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
10209 if (pMedium->m->state != MediumState_LockedWrite)
10210 uOpenFlags = VD_OPEN_FLAGS_READONLY;
10211 if (pMedium->m->type == MediumType_Shareable)
10212 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
10213
10214 /* Open all media in appropriate mode. */
10215 vrc = VDOpen(targetHdd,
10216 pMedium->m->strFormat.c_str(),
10217 pMedium->m->strLocationFull.c_str(),
10218 uOpenFlags | m->uOpenFlagsDef,
10219 pMedium->m->vdImageIfaces);
10220 if (RT_FAILURE(vrc))
10221 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10222 tr("Could not open the medium storage unit '%s'%s"),
10223 pMedium->m->strLocationFull.c_str(),
10224 i_vdError(vrc).c_str());
10225 }
10226
10227 vrc = VDCopy(hdd,
10228 VD_LAST_IMAGE,
10229 targetHdd,
10230 targetFormat.c_str(),
10231 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
10232 false /* fMoveByRename */,
10233 0 /* cbSize */,
10234 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
10235 targetId.raw(),
10236 VD_OPEN_FLAGS_NORMAL,
10237 NULL /* pVDIfsOperation */,
10238 m->vdImageIfaces,
10239 task.mVDOperationIfaces);
10240 if (RT_FAILURE(vrc))
10241 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10242 tr("Could not create the imported medium '%s'%s"),
10243 targetLocation.c_str(), i_vdError(vrc).c_str());
10244
10245 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
10246 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
10247 unsigned uImageFlags;
10248 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
10249 if (RT_SUCCESS(vrc))
10250 variant = (MediumVariant_T)uImageFlags;
10251 }
10252 catch (HRESULT aRC) { rcTmp = aRC; }
10253
10254 VDDestroy(targetHdd);
10255 }
10256 catch (HRESULT aRC) { rcTmp = aRC; }
10257
10258 VDDestroy(hdd);
10259 }
10260 catch (HRESULT aRC) { rcTmp = aRC; }
10261
10262 ErrorInfoKeeper eik;
10263 MultiResult mrc(rcTmp);
10264
10265 /* Only do the parent changes for newly created media. */
10266 if (SUCCEEDED(mrc) && fCreatingTarget)
10267 {
10268 /* we set m->pParent & children() */
10269 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10270
10271 Assert(m->pParent.isNull());
10272
10273 if (pParent)
10274 {
10275 /* Associate the imported medium with the parent and deassociate
10276 * from VirtualBox. Depth check above. */
10277 i_setParent(pParent);
10278
10279 /* register with mVirtualBox as the last step and move to
10280 * Created state only on success (leaving an orphan file is
10281 * better than breaking media registry consistency) */
10282 eik.restore();
10283 ComObjPtr<Medium> pMedium;
10284 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
10285 treeLock);
10286 Assert(this == pMedium);
10287 eik.fetch();
10288
10289 if (FAILED(mrc))
10290 /* break parent association on failure to register */
10291 this->i_deparent(); // removes target from parent
10292 }
10293 else
10294 {
10295 /* just register */
10296 eik.restore();
10297 ComObjPtr<Medium> pMedium;
10298 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
10299 Assert(this == pMedium);
10300 eik.fetch();
10301 }
10302 }
10303
10304 if (fCreatingTarget)
10305 {
10306 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
10307
10308 if (SUCCEEDED(mrc))
10309 {
10310 m->state = MediumState_Created;
10311
10312 m->size = size;
10313 m->logicalSize = logicalSize;
10314 m->variant = variant;
10315 }
10316 else
10317 {
10318 /* back to NotCreated on failure */
10319 m->state = MediumState_NotCreated;
10320
10321 /* reset UUID to prevent it from being reused next time */
10322 if (fGenerateUuid)
10323 unconst(m->id).clear();
10324 }
10325 }
10326
10327 // now, at the end of this task (always asynchronous), save the settings
10328 {
10329 // save the settings
10330 i_markRegistriesModified();
10331 /* collect multiple errors */
10332 eik.restore();
10333 m->pVirtualBox->i_saveModifiedRegistries();
10334 eik.fetch();
10335 }
10336
10337 /* Everything is explicitly unlocked when the task exits,
10338 * as the task destruction also destroys the target chain. */
10339
10340 /* Make sure the target chain is released early, otherwise it can
10341 * lead to deadlocks with concurrent IAppliance activities. */
10342 task.mpTargetMediumLockList->Clear();
10343
10344 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
10345 {
10346 if (pParent)
10347 m->pVirtualBox->i_onMediumConfigChanged(pParent);
10348 if (fCreatingTarget)
10349 m->pVirtualBox->i_onMediumConfigChanged(this);
10350 else
10351 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
10352 }
10353
10354 return mrc;
10355}
10356
10357/**
10358 * Sets up the encryption settings for a filter.
10359 */
10360void Medium::i_taskEncryptSettingsSetup(MediumCryptoFilterSettings *pSettings, const char *pszCipher,
10361 const char *pszKeyStore, const char *pszPassword,
10362 bool fCreateKeyStore)
10363{
10364 pSettings->pszCipher = pszCipher;
10365 pSettings->pszPassword = pszPassword;
10366 pSettings->pszKeyStoreLoad = pszKeyStore;
10367 pSettings->fCreateKeyStore = fCreateKeyStore;
10368 pSettings->pbDek = NULL;
10369 pSettings->cbDek = 0;
10370 pSettings->vdFilterIfaces = NULL;
10371
10372 pSettings->vdIfCfg.pfnAreKeysValid = i_vdCryptoConfigAreKeysValid;
10373 pSettings->vdIfCfg.pfnQuerySize = i_vdCryptoConfigQuerySize;
10374 pSettings->vdIfCfg.pfnQuery = i_vdCryptoConfigQuery;
10375 pSettings->vdIfCfg.pfnQueryBytes = NULL;
10376
10377 pSettings->vdIfCrypto.pfnKeyRetain = i_vdCryptoKeyRetain;
10378 pSettings->vdIfCrypto.pfnKeyRelease = i_vdCryptoKeyRelease;
10379 pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = i_vdCryptoKeyStorePasswordRetain;
10380 pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = i_vdCryptoKeyStorePasswordRelease;
10381 pSettings->vdIfCrypto.pfnKeyStoreSave = i_vdCryptoKeyStoreSave;
10382 pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = i_vdCryptoKeyStoreReturnParameters;
10383
10384 int vrc = VDInterfaceAdd(&pSettings->vdIfCfg.Core,
10385 "Medium::vdInterfaceCfgCrypto",
10386 VDINTERFACETYPE_CONFIG, pSettings,
10387 sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces);
10388 AssertRC(vrc);
10389
10390 vrc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core,
10391 "Medium::vdInterfaceCrypto",
10392 VDINTERFACETYPE_CRYPTO, pSettings,
10393 sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces);
10394 AssertRC(vrc);
10395}
10396
10397/**
10398 * Implementation code for the "encrypt" task.
10399 *
10400 * @param task
10401 * @return
10402 */
10403HRESULT Medium::i_taskEncryptHandler(Medium::EncryptTask &task)
10404{
10405# ifndef VBOX_WITH_EXTPACK
10406 RT_NOREF(task);
10407# endif
10408 HRESULT rc = S_OK;
10409
10410 /* Lock all in {parent,child} order. The lock is also used as a
10411 * signal from the task initiator (which releases it only after
10412 * RTThreadCreate()) that we can start the job. */
10413 ComObjPtr<Medium> pBase = i_getBase();
10414 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10415
10416 try
10417 {
10418# ifdef VBOX_WITH_EXTPACK
10419 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
10420 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
10421 {
10422 /* Load the plugin */
10423 Utf8Str strPlugin;
10424 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
10425 if (SUCCEEDED(rc))
10426 {
10427 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
10428 if (RT_FAILURE(vrc))
10429 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10430 tr("Encrypting the image failed because the encryption plugin could not be loaded (%s)"),
10431 i_vdError(vrc).c_str());
10432 }
10433 else
10434 throw setError(VBOX_E_NOT_SUPPORTED,
10435 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
10436 ORACLE_PUEL_EXTPACK_NAME);
10437 }
10438 else
10439 throw setError(VBOX_E_NOT_SUPPORTED,
10440 tr("Encryption is not supported because the extension pack '%s' is missing"),
10441 ORACLE_PUEL_EXTPACK_NAME);
10442
10443 PVDISK pDisk = NULL;
10444 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
10445 ComAssertRCThrow(vrc, E_FAIL);
10446
10447 MediumCryptoFilterSettings CryptoSettingsRead;
10448 MediumCryptoFilterSettings CryptoSettingsWrite;
10449
10450 void *pvBuf = NULL;
10451 const char *pszPasswordNew = NULL;
10452 try
10453 {
10454 /* Set up disk encryption filters. */
10455 if (task.mstrCurrentPassword.isEmpty())
10456 {
10457 /*
10458 * Query whether the medium property indicating that encryption is
10459 * configured is existing.
10460 */
10461 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10462 if (it != pBase->m->mapProperties.end())
10463 throw setError(VBOX_E_PASSWORD_INCORRECT,
10464 tr("The password given for the encrypted image is incorrect"));
10465 }
10466 else
10467 {
10468 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10469 if (it == pBase->m->mapProperties.end())
10470 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10471 tr("The image is not configured for encryption"));
10472
10473 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, it->second.c_str(), task.mstrCurrentPassword.c_str(),
10474 false /* fCreateKeyStore */);
10475 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
10476 if (vrc == VERR_VD_PASSWORD_INCORRECT)
10477 throw setError(VBOX_E_PASSWORD_INCORRECT,
10478 tr("The password to decrypt the image is incorrect"));
10479 else if (RT_FAILURE(vrc))
10480 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10481 tr("Failed to load the decryption filter: %s"),
10482 i_vdError(vrc).c_str());
10483 }
10484
10485 if (task.mstrCipher.isNotEmpty())
10486 {
10487 if ( task.mstrNewPassword.isEmpty()
10488 && task.mstrNewPasswordId.isEmpty()
10489 && task.mstrCurrentPassword.isNotEmpty())
10490 {
10491 /* An empty password and password ID will default to the current password. */
10492 pszPasswordNew = task.mstrCurrentPassword.c_str();
10493 }
10494 else if (task.mstrNewPassword.isEmpty())
10495 throw setError(VBOX_E_OBJECT_NOT_FOUND,
10496 tr("A password must be given for the image encryption"));
10497 else if (task.mstrNewPasswordId.isEmpty())
10498 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10499 tr("A valid identifier for the password must be given"));
10500 else
10501 pszPasswordNew = task.mstrNewPassword.c_str();
10502
10503 i_taskEncryptSettingsSetup(&CryptoSettingsWrite, task.mstrCipher.c_str(), NULL,
10504 pszPasswordNew, true /* fCreateKeyStore */);
10505 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_WRITE, CryptoSettingsWrite.vdFilterIfaces);
10506 if (RT_FAILURE(vrc))
10507 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
10508 tr("Failed to load the encryption filter: %s"),
10509 i_vdError(vrc).c_str());
10510 }
10511 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
10512 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10513 tr("The password and password identifier must be empty if the output should be unencrypted"));
10514
10515 /* Open all media in the chain. */
10516 MediumLockList::Base::const_iterator mediumListBegin =
10517 task.mpMediumLockList->GetBegin();
10518 MediumLockList::Base::const_iterator mediumListEnd =
10519 task.mpMediumLockList->GetEnd();
10520 MediumLockList::Base::const_iterator mediumListLast =
10521 mediumListEnd;
10522 --mediumListLast;
10523 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10524 it != mediumListEnd;
10525 ++it)
10526 {
10527 const MediumLock &mediumLock = *it;
10528 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10529 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10530
10531 Assert(pMedium->m->state == MediumState_LockedWrite);
10532
10533 /* Open all media but last in read-only mode. Do not handle
10534 * shareable media, as compaction and sharing are mutually
10535 * exclusive. */
10536 vrc = VDOpen(pDisk,
10537 pMedium->m->strFormat.c_str(),
10538 pMedium->m->strLocationFull.c_str(),
10539 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10540 pMedium->m->vdImageIfaces);
10541 if (RT_FAILURE(vrc))
10542 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10543 tr("Could not open the medium storage unit '%s'%s"),
10544 pMedium->m->strLocationFull.c_str(),
10545 i_vdError(vrc).c_str());
10546 }
10547
10548 Assert(m->state == MediumState_LockedWrite);
10549
10550 Utf8Str location(m->strLocationFull);
10551
10552 /* unlock before the potentially lengthy operation */
10553 thisLock.release();
10554
10555 vrc = VDPrepareWithFilters(pDisk, task.mVDOperationIfaces);
10556 if (RT_FAILURE(vrc))
10557 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10558 tr("Could not prepare disk images for encryption (%Rrc): %s"),
10559 vrc, i_vdError(vrc).c_str());
10560
10561 thisLock.acquire();
10562 /* If everything went well set the new key store. */
10563 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10564 if (it != pBase->m->mapProperties.end())
10565 pBase->m->mapProperties.erase(it);
10566
10567 /* Delete KeyId if encryption is removed or the password did change. */
10568 if ( task.mstrNewPasswordId.isNotEmpty()
10569 || task.mstrCipher.isEmpty())
10570 {
10571 it = pBase->m->mapProperties.find("CRYPT/KeyId");
10572 if (it != pBase->m->mapProperties.end())
10573 pBase->m->mapProperties.erase(it);
10574 }
10575
10576 if (CryptoSettingsWrite.pszKeyStore)
10577 {
10578 pBase->m->mapProperties["CRYPT/KeyStore"] = Utf8Str(CryptoSettingsWrite.pszKeyStore);
10579 if (task.mstrNewPasswordId.isNotEmpty())
10580 pBase->m->mapProperties["CRYPT/KeyId"] = task.mstrNewPasswordId;
10581 }
10582
10583 if (CryptoSettingsRead.pszCipherReturned)
10584 RTStrFree(CryptoSettingsRead.pszCipherReturned);
10585
10586 if (CryptoSettingsWrite.pszCipherReturned)
10587 RTStrFree(CryptoSettingsWrite.pszCipherReturned);
10588
10589 thisLock.release();
10590 pBase->i_markRegistriesModified();
10591 m->pVirtualBox->i_saveModifiedRegistries();
10592 }
10593 catch (HRESULT aRC) { rc = aRC; }
10594
10595 if (pvBuf)
10596 RTMemFree(pvBuf);
10597
10598 VDDestroy(pDisk);
10599# else
10600 throw setError(VBOX_E_NOT_SUPPORTED,
10601 tr("Encryption is not supported because extension pack support is not built in"));
10602# endif
10603 }
10604 catch (HRESULT aRC) { rc = aRC; }
10605
10606 /* Everything is explicitly unlocked when the task exits,
10607 * as the task destruction also destroys the media chain. */
10608
10609 return rc;
10610}
10611
10612/* vi: set tabstop=4 shiftwidth=4 expandtab: */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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