VirtualBox

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

Last change on this file since 92153 was 91718, checked in by vboxsync, 3 years ago

Main: bugref:1909: Added initial translation to Russian of API messages. Fixed errors and plurals wherever needed. Fixed type of the plural argument in the tr()

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