VirtualBox

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

Last change on this file since 86714 was 86501, checked in by vboxsync, 4 years ago

VBoxSVC: Fix Medium leak when encoutering duplicate instances while loading a media registry (from settings) via VirtualBox::initMedia(). The media registration is now moved to before any children are loaded, so that we use the right parent for them. This essentially changes the operation to a merge. An unaddressed issue is that on child load failure, we will leave the parent(s) still registered but non-functioning (uninit was done). (The old code would just leave any successfully loaded children registered with non-working parents only accessible via their parent attribute.) bugref:9841

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

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