VirtualBox

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

Last change on this file since 85672 was 85284, checked in by vboxsync, 4 years ago

Main/MediumImpl.cpp: Signed/unsigned conversion issues, a whole bunch which was mixing VBox status codes and COM statues via setErrorVrc in the moveTo() code. Changed i_resize to take an uint64_t size rather than LONG64, and made resize() fail on negative or zero size. bugref:9790

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