VirtualBox

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

Last change on this file since 85820 was 85820, checked in by vboxsync, 5 years ago

Main: bugref:9623: Allow MediumType_Readonly and DVD in particular to be attached twice

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 375.6 KB
Line 
1/* $Id: MediumImpl.cpp 85820 2020-08-18 13:23:26Z 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.215389.xyz. 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 {
4299 // Allow MediumType_Readonly mediums and DVD in particular to be attached twice.
4300 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
4301 if (m->type == MediumType_Readonly || m->devType == DeviceType_DVD)
4302 {
4303 BackRef ref(aMachineId, aSnapshotId);
4304 m->backRefs.push_back(ref);
4305 return S_OK;
4306 }
4307 }
4308
4309 // if the caller has not supplied a snapshot ID, then we're attaching
4310 // to a machine a medium which represents the machine's current state,
4311 // so set the flag
4312
4313 if (aSnapshotId.isZero())
4314 {
4315 /* sanity: no duplicate attachments */
4316 if (it->fInCurState)
4317 return setError(VBOX_E_OBJECT_IN_USE,
4318 tr("Cannot attach medium '%s' {%RTuuid}: medium is already associated with the current state of machine uuid {%RTuuid}!"),
4319 m->strLocationFull.c_str(),
4320 m->id.raw(),
4321 aMachineId.raw());
4322 it->fInCurState = true;
4323
4324 return S_OK;
4325 }
4326
4327 // otherwise: a snapshot medium is being attached
4328
4329 /* sanity: no duplicate attachments */
4330 for (GuidList::const_iterator jt = it->llSnapshotIds.begin();
4331 jt != it->llSnapshotIds.end();
4332 ++jt)
4333 {
4334 const Guid &idOldSnapshot = *jt;
4335
4336 if (idOldSnapshot == aSnapshotId)
4337 {
4338#ifdef DEBUG
4339 i_dumpBackRefs();
4340#endif
4341 return setError(VBOX_E_OBJECT_IN_USE,
4342 tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
4343 m->strLocationFull.c_str(),
4344 m->id.raw(),
4345 aSnapshotId.raw());
4346 }
4347 }
4348
4349 it->llSnapshotIds.push_back(aSnapshotId);
4350 // Do not touch fInCurState, as the image may be attached to the current
4351 // state *and* a snapshot, otherwise we lose the current state association!
4352
4353 LogFlowThisFuncLeave();
4354
4355 return S_OK;
4356}
4357
4358/**
4359 * Removes the given machine and optionally the snapshot from the list of the
4360 * objects this medium is attached to.
4361 *
4362 * @param aMachineId Machine ID.
4363 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
4364 * attachment.
4365 */
4366HRESULT Medium::i_removeBackReference(const Guid &aMachineId,
4367 const Guid &aSnapshotId /*= Guid::Empty*/)
4368{
4369 AssertReturn(aMachineId.isValid(), E_FAIL);
4370
4371 AutoCaller autoCaller(this);
4372 AssertComRCReturnRC(autoCaller.rc());
4373
4374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4375
4376 BackRefList::iterator it =
4377 std::find_if(m->backRefs.begin(), m->backRefs.end(),
4378 BackRef::EqualsTo(aMachineId));
4379 AssertReturn(it != m->backRefs.end(), E_FAIL);
4380
4381 if (aSnapshotId.isZero())
4382 {
4383 /* remove the current state attachment */
4384 it->fInCurState = false;
4385 }
4386 else
4387 {
4388 /* remove the snapshot attachment */
4389 GuidList::iterator jt = std::find(it->llSnapshotIds.begin(),
4390 it->llSnapshotIds.end(),
4391 aSnapshotId);
4392
4393 AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
4394 it->llSnapshotIds.erase(jt);
4395 }
4396
4397 /* if the backref becomes empty, remove it */
4398 if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
4399 m->backRefs.erase(it);
4400
4401 return S_OK;
4402}
4403
4404/**
4405 * Internal method to return the medium's list of backrefs. Must have caller + locking!
4406 * @return
4407 */
4408const Guid* Medium::i_getFirstMachineBackrefId() const
4409{
4410 if (!m->backRefs.size())
4411 return NULL;
4412
4413 return &m->backRefs.front().machineId;
4414}
4415
4416/**
4417 * Internal method which returns a machine that either this medium or one of its children
4418 * is attached to. This is used for finding a replacement media registry when an existing
4419 * media registry is about to be deleted in VirtualBox::unregisterMachine().
4420 *
4421 * Must have caller + locking, *and* caller must hold the media tree lock!
4422 * @return
4423 */
4424const Guid* Medium::i_getAnyMachineBackref() const
4425{
4426 if (m->backRefs.size())
4427 return &m->backRefs.front().machineId;
4428
4429 for (MediaList::const_iterator it = i_getChildren().begin();
4430 it != i_getChildren().end();
4431 ++it)
4432 {
4433 Medium *pChild = *it;
4434 // recurse for this child
4435 const Guid* puuid;
4436 if ((puuid = pChild->i_getAnyMachineBackref()))
4437 return puuid;
4438 }
4439
4440 return NULL;
4441}
4442
4443const Guid* Medium::i_getFirstMachineBackrefSnapshotId() const
4444{
4445 if (!m->backRefs.size())
4446 return NULL;
4447
4448 const BackRef &ref = m->backRefs.front();
4449 if (ref.llSnapshotIds.empty())
4450 return NULL;
4451
4452 return &ref.llSnapshotIds.front();
4453}
4454
4455size_t Medium::i_getMachineBackRefCount() const
4456{
4457 return m->backRefs.size();
4458}
4459
4460#ifdef DEBUG
4461/**
4462 * Debugging helper that gets called after VirtualBox initialization that writes all
4463 * machine backreferences to the debug log.
4464 */
4465void Medium::i_dumpBackRefs()
4466{
4467 AutoCaller autoCaller(this);
4468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4469
4470 LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
4471
4472 for (BackRefList::iterator it2 = m->backRefs.begin();
4473 it2 != m->backRefs.end();
4474 ++it2)
4475 {
4476 const BackRef &ref = *it2;
4477 LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d)\n", ref.machineId.raw(), ref.fInCurState));
4478
4479 for (GuidList::const_iterator jt2 = it2->llSnapshotIds.begin();
4480 jt2 != it2->llSnapshotIds.end();
4481 ++jt2)
4482 {
4483 const Guid &id = *jt2;
4484 LogFlowThisFunc((" Backref from snapshot {%RTuuid}\n", id.raw()));
4485 }
4486 }
4487}
4488#endif
4489
4490/**
4491 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
4492 * of this media and updates it if necessary to reflect the new location.
4493 *
4494 * @param strOldPath Old path (full).
4495 * @param strNewPath New path (full).
4496 *
4497 * @note Locks this object for writing.
4498 */
4499HRESULT Medium::i_updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
4500{
4501 AssertReturn(!strOldPath.isEmpty(), E_FAIL);
4502 AssertReturn(!strNewPath.isEmpty(), E_FAIL);
4503
4504 AutoCaller autoCaller(this);
4505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4506
4507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4508
4509 LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
4510
4511 const char *pcszMediumPath = m->strLocationFull.c_str();
4512
4513 if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
4514 {
4515 Utf8Str newPath(strNewPath);
4516 newPath.append(pcszMediumPath + strOldPath.length());
4517 unconst(m->strLocationFull) = newPath;
4518
4519 m->pVirtualBox->i_onMediumConfigChanged(this);
4520
4521 LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
4522 // we changed something
4523 return S_OK;
4524 }
4525
4526 // no change was necessary, signal error which the caller needs to interpret
4527 return VBOX_E_FILE_ERROR;
4528}
4529
4530/**
4531 * Returns the base medium of the media chain this medium is part of.
4532 *
4533 * The base medium is found by walking up the parent-child relationship axis.
4534 * If the medium doesn't have a parent (i.e. it's a base medium), it
4535 * returns itself in response to this method.
4536 *
4537 * @param aLevel Where to store the number of ancestors of this medium
4538 * (zero for the base), may be @c NULL.
4539 *
4540 * @note Locks medium tree for reading.
4541 */
4542ComObjPtr<Medium> Medium::i_getBase(uint32_t *aLevel /*= NULL*/)
4543{
4544 ComObjPtr<Medium> pBase;
4545
4546 /* it is possible that some previous/concurrent uninit has already cleared
4547 * the pVirtualBox reference, and in this case we don't need to continue */
4548 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4549 if (!pVirtualBox)
4550 return pBase;
4551
4552 /* we access m->pParent */
4553 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4554
4555 AutoCaller autoCaller(this);
4556 AssertReturn(autoCaller.isOk(), pBase);
4557
4558 pBase = this;
4559 uint32_t level = 0;
4560
4561 if (m->pParent)
4562 {
4563 for (;;)
4564 {
4565 AutoCaller baseCaller(pBase);
4566 AssertReturn(baseCaller.isOk(), pBase);
4567
4568 if (pBase->m->pParent.isNull())
4569 break;
4570
4571 pBase = pBase->m->pParent;
4572 ++level;
4573 }
4574 }
4575
4576 if (aLevel != NULL)
4577 *aLevel = level;
4578
4579 return pBase;
4580}
4581
4582/**
4583 * Returns the depth of this medium in the media chain.
4584 *
4585 * @note Locks medium tree for reading.
4586 */
4587uint32_t Medium::i_getDepth()
4588{
4589 /* it is possible that some previous/concurrent uninit has already cleared
4590 * the pVirtualBox reference, and in this case we don't need to continue */
4591 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4592 if (!pVirtualBox)
4593 return 1;
4594
4595 /* we access m->pParent */
4596 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4597
4598 uint32_t cDepth = 0;
4599 ComObjPtr<Medium> pMedium(this);
4600 while (!pMedium.isNull())
4601 {
4602 AutoCaller autoCaller(this);
4603 AssertReturn(autoCaller.isOk(), cDepth + 1);
4604
4605 pMedium = pMedium->m->pParent;
4606 cDepth++;
4607 }
4608
4609 return cDepth;
4610}
4611
4612/**
4613 * Returns @c true if this medium cannot be modified because it has
4614 * dependents (children) or is part of the snapshot. Related to the medium
4615 * type and posterity, not to the current media state.
4616 *
4617 * @note Locks this object and medium tree for reading.
4618 */
4619bool Medium::i_isReadOnly()
4620{
4621 /* it is possible that some previous/concurrent uninit has already cleared
4622 * the pVirtualBox reference, and in this case we don't need to continue */
4623 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4624 if (!pVirtualBox)
4625 return false;
4626
4627 /* we access children */
4628 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4629
4630 AutoCaller autoCaller(this);
4631 AssertComRCReturn(autoCaller.rc(), false);
4632
4633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4634
4635 switch (m->type)
4636 {
4637 case MediumType_Normal:
4638 {
4639 if (i_getChildren().size() != 0)
4640 return true;
4641
4642 for (BackRefList::const_iterator it = m->backRefs.begin();
4643 it != m->backRefs.end(); ++it)
4644 if (it->llSnapshotIds.size() != 0)
4645 return true;
4646
4647 if (m->variant & MediumVariant_VmdkStreamOptimized)
4648 return true;
4649
4650 return false;
4651 }
4652 case MediumType_Immutable:
4653 case MediumType_MultiAttach:
4654 return true;
4655 case MediumType_Writethrough:
4656 case MediumType_Shareable:
4657 case MediumType_Readonly: /* explicit readonly media has no diffs */
4658 return false;
4659 default:
4660 break;
4661 }
4662
4663 AssertFailedReturn(false);
4664}
4665
4666/**
4667 * Internal method to update the medium's id. Must have caller + locking!
4668 * @return
4669 */
4670void Medium::i_updateId(const Guid &id)
4671{
4672 unconst(m->id) = id;
4673}
4674
4675/**
4676 * Saves the settings of one medium.
4677 *
4678 * @note Caller MUST take care of the medium tree lock and caller.
4679 *
4680 * @param data Settings struct to be updated.
4681 * @param strHardDiskFolder Folder for which paths should be relative.
4682 */
4683void Medium::i_saveSettingsOne(settings::Medium &data, const Utf8Str &strHardDiskFolder)
4684{
4685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4686
4687 data.uuid = m->id;
4688
4689 // make path relative if needed
4690 if ( !strHardDiskFolder.isEmpty()
4691 && RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
4692 )
4693 data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
4694 else
4695 data.strLocation = m->strLocationFull;
4696 data.strFormat = m->strFormat;
4697
4698 /* optional, only for diffs, default is false */
4699 if (m->pParent)
4700 data.fAutoReset = m->autoReset;
4701 else
4702 data.fAutoReset = false;
4703
4704 /* optional */
4705 data.strDescription = m->strDescription;
4706
4707 /* optional properties */
4708 data.properties.clear();
4709
4710 /* handle iSCSI initiator secrets transparently */
4711 bool fHaveInitiatorSecretEncrypted = false;
4712 Utf8Str strCiphertext;
4713 settings::StringsMap::const_iterator itPln = m->mapProperties.find("InitiatorSecret");
4714 if ( itPln != m->mapProperties.end()
4715 && !itPln->second.isEmpty())
4716 {
4717 /* Encrypt the plain secret. If that does not work (i.e. no or wrong settings key
4718 * specified), just use the encrypted secret (if there is any). */
4719 int rc = m->pVirtualBox->i_encryptSetting(itPln->second, &strCiphertext);
4720 if (RT_SUCCESS(rc))
4721 fHaveInitiatorSecretEncrypted = true;
4722 }
4723 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
4724 it != m->mapProperties.end();
4725 ++it)
4726 {
4727 /* only save properties that have non-default values */
4728 if (!it->second.isEmpty())
4729 {
4730 const Utf8Str &name = it->first;
4731 const Utf8Str &value = it->second;
4732 bool fCreateOnly = false;
4733 for (MediumFormat::PropertyArray::const_iterator itf = m->formatObj->i_getProperties().begin();
4734 itf != m->formatObj->i_getProperties().end();
4735 ++itf)
4736 {
4737 if (itf->strName.equals(name) &&
4738 (itf->flags & VD_CFGKEY_CREATEONLY))
4739 {
4740 fCreateOnly = true;
4741 break;
4742 }
4743 }
4744 if (!fCreateOnly)
4745 /* do NOT store the plain InitiatorSecret */
4746 if ( !fHaveInitiatorSecretEncrypted
4747 || !name.equals("InitiatorSecret"))
4748 data.properties[name] = value; }
4749 }
4750 if (fHaveInitiatorSecretEncrypted)
4751 data.properties["InitiatorSecretEncrypted"] = strCiphertext;
4752
4753 /* only for base media */
4754 if (m->pParent.isNull())
4755 data.hdType = m->type;
4756}
4757
4758/**
4759 * Saves medium data by putting it into the provided data structure.
4760 * Recurses over all children to save their settings, too.
4761 *
4762 * @param data Settings struct to be updated.
4763 * @param strHardDiskFolder Folder for which paths should be relative.
4764 *
4765 * @note Locks this object, medium tree and children for reading.
4766 */
4767HRESULT Medium::i_saveSettings(settings::Medium &data,
4768 const Utf8Str &strHardDiskFolder)
4769{
4770 /* we access m->pParent */
4771 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4772
4773 AutoCaller autoCaller(this);
4774 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4775
4776 i_saveSettingsOne(data, strHardDiskFolder);
4777
4778 /* save all children */
4779 settings::MediaList &llSettingsChildren = data.llChildren;
4780 for (MediaList::const_iterator it = i_getChildren().begin();
4781 it != i_getChildren().end();
4782 ++it)
4783 {
4784 // Use the element straight in the list to reduce both unnecessary
4785 // deep copying (when unwinding the recursion the entire medium
4786 // settings sub-tree is copied) and the stack footprint (the settings
4787 // need almost 1K, and there can be VMs with long image chains.
4788 llSettingsChildren.push_back(settings::Medium::Empty);
4789 HRESULT rc = (*it)->i_saveSettings(llSettingsChildren.back(), strHardDiskFolder);
4790 if (FAILED(rc))
4791 {
4792 llSettingsChildren.pop_back();
4793 return rc;
4794 }
4795 }
4796
4797 return S_OK;
4798}
4799
4800/**
4801 * Constructs a medium lock list for this medium. The lock is not taken.
4802 *
4803 * @note Caller MUST NOT hold the media tree or medium lock.
4804 *
4805 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
4806 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
4807 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
4808 * @param pToLockWrite If not NULL, associate a write lock with this medium object.
4809 * @param fMediumLockWriteAll Whether to associate a write lock to all other media too.
4810 * @param pToBeParent Medium which will become the parent of this medium.
4811 * @param mediumLockList Where to store the resulting list.
4812 */
4813HRESULT Medium::i_createMediumLockList(bool fFailIfInaccessible,
4814 Medium *pToLockWrite,
4815 bool fMediumLockWriteAll,
4816 Medium *pToBeParent,
4817 MediumLockList &mediumLockList)
4818{
4819 /** @todo r=klaus this needs to be reworked, as the code below uses
4820 * i_getParent without holding the tree lock, and changing this is
4821 * a significant amount of effort. */
4822 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4823 Assert(!isWriteLockOnCurrentThread());
4824
4825 AutoCaller autoCaller(this);
4826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4827
4828 HRESULT rc = S_OK;
4829
4830 /* paranoid sanity checking if the medium has a to-be parent medium */
4831 if (pToBeParent)
4832 {
4833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4834 ComAssertRet(i_getParent().isNull(), E_FAIL);
4835 ComAssertRet(i_getChildren().size() == 0, E_FAIL);
4836 }
4837
4838 ErrorInfoKeeper eik;
4839 MultiResult mrc(S_OK);
4840
4841 ComObjPtr<Medium> pMedium = this;
4842 while (!pMedium.isNull())
4843 {
4844 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4845
4846 /* Accessibility check must be first, otherwise locking interferes
4847 * with getting the medium state. Lock lists are not created for
4848 * fun, and thus getting the medium status is no luxury. */
4849 MediumState_T mediumState = pMedium->i_getState();
4850 if (mediumState == MediumState_Inaccessible)
4851 {
4852 alock.release();
4853 rc = pMedium->i_queryInfo(false /* fSetImageId */, false /* fSetParentId */,
4854 autoCaller);
4855 alock.acquire();
4856 if (FAILED(rc)) return rc;
4857
4858 mediumState = pMedium->i_getState();
4859 if (mediumState == MediumState_Inaccessible)
4860 {
4861 // ignore inaccessible ISO media and silently return S_OK,
4862 // otherwise VM startup (esp. restore) may fail without good reason
4863 if (!fFailIfInaccessible)
4864 return S_OK;
4865
4866 // otherwise report an error
4867 Bstr error;
4868 rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
4869 if (FAILED(rc)) return rc;
4870
4871 /* collect multiple errors */
4872 eik.restore();
4873 Assert(!error.isEmpty());
4874 mrc = setError(E_FAIL,
4875 "%ls",
4876 error.raw());
4877 // error message will be something like
4878 // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
4879 eik.fetch();
4880 }
4881 }
4882
4883 if (pMedium == pToLockWrite)
4884 mediumLockList.Prepend(pMedium, true);
4885 else
4886 mediumLockList.Prepend(pMedium, fMediumLockWriteAll);
4887
4888 pMedium = pMedium->i_getParent();
4889 if (pMedium.isNull() && pToBeParent)
4890 {
4891 pMedium = pToBeParent;
4892 pToBeParent = NULL;
4893 }
4894 }
4895
4896 return mrc;
4897}
4898
4899/**
4900 * Creates a new differencing storage unit using the format of the given target
4901 * medium and the location. Note that @c aTarget must be NotCreated.
4902 *
4903 * The @a aMediumLockList parameter contains the associated medium lock list,
4904 * which must be in locked state. If @a aWait is @c true then the caller is
4905 * responsible for unlocking.
4906 *
4907 * If @a aProgress is not NULL but the object it points to is @c null then a
4908 * new progress object will be created and assigned to @a *aProgress on
4909 * success, otherwise the existing progress object is used. If @a aProgress is
4910 * NULL, then no progress object is created/used at all.
4911 *
4912 * When @a aWait is @c false, this method will create a thread to perform the
4913 * create operation asynchronously and will return immediately. Otherwise, it
4914 * will perform the operation on the calling thread and will not return to the
4915 * caller until the operation is completed. Note that @a aProgress cannot be
4916 * NULL when @a aWait is @c false (this method will assert in this case).
4917 *
4918 * @param aTarget Target medium.
4919 * @param aVariant Precise medium variant to create.
4920 * @param aMediumLockList List of media which should be locked.
4921 * @param aProgress Where to find/store a Progress object to track
4922 * operation completion.
4923 * @param aWait @c true if this method should block instead of
4924 * creating an asynchronous thread.
4925 * @param aNotify Notify about mediums which metadatа are changed
4926 * during execution of the function.
4927 *
4928 * @note Locks this object and @a aTarget for writing.
4929 */
4930HRESULT Medium::i_createDiffStorage(ComObjPtr<Medium> &aTarget,
4931 MediumVariant_T aVariant,
4932 MediumLockList *aMediumLockList,
4933 ComObjPtr<Progress> *aProgress,
4934 bool aWait,
4935 bool aNotify)
4936{
4937 AssertReturn(!aTarget.isNull(), E_FAIL);
4938 AssertReturn(aMediumLockList, E_FAIL);
4939 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4940
4941 AutoCaller autoCaller(this);
4942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4943
4944 AutoCaller targetCaller(aTarget);
4945 if (FAILED(targetCaller.rc())) return targetCaller.rc();
4946
4947 HRESULT rc = S_OK;
4948 ComObjPtr<Progress> pProgress;
4949 Medium::Task *pTask = NULL;
4950
4951 try
4952 {
4953 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
4954
4955 ComAssertThrow( m->type != MediumType_Writethrough
4956 && m->type != MediumType_Shareable
4957 && m->type != MediumType_Readonly, E_FAIL);
4958 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
4959
4960 if (aTarget->m->state != MediumState_NotCreated)
4961 throw aTarget->i_setStateError();
4962
4963 /* Check that the medium is not attached to the current state of
4964 * any VM referring to it. */
4965 for (BackRefList::const_iterator it = m->backRefs.begin();
4966 it != m->backRefs.end();
4967 ++it)
4968 {
4969 if (it->fInCurState)
4970 {
4971 /* Note: when a VM snapshot is being taken, all normal media
4972 * attached to the VM in the current state will be, as an
4973 * exception, also associated with the snapshot which is about
4974 * to create (see SnapshotMachine::init()) before deassociating
4975 * them from the current state (which takes place only on
4976 * success in Machine::fixupHardDisks()), so that the size of
4977 * snapshotIds will be 1 in this case. The extra condition is
4978 * used to filter out this legal situation. */
4979 if (it->llSnapshotIds.size() == 0)
4980 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4981 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"),
4982 m->strLocationFull.c_str(), it->machineId.raw());
4983
4984 Assert(it->llSnapshotIds.size() == 1);
4985 }
4986 }
4987
4988 if (aProgress != NULL)
4989 {
4990 /* use the existing progress object... */
4991 pProgress = *aProgress;
4992
4993 /* ...but create a new one if it is null */
4994 if (pProgress.isNull())
4995 {
4996 pProgress.createObject();
4997 rc = pProgress->init(m->pVirtualBox,
4998 static_cast<IMedium*>(this),
4999 BstrFmt(tr("Creating differencing medium storage unit '%s'"),
5000 aTarget->m->strLocationFull.c_str()).raw(),
5001 TRUE /* aCancelable */);
5002 if (FAILED(rc))
5003 throw rc;
5004 }
5005 }
5006
5007 /* setup task object to carry out the operation sync/async */
5008 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
5009 aMediumLockList,
5010 aWait /* fKeepMediumLockList */,
5011 aNotify);
5012 rc = pTask->rc();
5013 AssertComRC(rc);
5014 if (FAILED(rc))
5015 throw rc;
5016
5017 /* register a task (it will deregister itself when done) */
5018 ++m->numCreateDiffTasks;
5019 Assert(m->numCreateDiffTasks != 0); /* overflow? */
5020
5021 aTarget->m->state = MediumState_Creating;
5022 }
5023 catch (HRESULT aRC) { rc = aRC; }
5024
5025 if (SUCCEEDED(rc))
5026 {
5027 if (aWait)
5028 {
5029 rc = pTask->runNow();
5030 delete pTask;
5031 }
5032 else
5033 rc = pTask->createThread();
5034 pTask = NULL;
5035 if (SUCCEEDED(rc) && aProgress != NULL)
5036 *aProgress = pProgress;
5037 }
5038 else if (pTask != NULL)
5039 delete pTask;
5040
5041 return rc;
5042}
5043
5044/**
5045 * Returns a preferred format for differencing media.
5046 */
5047Utf8Str Medium::i_getPreferredDiffFormat()
5048{
5049 AutoCaller autoCaller(this);
5050 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
5051
5052 /* check that our own format supports diffs */
5053 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5054 {
5055 /* use the default format if not */
5056 Utf8Str tmp;
5057 m->pVirtualBox->i_getDefaultHardDiskFormat(tmp);
5058 return tmp;
5059 }
5060
5061 /* m->strFormat is const, no need to lock */
5062 return m->strFormat;
5063}
5064
5065/**
5066 * Returns a preferred variant for differencing media.
5067 */
5068MediumVariant_T Medium::i_getPreferredDiffVariant()
5069{
5070 AutoCaller autoCaller(this);
5071 AssertComRCReturn(autoCaller.rc(), MediumVariant_Standard);
5072
5073 /* check that our own format supports diffs */
5074 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5075 return MediumVariant_Standard;
5076
5077 /* m->variant is const, no need to lock */
5078 ULONG mediumVariantFlags = (ULONG)m->variant;
5079 mediumVariantFlags &= ~(ULONG)(MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized);
5080 mediumVariantFlags |= MediumVariant_Diff;
5081 return (MediumVariant_T)mediumVariantFlags;
5082}
5083
5084/**
5085 * Implementation for the public Medium::Close() with the exception of calling
5086 * VirtualBox::saveRegistries(), in case someone wants to call this for several
5087 * media.
5088 *
5089 * After this returns with success, uninit() has been called on the medium, and
5090 * the object is no longer usable ("not ready" state).
5091 *
5092 * @param autoCaller AutoCaller instance which must have been created on the caller's
5093 * stack for this medium. This gets released hereupon
5094 * which the Medium instance gets uninitialized.
5095 * @return
5096 */
5097HRESULT Medium::i_close(AutoCaller &autoCaller)
5098{
5099 // must temporarily drop the caller, need the tree lock first
5100 autoCaller.release();
5101
5102 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
5103 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
5104 this->lockHandle()
5105 COMMA_LOCKVAL_SRC_POS);
5106
5107 autoCaller.add();
5108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5109
5110 LogFlowFunc(("ENTER for %s\n", i_getLocationFull().c_str()));
5111
5112 bool wasCreated = true;
5113
5114 switch (m->state)
5115 {
5116 case MediumState_NotCreated:
5117 wasCreated = false;
5118 break;
5119 case MediumState_Created:
5120 case MediumState_Inaccessible:
5121 break;
5122 default:
5123 return i_setStateError();
5124 }
5125
5126 if (m->backRefs.size() != 0)
5127 return setError(VBOX_E_OBJECT_IN_USE,
5128 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
5129 m->strLocationFull.c_str(), m->backRefs.size());
5130
5131 // perform extra media-dependent close checks
5132 HRESULT rc = i_canClose();
5133 if (FAILED(rc)) return rc;
5134
5135 m->fClosing = true;
5136
5137 if (wasCreated)
5138 {
5139 // remove from the list of known media before performing actual
5140 // uninitialization (to keep the media registry consistent on
5141 // failure to do so)
5142 rc = i_unregisterWithVirtualBox();
5143 if (FAILED(rc)) return rc;
5144
5145 multilock.release();
5146 // Release the AutoCaller now, as otherwise uninit() will simply hang.
5147 // Needs to be done before mark the registries as modified and saving
5148 // the registry, as otherwise there may be a deadlock with someone else
5149 // closing this object while we're in i_saveModifiedRegistries(), which
5150 // needs the media tree lock, which the other thread holds until after
5151 // uninit() below.
5152 autoCaller.release();
5153 i_markRegistriesModified();
5154 m->pVirtualBox->i_saveModifiedRegistries();
5155 }
5156 else
5157 {
5158 multilock.release();
5159 // release the AutoCaller, as otherwise uninit() will simply hang
5160 autoCaller.release();
5161 }
5162
5163 // Keep the locks held until after uninit, as otherwise the consistency
5164 // of the medium tree cannot be guaranteed.
5165 uninit();
5166
5167 LogFlowFuncLeave();
5168
5169 return rc;
5170}
5171
5172/**
5173 * Deletes the medium storage unit.
5174 *
5175 * If @a aProgress is not NULL but the object it points to is @c null then a new
5176 * progress object will be created and assigned to @a *aProgress on success,
5177 * otherwise the existing progress object is used. If Progress is NULL, then no
5178 * progress object is created/used at all.
5179 *
5180 * When @a aWait is @c false, this method will create a thread to perform the
5181 * delete operation asynchronously and will return immediately. Otherwise, it
5182 * will perform the operation on the calling thread and will not return to the
5183 * caller until the operation is completed. Note that @a aProgress cannot be
5184 * NULL when @a aWait is @c false (this method will assert in this case).
5185 *
5186 * @param aProgress Where to find/store a Progress object to track operation
5187 * completion.
5188 * @param aWait @c true if this method should block instead of creating
5189 * an asynchronous thread.
5190 * @param aNotify Notify about mediums which metadatа are changed
5191 * during execution of the function.
5192 *
5193 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
5194 * writing.
5195 */
5196HRESULT Medium::i_deleteStorage(ComObjPtr<Progress> *aProgress,
5197 bool aWait, bool aNotify)
5198{
5199 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5200
5201 HRESULT rc = S_OK;
5202 ComObjPtr<Progress> pProgress;
5203 Medium::Task *pTask = NULL;
5204
5205 try
5206 {
5207 /* we're accessing the media tree, and canClose() needs it too */
5208 AutoWriteLock treelock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5209
5210 AutoCaller autoCaller(this);
5211 AssertComRCThrowRC(autoCaller.rc());
5212
5213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5214
5215 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, i_getLocationFull().c_str() ));
5216
5217 if ( !(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
5218 | MediumFormatCapabilities_CreateFixed)))
5219 throw setError(VBOX_E_NOT_SUPPORTED,
5220 tr("Medium format '%s' does not support storage deletion"),
5221 m->strFormat.c_str());
5222
5223 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5224 /** @todo r=klaus would be great if this could be moved to the async
5225 * part of the operation as it can take quite a while */
5226 if (m->queryInfoRunning)
5227 {
5228 while (m->queryInfoRunning)
5229 {
5230 alock.release();
5231 autoCaller.release();
5232 treelock.release();
5233 /* Must not hold the media tree lock or the object lock, as
5234 * Medium::i_queryInfo needs this lock and thus we would run
5235 * into a deadlock here. */
5236 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5237 Assert(!isWriteLockOnCurrentThread());
5238 {
5239 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5240 }
5241 treelock.acquire();
5242 autoCaller.add();
5243 AssertComRCThrowRC(autoCaller.rc());
5244 alock.acquire();
5245 }
5246 }
5247
5248 /* Note that we are fine with Inaccessible state too: a) for symmetry
5249 * with create calls and b) because it doesn't really harm to try, if
5250 * it is really inaccessible, the delete operation will fail anyway.
5251 * Accepting Inaccessible state is especially important because all
5252 * registered media are initially Inaccessible upon VBoxSVC startup
5253 * until COMGETTER(RefreshState) is called. Accept Deleting state
5254 * because some callers need to put the medium in this state early
5255 * to prevent races. */
5256 switch (m->state)
5257 {
5258 case MediumState_Created:
5259 case MediumState_Deleting:
5260 case MediumState_Inaccessible:
5261 break;
5262 default:
5263 throw i_setStateError();
5264 }
5265
5266 if (m->backRefs.size() != 0)
5267 {
5268 Utf8Str strMachines;
5269 for (BackRefList::const_iterator it = m->backRefs.begin();
5270 it != m->backRefs.end();
5271 ++it)
5272 {
5273 const BackRef &b = *it;
5274 if (strMachines.length())
5275 strMachines.append(", ");
5276 strMachines.append(b.machineId.toString().c_str());
5277 }
5278#ifdef DEBUG
5279 i_dumpBackRefs();
5280#endif
5281 throw setError(VBOX_E_OBJECT_IN_USE,
5282 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
5283 m->strLocationFull.c_str(),
5284 m->backRefs.size(),
5285 strMachines.c_str());
5286 }
5287
5288 rc = i_canClose();
5289 if (FAILED(rc))
5290 throw rc;
5291
5292 /* go to Deleting state, so that the medium is not actually locked */
5293 if (m->state != MediumState_Deleting)
5294 {
5295 rc = i_markForDeletion();
5296 if (FAILED(rc))
5297 throw rc;
5298 }
5299
5300 /* Build the medium lock list. */
5301 MediumLockList *pMediumLockList(new MediumLockList());
5302 alock.release();
5303 autoCaller.release();
5304 treelock.release();
5305 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5306 this /* pToLockWrite */,
5307 false /* fMediumLockWriteAll */,
5308 NULL,
5309 *pMediumLockList);
5310 treelock.acquire();
5311 autoCaller.add();
5312 AssertComRCThrowRC(autoCaller.rc());
5313 alock.acquire();
5314 if (FAILED(rc))
5315 {
5316 delete pMediumLockList;
5317 throw rc;
5318 }
5319
5320 alock.release();
5321 autoCaller.release();
5322 treelock.release();
5323 rc = pMediumLockList->Lock();
5324 treelock.acquire();
5325 autoCaller.add();
5326 AssertComRCThrowRC(autoCaller.rc());
5327 alock.acquire();
5328 if (FAILED(rc))
5329 {
5330 delete pMediumLockList;
5331 throw setError(rc,
5332 tr("Failed to lock media when deleting '%s'"),
5333 i_getLocationFull().c_str());
5334 }
5335
5336 /* try to remove from the list of known media before performing
5337 * actual deletion (we favor the consistency of the media registry
5338 * which would have been broken if unregisterWithVirtualBox() failed
5339 * after we successfully deleted the storage) */
5340 rc = i_unregisterWithVirtualBox();
5341 if (FAILED(rc))
5342 throw rc;
5343 // no longer need lock
5344 alock.release();
5345 autoCaller.release();
5346 treelock.release();
5347 i_markRegistriesModified();
5348
5349 if (aProgress != NULL)
5350 {
5351 /* use the existing progress object... */
5352 pProgress = *aProgress;
5353
5354 /* ...but create a new one if it is null */
5355 if (pProgress.isNull())
5356 {
5357 pProgress.createObject();
5358 rc = pProgress->init(m->pVirtualBox,
5359 static_cast<IMedium*>(this),
5360 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
5361 FALSE /* aCancelable */);
5362 if (FAILED(rc))
5363 throw rc;
5364 }
5365 }
5366
5367 /* setup task object to carry out the operation sync/async */
5368 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList, false, aNotify);
5369 rc = pTask->rc();
5370 AssertComRC(rc);
5371 if (FAILED(rc))
5372 throw rc;
5373 }
5374 catch (HRESULT aRC) { rc = aRC; }
5375
5376 if (SUCCEEDED(rc))
5377 {
5378 if (aWait)
5379 {
5380 rc = pTask->runNow();
5381 delete pTask;
5382 }
5383 else
5384 rc = pTask->createThread();
5385 pTask = NULL;
5386 if (SUCCEEDED(rc) && aProgress != NULL)
5387 *aProgress = pProgress;
5388 }
5389 else
5390 {
5391 if (pTask)
5392 delete pTask;
5393
5394 /* Undo deleting state if necessary. */
5395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5396 /* Make sure that any error signalled by unmarkForDeletion() is not
5397 * ending up in the error list (if the caller uses MultiResult). It
5398 * usually is spurious, as in most cases the medium hasn't been marked
5399 * for deletion when the error was thrown above. */
5400 ErrorInfoKeeper eik;
5401 i_unmarkForDeletion();
5402 }
5403
5404 return rc;
5405}
5406
5407/**
5408 * Mark a medium for deletion.
5409 *
5410 * @note Caller must hold the write lock on this medium!
5411 */
5412HRESULT Medium::i_markForDeletion()
5413{
5414 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5415 switch (m->state)
5416 {
5417 case MediumState_Created:
5418 case MediumState_Inaccessible:
5419 m->preLockState = m->state;
5420 m->state = MediumState_Deleting;
5421 return S_OK;
5422 default:
5423 return i_setStateError();
5424 }
5425}
5426
5427/**
5428 * Removes the "mark for deletion".
5429 *
5430 * @note Caller must hold the write lock on this medium!
5431 */
5432HRESULT Medium::i_unmarkForDeletion()
5433{
5434 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5435 switch (m->state)
5436 {
5437 case MediumState_Deleting:
5438 m->state = m->preLockState;
5439 return S_OK;
5440 default:
5441 return i_setStateError();
5442 }
5443}
5444
5445/**
5446 * Mark a medium for deletion which is in locked state.
5447 *
5448 * @note Caller must hold the write lock on this medium!
5449 */
5450HRESULT Medium::i_markLockedForDeletion()
5451{
5452 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5453 if ( ( m->state == MediumState_LockedRead
5454 || m->state == MediumState_LockedWrite)
5455 && m->preLockState == MediumState_Created)
5456 {
5457 m->preLockState = MediumState_Deleting;
5458 return S_OK;
5459 }
5460 else
5461 return i_setStateError();
5462}
5463
5464/**
5465 * Removes the "mark for deletion" for a medium in locked state.
5466 *
5467 * @note Caller must hold the write lock on this medium!
5468 */
5469HRESULT Medium::i_unmarkLockedForDeletion()
5470{
5471 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5472 if ( ( m->state == MediumState_LockedRead
5473 || m->state == MediumState_LockedWrite)
5474 && m->preLockState == MediumState_Deleting)
5475 {
5476 m->preLockState = MediumState_Created;
5477 return S_OK;
5478 }
5479 else
5480 return i_setStateError();
5481}
5482
5483/**
5484 * Queries the preferred merge direction from this to the other medium, i.e.
5485 * the one which requires the least amount of I/O and therefore time and
5486 * disk consumption.
5487 *
5488 * @returns Status code.
5489 * @retval E_FAIL in case determining the merge direction fails for some reason,
5490 * for example if getting the size of the media fails. There is no
5491 * error set though and the caller is free to continue to find out
5492 * what was going wrong later. Leaves fMergeForward unset.
5493 * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
5494 * An error is set.
5495 * @param pOther The other medium to merge with.
5496 * @param fMergeForward Resulting preferred merge direction (out).
5497 */
5498HRESULT Medium::i_queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
5499 bool &fMergeForward)
5500{
5501 AssertReturn(pOther != NULL, E_FAIL);
5502 AssertReturn(pOther != this, E_FAIL);
5503
5504 HRESULT rc = S_OK;
5505 bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
5506
5507 try
5508 {
5509 // locking: we need the tree lock first because we access parent pointers
5510 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5511
5512 AutoCaller autoCaller(this);
5513 AssertComRCThrowRC(autoCaller.rc());
5514
5515 AutoCaller otherCaller(pOther);
5516 AssertComRCThrowRC(otherCaller.rc());
5517
5518 /* more sanity checking and figuring out the current merge direction */
5519 ComObjPtr<Medium> pMedium = i_getParent();
5520 while (!pMedium.isNull() && pMedium != pOther)
5521 pMedium = pMedium->i_getParent();
5522 if (pMedium == pOther)
5523 fThisParent = false;
5524 else
5525 {
5526 pMedium = pOther->i_getParent();
5527 while (!pMedium.isNull() && pMedium != this)
5528 pMedium = pMedium->i_getParent();
5529 if (pMedium == this)
5530 fThisParent = true;
5531 else
5532 {
5533 Utf8Str tgtLoc;
5534 {
5535 AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
5536 tgtLoc = pOther->i_getLocationFull();
5537 }
5538
5539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5540 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5541 tr("Media '%s' and '%s' are unrelated"),
5542 m->strLocationFull.c_str(), tgtLoc.c_str());
5543 }
5544 }
5545
5546 /*
5547 * Figure out the preferred merge direction. The current way is to
5548 * get the current sizes of file based images and select the merge
5549 * direction depending on the size.
5550 *
5551 * Can't use the VD API to get current size here as the media might
5552 * be write locked by a running VM. Resort to RTFileQuerySize().
5553 */
5554 int vrc = VINF_SUCCESS;
5555 uint64_t cbMediumThis = 0;
5556 uint64_t cbMediumOther = 0;
5557
5558 if (i_isMediumFormatFile() && pOther->i_isMediumFormatFile())
5559 {
5560 vrc = RTFileQuerySizeByPath(this->i_getLocationFull().c_str(), &cbMediumThis);
5561 if (RT_SUCCESS(vrc))
5562 {
5563 vrc = RTFileQuerySizeByPath(pOther->i_getLocationFull().c_str(),
5564 &cbMediumOther);
5565 }
5566
5567 if (RT_FAILURE(vrc))
5568 rc = E_FAIL;
5569 else
5570 {
5571 /*
5572 * Check which merge direction might be more optimal.
5573 * This method is not bullet proof of course as there might
5574 * be overlapping blocks in the images so the file size is
5575 * not the best indicator but it is good enough for our purpose
5576 * and everything else is too complicated, especially when the
5577 * media are used by a running VM.
5578 */
5579
5580 uint32_t mediumVariants = MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized;
5581 uint32_t mediumCaps = MediumFormatCapabilities_CreateDynamic | MediumFormatCapabilities_File;
5582
5583 bool fDynamicOther = pOther->i_getMediumFormat()->i_getCapabilities() & mediumCaps
5584 && pOther->i_getVariant() & ~mediumVariants;
5585 bool fDynamicThis = i_getMediumFormat()->i_getCapabilities() & mediumCaps
5586 && i_getVariant() & ~mediumVariants;
5587 bool fMergeIntoThis = (fDynamicThis && !fDynamicOther)
5588 || (fDynamicThis == fDynamicOther && cbMediumThis > cbMediumOther);
5589 fMergeForward = fMergeIntoThis != fThisParent;
5590 }
5591 }
5592 }
5593 catch (HRESULT aRC) { rc = aRC; }
5594
5595 return rc;
5596}
5597
5598/**
5599 * Prepares this (source) medium, target medium and all intermediate media
5600 * for the merge operation.
5601 *
5602 * This method is to be called prior to calling the #mergeTo() to perform
5603 * necessary consistency checks and place involved media to appropriate
5604 * states. If #mergeTo() is not called or fails, the state modifications
5605 * performed by this method must be undone by #i_cancelMergeTo().
5606 *
5607 * See #mergeTo() for more information about merging.
5608 *
5609 * @param pTarget Target medium.
5610 * @param aMachineId Allowed machine attachment. NULL means do not check.
5611 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
5612 * do not check.
5613 * @param fLockMedia Flag whether to lock the medium lock list or not.
5614 * If set to false and the medium lock list locking fails
5615 * later you must call #i_cancelMergeTo().
5616 * @param fMergeForward Resulting merge direction (out).
5617 * @param pParentForTarget New parent for target medium after merge (out).
5618 * @param aChildrenToReparent Medium lock list containing all children of the
5619 * source which will have to be reparented to the target
5620 * after merge (out).
5621 * @param aMediumLockList Medium locking information (out).
5622 *
5623 * @note Locks medium tree for reading. Locks this object, aTarget and all
5624 * intermediate media for writing.
5625 */
5626HRESULT Medium::i_prepareMergeTo(const ComObjPtr<Medium> &pTarget,
5627 const Guid *aMachineId,
5628 const Guid *aSnapshotId,
5629 bool fLockMedia,
5630 bool &fMergeForward,
5631 ComObjPtr<Medium> &pParentForTarget,
5632 MediumLockList * &aChildrenToReparent,
5633 MediumLockList * &aMediumLockList)
5634{
5635 AssertReturn(pTarget != NULL, E_FAIL);
5636 AssertReturn(pTarget != this, E_FAIL);
5637
5638 HRESULT rc = S_OK;
5639 fMergeForward = false;
5640 pParentForTarget.setNull();
5641 Assert(aChildrenToReparent == NULL);
5642 aChildrenToReparent = NULL;
5643 Assert(aMediumLockList == NULL);
5644 aMediumLockList = NULL;
5645
5646 try
5647 {
5648 // locking: we need the tree lock first because we access parent pointers
5649 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5650
5651 AutoCaller autoCaller(this);
5652 AssertComRCThrowRC(autoCaller.rc());
5653
5654 AutoCaller targetCaller(pTarget);
5655 AssertComRCThrowRC(targetCaller.rc());
5656
5657 /* more sanity checking and figuring out the merge direction */
5658 ComObjPtr<Medium> pMedium = i_getParent();
5659 while (!pMedium.isNull() && pMedium != pTarget)
5660 pMedium = pMedium->i_getParent();
5661 if (pMedium == pTarget)
5662 fMergeForward = false;
5663 else
5664 {
5665 pMedium = pTarget->i_getParent();
5666 while (!pMedium.isNull() && pMedium != this)
5667 pMedium = pMedium->i_getParent();
5668 if (pMedium == this)
5669 fMergeForward = true;
5670 else
5671 {
5672 Utf8Str tgtLoc;
5673 {
5674 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5675 tgtLoc = pTarget->i_getLocationFull();
5676 }
5677
5678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5679 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5680 tr("Media '%s' and '%s' are unrelated"),
5681 m->strLocationFull.c_str(), tgtLoc.c_str());
5682 }
5683 }
5684
5685 /* Build the lock list. */
5686 aMediumLockList = new MediumLockList();
5687 targetCaller.release();
5688 autoCaller.release();
5689 treeLock.release();
5690 if (fMergeForward)
5691 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
5692 pTarget /* pToLockWrite */,
5693 false /* fMediumLockWriteAll */,
5694 NULL,
5695 *aMediumLockList);
5696 else
5697 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5698 pTarget /* pToLockWrite */,
5699 false /* fMediumLockWriteAll */,
5700 NULL,
5701 *aMediumLockList);
5702 treeLock.acquire();
5703 autoCaller.add();
5704 AssertComRCThrowRC(autoCaller.rc());
5705 targetCaller.add();
5706 AssertComRCThrowRC(targetCaller.rc());
5707 if (FAILED(rc))
5708 throw rc;
5709
5710 /* Sanity checking, must be after lock list creation as it depends on
5711 * valid medium states. The medium objects must be accessible. Only
5712 * do this if immediate locking is requested, otherwise it fails when
5713 * we construct a medium lock list for an already running VM. Snapshot
5714 * deletion uses this to simplify its life. */
5715 if (fLockMedia)
5716 {
5717 {
5718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5719 if (m->state != MediumState_Created)
5720 throw i_setStateError();
5721 }
5722 {
5723 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5724 if (pTarget->m->state != MediumState_Created)
5725 throw pTarget->i_setStateError();
5726 }
5727 }
5728
5729 /* check medium attachment and other sanity conditions */
5730 if (fMergeForward)
5731 {
5732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5733 if (i_getChildren().size() > 1)
5734 {
5735 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5736 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5737 m->strLocationFull.c_str(), i_getChildren().size());
5738 }
5739 /* One backreference is only allowed if the machine ID is not empty
5740 * and it matches the machine the medium is attached to (including
5741 * the snapshot ID if not empty). */
5742 if ( m->backRefs.size() != 0
5743 && ( !aMachineId
5744 || m->backRefs.size() != 1
5745 || aMachineId->isZero()
5746 || *i_getFirstMachineBackrefId() != *aMachineId
5747 || ( (!aSnapshotId || !aSnapshotId->isZero())
5748 && *i_getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
5749 throw setError(VBOX_E_OBJECT_IN_USE,
5750 tr("Medium '%s' is attached to %d virtual machines"),
5751 m->strLocationFull.c_str(), m->backRefs.size());
5752 if (m->type == MediumType_Immutable)
5753 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5754 tr("Medium '%s' is immutable"),
5755 m->strLocationFull.c_str());
5756 if (m->type == MediumType_MultiAttach)
5757 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5758 tr("Medium '%s' is multi-attach"),
5759 m->strLocationFull.c_str());
5760 }
5761 else
5762 {
5763 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5764 if (pTarget->i_getChildren().size() > 1)
5765 {
5766 throw setError(VBOX_E_OBJECT_IN_USE,
5767 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5768 pTarget->m->strLocationFull.c_str(),
5769 pTarget->i_getChildren().size());
5770 }
5771 if (pTarget->m->type == MediumType_Immutable)
5772 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5773 tr("Medium '%s' is immutable"),
5774 pTarget->m->strLocationFull.c_str());
5775 if (pTarget->m->type == MediumType_MultiAttach)
5776 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5777 tr("Medium '%s' is multi-attach"),
5778 pTarget->m->strLocationFull.c_str());
5779 }
5780 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
5781 ComObjPtr<Medium> pLastIntermediate = pLast->i_getParent();
5782 for (pLast = pLastIntermediate;
5783 !pLast.isNull() && pLast != pTarget && pLast != this;
5784 pLast = pLast->i_getParent())
5785 {
5786 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
5787 if (pLast->i_getChildren().size() > 1)
5788 {
5789 throw setError(VBOX_E_OBJECT_IN_USE,
5790 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5791 pLast->m->strLocationFull.c_str(),
5792 pLast->i_getChildren().size());
5793 }
5794 if (pLast->m->backRefs.size() != 0)
5795 throw setError(VBOX_E_OBJECT_IN_USE,
5796 tr("Medium '%s' is attached to %d virtual machines"),
5797 pLast->m->strLocationFull.c_str(),
5798 pLast->m->backRefs.size());
5799
5800 }
5801
5802 /* Update medium states appropriately */
5803 {
5804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5805
5806 if (m->state == MediumState_Created)
5807 {
5808 rc = i_markForDeletion();
5809 if (FAILED(rc))
5810 throw rc;
5811 }
5812 else
5813 {
5814 if (fLockMedia)
5815 throw i_setStateError();
5816 else if ( m->state == MediumState_LockedWrite
5817 || m->state == MediumState_LockedRead)
5818 {
5819 /* Either mark it for deletion in locked state or allow
5820 * others to have done so. */
5821 if (m->preLockState == MediumState_Created)
5822 i_markLockedForDeletion();
5823 else if (m->preLockState != MediumState_Deleting)
5824 throw i_setStateError();
5825 }
5826 else
5827 throw i_setStateError();
5828 }
5829 }
5830
5831 if (fMergeForward)
5832 {
5833 /* we will need parent to reparent target */
5834 pParentForTarget = i_getParent();
5835 }
5836 else
5837 {
5838 /* we will need to reparent children of the source */
5839 aChildrenToReparent = new MediumLockList();
5840 for (MediaList::const_iterator it = i_getChildren().begin();
5841 it != i_getChildren().end();
5842 ++it)
5843 {
5844 pMedium = *it;
5845 aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
5846 }
5847 if (fLockMedia && aChildrenToReparent)
5848 {
5849 targetCaller.release();
5850 autoCaller.release();
5851 treeLock.release();
5852 rc = aChildrenToReparent->Lock();
5853 treeLock.acquire();
5854 autoCaller.add();
5855 AssertComRCThrowRC(autoCaller.rc());
5856 targetCaller.add();
5857 AssertComRCThrowRC(targetCaller.rc());
5858 if (FAILED(rc))
5859 throw rc;
5860 }
5861 }
5862 for (pLast = pLastIntermediate;
5863 !pLast.isNull() && pLast != pTarget && pLast != this;
5864 pLast = pLast->i_getParent())
5865 {
5866 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
5867 if (pLast->m->state == MediumState_Created)
5868 {
5869 rc = pLast->i_markForDeletion();
5870 if (FAILED(rc))
5871 throw rc;
5872 }
5873 else
5874 throw pLast->i_setStateError();
5875 }
5876
5877 /* Tweak the lock list in the backward merge case, as the target
5878 * isn't marked to be locked for writing yet. */
5879 if (!fMergeForward)
5880 {
5881 MediumLockList::Base::iterator lockListBegin =
5882 aMediumLockList->GetBegin();
5883 MediumLockList::Base::iterator lockListEnd =
5884 aMediumLockList->GetEnd();
5885 ++lockListEnd;
5886 for (MediumLockList::Base::iterator it = lockListBegin;
5887 it != lockListEnd;
5888 ++it)
5889 {
5890 MediumLock &mediumLock = *it;
5891 if (mediumLock.GetMedium() == pTarget)
5892 {
5893 HRESULT rc2 = mediumLock.UpdateLock(true);
5894 AssertComRC(rc2);
5895 break;
5896 }
5897 }
5898 }
5899
5900 if (fLockMedia)
5901 {
5902 targetCaller.release();
5903 autoCaller.release();
5904 treeLock.release();
5905 rc = aMediumLockList->Lock();
5906 treeLock.acquire();
5907 autoCaller.add();
5908 AssertComRCThrowRC(autoCaller.rc());
5909 targetCaller.add();
5910 AssertComRCThrowRC(targetCaller.rc());
5911 if (FAILED(rc))
5912 {
5913 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5914 throw setError(rc,
5915 tr("Failed to lock media when merging to '%s'"),
5916 pTarget->i_getLocationFull().c_str());
5917 }
5918 }
5919 }
5920 catch (HRESULT aRC) { rc = aRC; }
5921
5922 if (FAILED(rc))
5923 {
5924 if (aMediumLockList)
5925 {
5926 delete aMediumLockList;
5927 aMediumLockList = NULL;
5928 }
5929 if (aChildrenToReparent)
5930 {
5931 delete aChildrenToReparent;
5932 aChildrenToReparent = NULL;
5933 }
5934 }
5935
5936 return rc;
5937}
5938
5939/**
5940 * Merges this medium to the specified medium which must be either its
5941 * direct ancestor or descendant.
5942 *
5943 * Given this medium is SOURCE and the specified medium is TARGET, we will
5944 * get two variants of the merge operation:
5945 *
5946 * forward merge
5947 * ------------------------->
5948 * [Extra] <- SOURCE <- Intermediate <- TARGET
5949 * Any Del Del LockWr
5950 *
5951 *
5952 * backward merge
5953 * <-------------------------
5954 * TARGET <- Intermediate <- SOURCE <- [Extra]
5955 * LockWr Del Del LockWr
5956 *
5957 * Each diagram shows the involved media on the media chain where
5958 * SOURCE and TARGET belong. Under each medium there is a state value which
5959 * the medium must have at a time of the mergeTo() call.
5960 *
5961 * The media in the square braces may be absent (e.g. when the forward
5962 * operation takes place and SOURCE is the base medium, or when the backward
5963 * merge operation takes place and TARGET is the last child in the chain) but if
5964 * they present they are involved too as shown.
5965 *
5966 * Neither the source medium nor intermediate media may be attached to
5967 * any VM directly or in the snapshot, otherwise this method will assert.
5968 *
5969 * The #i_prepareMergeTo() method must be called prior to this method to place
5970 * all involved to necessary states and perform other consistency checks.
5971 *
5972 * If @a aWait is @c true then this method will perform the operation on the
5973 * calling thread and will not return to the caller until the operation is
5974 * completed. When this method succeeds, all intermediate medium objects in
5975 * the chain will be uninitialized, the state of the target medium (and all
5976 * involved extra media) will be restored. @a aMediumLockList will not be
5977 * deleted, whether the operation is successful or not. The caller has to do
5978 * this if appropriate. Note that this (source) medium is not uninitialized
5979 * because of possible AutoCaller instances held by the caller of this method
5980 * on the current thread. It's therefore the responsibility of the caller to
5981 * call Medium::uninit() after releasing all callers.
5982 *
5983 * If @a aWait is @c false then this method will create a thread to perform the
5984 * operation asynchronously and will return immediately. If the operation
5985 * succeeds, the thread will uninitialize the source medium object and all
5986 * intermediate medium objects in the chain, reset the state of the target
5987 * medium (and all involved extra media) and delete @a aMediumLockList.
5988 * If the operation fails, the thread will only reset the states of all
5989 * involved media and delete @a aMediumLockList.
5990 *
5991 * When this method fails (regardless of the @a aWait mode), it is a caller's
5992 * responsibility to undo state changes and delete @a aMediumLockList using
5993 * #i_cancelMergeTo().
5994 *
5995 * If @a aProgress is not NULL but the object it points to is @c null then a new
5996 * progress object will be created and assigned to @a *aProgress on success,
5997 * otherwise the existing progress object is used. If Progress is NULL, then no
5998 * progress object is created/used at all. Note that @a aProgress cannot be
5999 * NULL when @a aWait is @c false (this method will assert in this case).
6000 *
6001 * @param pTarget Target medium.
6002 * @param fMergeForward Merge direction.
6003 * @param pParentForTarget New parent for target medium after merge.
6004 * @param aChildrenToReparent List of children of the source which will have
6005 * to be reparented to the target after merge.
6006 * @param aMediumLockList Medium locking information.
6007 * @param aProgress Where to find/store a Progress object to track operation
6008 * completion.
6009 * @param aWait @c true if this method should block instead of creating
6010 * an asynchronous thread.
6011 * @param aNotify Notify about mediums which metadatа are changed
6012 * during execution of the function.
6013 *
6014 * @note Locks the tree lock for writing. Locks the media from the chain
6015 * for writing.
6016 */
6017HRESULT Medium::i_mergeTo(const ComObjPtr<Medium> &pTarget,
6018 bool fMergeForward,
6019 const ComObjPtr<Medium> &pParentForTarget,
6020 MediumLockList *aChildrenToReparent,
6021 MediumLockList *aMediumLockList,
6022 ComObjPtr<Progress> *aProgress,
6023 bool aWait, bool aNotify)
6024{
6025 AssertReturn(pTarget != NULL, E_FAIL);
6026 AssertReturn(pTarget != this, E_FAIL);
6027 AssertReturn(aMediumLockList != NULL, E_FAIL);
6028 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6029
6030 AutoCaller autoCaller(this);
6031 AssertComRCReturnRC(autoCaller.rc());
6032
6033 AutoCaller targetCaller(pTarget);
6034 AssertComRCReturnRC(targetCaller.rc());
6035
6036 HRESULT rc = S_OK;
6037 ComObjPtr<Progress> pProgress;
6038 Medium::Task *pTask = NULL;
6039
6040 try
6041 {
6042 if (aProgress != NULL)
6043 {
6044 /* use the existing progress object... */
6045 pProgress = *aProgress;
6046
6047 /* ...but create a new one if it is null */
6048 if (pProgress.isNull())
6049 {
6050 Utf8Str tgtName;
6051 {
6052 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6053 tgtName = pTarget->i_getName();
6054 }
6055
6056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6057
6058 pProgress.createObject();
6059 rc = pProgress->init(m->pVirtualBox,
6060 static_cast<IMedium*>(this),
6061 BstrFmt(tr("Merging medium '%s' to '%s'"),
6062 i_getName().c_str(),
6063 tgtName.c_str()).raw(),
6064 TRUE, /* aCancelable */
6065 2, /* Number of opearations */
6066 BstrFmt(tr("Resizing medium '%s' before merge"),
6067 tgtName.c_str()).raw()
6068 );
6069 if (FAILED(rc))
6070 throw rc;
6071 }
6072 }
6073
6074 /* setup task object to carry out the operation sync/async */
6075 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
6076 pParentForTarget, aChildrenToReparent,
6077 pProgress, aMediumLockList,
6078 aWait /* fKeepMediumLockList */,
6079 aNotify);
6080 rc = pTask->rc();
6081 AssertComRC(rc);
6082 if (FAILED(rc))
6083 throw rc;
6084 }
6085 catch (HRESULT aRC) { rc = aRC; }
6086
6087 if (SUCCEEDED(rc))
6088 {
6089 if (aWait)
6090 {
6091 rc = pTask->runNow();
6092 delete pTask;
6093 }
6094 else
6095 rc = pTask->createThread();
6096 pTask = NULL;
6097 if (SUCCEEDED(rc) && aProgress != NULL)
6098 *aProgress = pProgress;
6099 }
6100 else if (pTask != NULL)
6101 delete pTask;
6102
6103 return rc;
6104}
6105
6106/**
6107 * Undoes what #i_prepareMergeTo() did. Must be called if #mergeTo() is not
6108 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
6109 * the medium objects in @a aChildrenToReparent.
6110 *
6111 * @param aChildrenToReparent List of children of the source which will have
6112 * to be reparented to the target after merge.
6113 * @param aMediumLockList Medium locking information.
6114 *
6115 * @note Locks the tree lock for writing. Locks the media from the chain
6116 * for writing.
6117 */
6118void Medium::i_cancelMergeTo(MediumLockList *aChildrenToReparent,
6119 MediumLockList *aMediumLockList)
6120{
6121 AutoCaller autoCaller(this);
6122 AssertComRCReturnVoid(autoCaller.rc());
6123
6124 AssertReturnVoid(aMediumLockList != NULL);
6125
6126 /* Revert media marked for deletion to previous state. */
6127 HRESULT rc;
6128 MediumLockList::Base::const_iterator mediumListBegin =
6129 aMediumLockList->GetBegin();
6130 MediumLockList::Base::const_iterator mediumListEnd =
6131 aMediumLockList->GetEnd();
6132 for (MediumLockList::Base::const_iterator it = mediumListBegin;
6133 it != mediumListEnd;
6134 ++it)
6135 {
6136 const MediumLock &mediumLock = *it;
6137 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6138 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6139
6140 if (pMedium->m->state == MediumState_Deleting)
6141 {
6142 rc = pMedium->i_unmarkForDeletion();
6143 AssertComRC(rc);
6144 }
6145 else if ( ( pMedium->m->state == MediumState_LockedWrite
6146 || pMedium->m->state == MediumState_LockedRead)
6147 && pMedium->m->preLockState == MediumState_Deleting)
6148 {
6149 rc = pMedium->i_unmarkLockedForDeletion();
6150 AssertComRC(rc);
6151 }
6152 }
6153
6154 /* the destructor will do the work */
6155 delete aMediumLockList;
6156
6157 /* unlock the children which had to be reparented, the destructor will do
6158 * the work */
6159 if (aChildrenToReparent)
6160 delete aChildrenToReparent;
6161}
6162
6163/**
6164 * Resizes the media.
6165 *
6166 * If @a aWait is @c true then this method will perform the operation on the
6167 * calling thread and will not return to the caller until the operation is
6168 * completed. When this method succeeds, the state of the target medium (and all
6169 * involved extra media) will be restored. @a aMediumLockList will not be
6170 * deleted, whether the operation is successful or not. The caller has to do
6171 * this if appropriate.
6172 *
6173 * If @a aWait is @c false then this method will create a thread to perform the
6174 * operation asynchronously and will return immediately. The thread will reset
6175 * the state of the target medium (and all involved extra media) and delete
6176 * @a aMediumLockList.
6177 *
6178 * When this method fails (regardless of the @a aWait mode), it is a caller's
6179 * responsibility to undo state changes and delete @a aMediumLockList.
6180 *
6181 * If @a aProgress is not NULL but the object it points to is @c null then a new
6182 * progress object will be created and assigned to @a *aProgress on success,
6183 * otherwise the existing progress object is used. If Progress is NULL, then no
6184 * progress object is created/used at all. Note that @a aProgress cannot be
6185 * NULL when @a aWait is @c false (this method will assert in this case).
6186 *
6187 * @param aLogicalSize New nominal capacity of the medium in bytes.
6188 * @param aMediumLockList Medium locking information.
6189 * @param aProgress Where to find/store a Progress object to track operation
6190 * completion.
6191 * @param aWait @c true if this method should block instead of creating
6192 * an asynchronous thread.
6193 * @param aNotify Notify about mediums which metadatа are changed
6194 * during execution of the function.
6195 *
6196 * @note Locks the media from the chain for writing.
6197 */
6198
6199HRESULT Medium::i_resize(uint64_t aLogicalSize,
6200 MediumLockList *aMediumLockList,
6201 ComObjPtr<Progress> *aProgress,
6202 bool aWait,
6203 bool aNotify)
6204{
6205 AssertReturn(aMediumLockList != NULL, E_FAIL);
6206 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6207
6208 AutoCaller autoCaller(this);
6209 AssertComRCReturnRC(autoCaller.rc());
6210
6211 HRESULT rc = S_OK;
6212 ComObjPtr<Progress> pProgress;
6213 Medium::Task *pTask = NULL;
6214
6215 try
6216 {
6217 if (aProgress != NULL)
6218 {
6219 /* use the existing progress object... */
6220 pProgress = *aProgress;
6221
6222 /* ...but create a new one if it is null */
6223 if (pProgress.isNull())
6224 {
6225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6226
6227 pProgress.createObject();
6228 rc = pProgress->init(m->pVirtualBox,
6229 static_cast <IMedium *>(this),
6230 BstrFmt(tr("Resizing medium '%s'"), m->strLocationFull.c_str()).raw(),
6231 TRUE /* aCancelable */);
6232 if (FAILED(rc))
6233 throw rc;
6234 }
6235 }
6236
6237 /* setup task object to carry out the operation asynchronously */
6238 pTask = new Medium::ResizeTask(this,
6239 aLogicalSize,
6240 pProgress,
6241 aMediumLockList,
6242 aWait /* fKeepMediumLockList */,
6243 aNotify);
6244 rc = pTask->rc();
6245 AssertComRC(rc);
6246 if (FAILED(rc))
6247 throw rc;
6248 }
6249 catch (HRESULT aRC) { rc = aRC; }
6250
6251 if (SUCCEEDED(rc))
6252 {
6253 if (aWait)
6254 {
6255 rc = pTask->runNow();
6256 delete pTask;
6257 }
6258 else
6259 rc = pTask->createThread();
6260 pTask = NULL;
6261 if (SUCCEEDED(rc) && aProgress != NULL)
6262 *aProgress = pProgress;
6263 }
6264 else if (pTask != NULL)
6265 delete pTask;
6266
6267 return rc;
6268}
6269
6270/**
6271 * Fix the parent UUID of all children to point to this medium as their
6272 * parent.
6273 */
6274HRESULT Medium::i_fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
6275{
6276 /** @todo r=klaus The code below needs to be double checked with regard
6277 * to lock order violations, it probably causes lock order issues related
6278 * to the AutoCaller usage. Likewise the code using this method seems
6279 * problematic. */
6280 Assert(!isWriteLockOnCurrentThread());
6281 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6282 MediumLockList mediumLockList;
6283 HRESULT rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6284 NULL /* pToLockWrite */,
6285 false /* fMediumLockWriteAll */,
6286 this,
6287 mediumLockList);
6288 AssertComRCReturnRC(rc);
6289
6290 try
6291 {
6292 PVDISK hdd;
6293 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6294 ComAssertRCThrow(vrc, E_FAIL);
6295
6296 try
6297 {
6298 MediumLockList::Base::iterator lockListBegin =
6299 mediumLockList.GetBegin();
6300 MediumLockList::Base::iterator lockListEnd =
6301 mediumLockList.GetEnd();
6302 for (MediumLockList::Base::iterator it = lockListBegin;
6303 it != lockListEnd;
6304 ++it)
6305 {
6306 MediumLock &mediumLock = *it;
6307 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6308 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6309
6310 // open the medium
6311 vrc = VDOpen(hdd,
6312 pMedium->m->strFormat.c_str(),
6313 pMedium->m->strLocationFull.c_str(),
6314 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
6315 pMedium->m->vdImageIfaces);
6316 if (RT_FAILURE(vrc))
6317 throw vrc;
6318 }
6319
6320 MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
6321 MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
6322 for (MediumLockList::Base::iterator it = childrenBegin;
6323 it != childrenEnd;
6324 ++it)
6325 {
6326 Medium *pMedium = it->GetMedium();
6327 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
6328 vrc = VDOpen(hdd,
6329 pMedium->m->strFormat.c_str(),
6330 pMedium->m->strLocationFull.c_str(),
6331 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
6332 pMedium->m->vdImageIfaces);
6333 if (RT_FAILURE(vrc))
6334 throw vrc;
6335
6336 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
6337 if (RT_FAILURE(vrc))
6338 throw vrc;
6339
6340 vrc = VDClose(hdd, false /* fDelete */);
6341 if (RT_FAILURE(vrc))
6342 throw vrc;
6343 }
6344 }
6345 catch (HRESULT aRC) { rc = aRC; }
6346 catch (int aVRC)
6347 {
6348 rc = setErrorBoth(E_FAIL, aVRC,
6349 tr("Could not update medium UUID references to parent '%s' (%s)"),
6350 m->strLocationFull.c_str(),
6351 i_vdError(aVRC).c_str());
6352 }
6353
6354 VDDestroy(hdd);
6355 }
6356 catch (HRESULT aRC) { rc = aRC; }
6357
6358 return rc;
6359}
6360
6361/**
6362 *
6363 * @note Similar code exists in i_taskExportHandler.
6364 */
6365HRESULT Medium::i_addRawToFss(const char *aFilename, SecretKeyStore *pKeyStore, RTVFSFSSTREAM hVfsFssDst,
6366 const ComObjPtr<Progress> &aProgress, bool fSparse)
6367{
6368 AutoCaller autoCaller(this);
6369 HRESULT hrc = autoCaller.rc();
6370 if (SUCCEEDED(hrc))
6371 {
6372 /*
6373 * Get a readonly hdd for this medium.
6374 */
6375 MediumCryptoFilterSettings CryptoSettingsRead;
6376 MediumLockList SourceMediumLockList;
6377 PVDISK pHdd;
6378 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pHdd, &SourceMediumLockList, &CryptoSettingsRead);
6379 if (SUCCEEDED(hrc))
6380 {
6381 /*
6382 * Create a VFS file interface to the HDD and attach a progress wrapper
6383 * that monitors the progress reading of the raw image. The image will
6384 * be read twice if hVfsFssDst does sparse processing.
6385 */
6386 RTVFSFILE hVfsFileDisk = NIL_RTVFSFILE;
6387 int vrc = VDCreateVfsFileFromDisk(pHdd, 0 /*fFlags*/, &hVfsFileDisk);
6388 if (RT_SUCCESS(vrc))
6389 {
6390 RTVFSFILE hVfsFileProgress = NIL_RTVFSFILE;
6391 vrc = RTVfsCreateProgressForFile(hVfsFileDisk, aProgress->i_iprtProgressCallback, &*aProgress,
6392 RTVFSPROGRESS_F_CANCELABLE | RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ,
6393 VDGetSize(pHdd, VD_LAST_IMAGE) * (fSparse ? 2 : 1) /*cbExpectedRead*/,
6394 0 /*cbExpectedWritten*/, &hVfsFileProgress);
6395 RTVfsFileRelease(hVfsFileDisk);
6396 if (RT_SUCCESS(vrc))
6397 {
6398 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFileProgress);
6399 RTVfsFileRelease(hVfsFileProgress);
6400
6401 vrc = RTVfsFsStrmAdd(hVfsFssDst, aFilename, hVfsObj, 0 /*fFlags*/);
6402 RTVfsObjRelease(hVfsObj);
6403 if (RT_FAILURE(vrc))
6404 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to add '%s' to output (%Rrc)"), aFilename, vrc);
6405 }
6406 else
6407 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
6408 tr("RTVfsCreateProgressForFile failed when processing '%s' (%Rrc)"), aFilename, vrc);
6409 }
6410 else
6411 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("VDCreateVfsFileFromDisk failed for '%s' (%Rrc)"), aFilename, vrc);
6412 VDDestroy(pHdd);
6413 }
6414 }
6415 return hrc;
6416}
6417
6418/**
6419 * Used by IAppliance to export disk images.
6420 *
6421 * @param aFilename Filename to create (UTF8).
6422 * @param aFormat Medium format for creating @a aFilename.
6423 * @param aVariant Which exact image format variant to use for the
6424 * destination image.
6425 * @param pKeyStore The optional key store for decrypting the data for
6426 * encrypted media during the export.
6427 * @param hVfsIosDst The destination I/O stream object.
6428 * @param aProgress Progress object to use.
6429 * @return
6430 *
6431 * @note The source format is defined by the Medium instance.
6432 */
6433HRESULT Medium::i_exportFile(const char *aFilename,
6434 const ComObjPtr<MediumFormat> &aFormat,
6435 MediumVariant_T aVariant,
6436 SecretKeyStore *pKeyStore,
6437 RTVFSIOSTREAM hVfsIosDst,
6438 const ComObjPtr<Progress> &aProgress)
6439{
6440 AssertPtrReturn(aFilename, E_INVALIDARG);
6441 AssertReturn(aFormat.isNotNull(), E_INVALIDARG);
6442 AssertReturn(aProgress.isNotNull(), E_INVALIDARG);
6443
6444 AutoCaller autoCaller(this);
6445 HRESULT hrc = autoCaller.rc();
6446 if (SUCCEEDED(hrc))
6447 {
6448 /*
6449 * Setup VD interfaces.
6450 */
6451 PVDINTERFACE pVDImageIfaces = m->vdImageIfaces;
6452 PVDINTERFACEIO pVfsIoIf;
6453 int vrc = VDIfCreateFromVfsStream(hVfsIosDst, RTFILE_O_WRITE, &pVfsIoIf);
6454 if (RT_SUCCESS(vrc))
6455 {
6456 vrc = VDInterfaceAdd(&pVfsIoIf->Core, "Medium::ExportTaskVfsIos", VDINTERFACETYPE_IO,
6457 pVfsIoIf, sizeof(VDINTERFACEIO), &pVDImageIfaces);
6458 if (RT_SUCCESS(vrc))
6459 {
6460 /*
6461 * Get a readonly hdd for this medium (source).
6462 */
6463 MediumCryptoFilterSettings CryptoSettingsRead;
6464 MediumLockList SourceMediumLockList;
6465 PVDISK pSrcHdd;
6466 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pSrcHdd, &SourceMediumLockList, &CryptoSettingsRead);
6467 if (SUCCEEDED(hrc))
6468 {
6469 /*
6470 * Create the target medium.
6471 */
6472 Utf8Str strDstFormat(aFormat->i_getId());
6473
6474 /* ensure the target directory exists */
6475 uint64_t fDstCapabilities = aFormat->i_getCapabilities();
6476 if (fDstCapabilities & MediumFormatCapabilities_File)
6477 {
6478 Utf8Str strDstLocation(aFilename);
6479 hrc = VirtualBox::i_ensureFilePathExists(strDstLocation.c_str(),
6480 !(aVariant & MediumVariant_NoCreateDir) /* fCreate */);
6481 }
6482 if (SUCCEEDED(hrc))
6483 {
6484 PVDISK pDstHdd;
6485 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDstHdd);
6486 if (RT_SUCCESS(vrc))
6487 {
6488 /*
6489 * Create an interface for getting progress callbacks.
6490 */
6491 VDINTERFACEPROGRESS ProgressIf = VDINTERFACEPROGRESS_INITALIZER(aProgress->i_vdProgressCallback);
6492 PVDINTERFACE pProgress = NULL;
6493 vrc = VDInterfaceAdd(&ProgressIf.Core, "export-progress", VDINTERFACETYPE_PROGRESS,
6494 &*aProgress, sizeof(ProgressIf), &pProgress);
6495 AssertRC(vrc);
6496
6497 /*
6498 * Do the exporting.
6499 */
6500 vrc = VDCopy(pSrcHdd,
6501 VD_LAST_IMAGE,
6502 pDstHdd,
6503 strDstFormat.c_str(),
6504 aFilename,
6505 false /* fMoveByRename */,
6506 0 /* cbSize */,
6507 aVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
6508 NULL /* pDstUuid */,
6509 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
6510 pProgress,
6511 pVDImageIfaces,
6512 NULL);
6513 if (RT_SUCCESS(vrc))
6514 hrc = S_OK;
6515 else
6516 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Could not create the exported medium '%s'%s"),
6517 aFilename, i_vdError(vrc).c_str());
6518 VDDestroy(pDstHdd);
6519 }
6520 else
6521 hrc = setErrorVrc(vrc);
6522 }
6523 }
6524 VDDestroy(pSrcHdd);
6525 }
6526 else
6527 hrc = setErrorVrc(vrc, "VDInterfaceAdd -> %Rrc", vrc);
6528 VDIfDestroyFromVfsStream(pVfsIoIf);
6529 }
6530 else
6531 hrc = setErrorVrc(vrc, "VDIfCreateFromVfsStream -> %Rrc", vrc);
6532 }
6533 return hrc;
6534}
6535
6536/**
6537 * Used by IAppliance to import disk images.
6538 *
6539 * @param aFilename Filename to read (UTF8).
6540 * @param aFormat Medium format for reading @a aFilename.
6541 * @param aVariant Which exact image format variant to use
6542 * for the destination image.
6543 * @param aVfsIosSrc Handle to the source I/O stream.
6544 * @param aParent Parent medium. May be NULL.
6545 * @param aProgress Progress object to use.
6546 * @param aNotify Notify about mediums which metadatа are changed
6547 * during execution of the function.
6548 * @return
6549 * @note The destination format is defined by the Medium instance.
6550 *
6551 * @todo The only consumer of this method (Appliance::i_importOneDiskImage) is
6552 * already on a worker thread, so perhaps consider bypassing the thread
6553 * here and run in the task synchronously? VBoxSVC has enough threads as
6554 * it is...
6555 */
6556HRESULT Medium::i_importFile(const char *aFilename,
6557 const ComObjPtr<MediumFormat> &aFormat,
6558 MediumVariant_T aVariant,
6559 RTVFSIOSTREAM aVfsIosSrc,
6560 const ComObjPtr<Medium> &aParent,
6561 const ComObjPtr<Progress> &aProgress,
6562 bool aNotify)
6563{
6564 /** @todo r=klaus The code below needs to be double checked with regard
6565 * to lock order violations, it probably causes lock order issues related
6566 * to the AutoCaller usage. */
6567 AssertPtrReturn(aFilename, E_INVALIDARG);
6568 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
6569 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
6570
6571 AutoCaller autoCaller(this);
6572 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6573
6574 HRESULT rc = S_OK;
6575 Medium::Task *pTask = NULL;
6576
6577 try
6578 {
6579 // locking: we need the tree lock first because we access parent pointers
6580 // and we need to write-lock the media involved
6581 uint32_t cHandles = 2;
6582 LockHandle* pHandles[3] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
6583 this->lockHandle() };
6584 /* Only add parent to the lock if it is not null */
6585 if (!aParent.isNull())
6586 pHandles[cHandles++] = aParent->lockHandle();
6587 AutoWriteLock alock(cHandles,
6588 pHandles
6589 COMMA_LOCKVAL_SRC_POS);
6590
6591 if ( m->state != MediumState_NotCreated
6592 && m->state != MediumState_Created)
6593 throw i_setStateError();
6594
6595 /* Build the target lock list. */
6596 MediumLockList *pTargetMediumLockList(new MediumLockList());
6597 alock.release();
6598 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6599 this /* pToLockWrite */,
6600 false /* fMediumLockWriteAll */,
6601 aParent,
6602 *pTargetMediumLockList);
6603 alock.acquire();
6604 if (FAILED(rc))
6605 {
6606 delete pTargetMediumLockList;
6607 throw rc;
6608 }
6609
6610 alock.release();
6611 rc = pTargetMediumLockList->Lock();
6612 alock.acquire();
6613 if (FAILED(rc))
6614 {
6615 delete pTargetMediumLockList;
6616 throw setError(rc,
6617 tr("Failed to lock target media '%s'"),
6618 i_getLocationFull().c_str());
6619 }
6620
6621 /* setup task object to carry out the operation asynchronously */
6622 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat, aVariant,
6623 aVfsIosSrc, aParent, pTargetMediumLockList, false, aNotify);
6624 rc = pTask->rc();
6625 AssertComRC(rc);
6626 if (FAILED(rc))
6627 throw rc;
6628
6629 if (m->state == MediumState_NotCreated)
6630 m->state = MediumState_Creating;
6631 }
6632 catch (HRESULT aRC) { rc = aRC; }
6633
6634 if (SUCCEEDED(rc))
6635 {
6636 rc = pTask->createThread();
6637 pTask = NULL;
6638 }
6639 else if (pTask != NULL)
6640 delete pTask;
6641
6642 return rc;
6643}
6644
6645/**
6646 * Internal version of the public CloneTo API which allows to enable certain
6647 * optimizations to improve speed during VM cloning.
6648 *
6649 * @param aTarget Target medium
6650 * @param aVariant Which exact image format variant to use
6651 * for the destination image.
6652 * @param aParent Parent medium. May be NULL.
6653 * @param aProgress Progress object to use.
6654 * @param idxSrcImageSame The last image in the source chain which has the
6655 * same content as the given image in the destination
6656 * chain. Use UINT32_MAX to disable this optimization.
6657 * @param idxDstImageSame The last image in the destination chain which has the
6658 * same content as the given image in the source chain.
6659 * Use UINT32_MAX to disable this optimization.
6660 * @param aNotify Notify about mediums which metadatа are changed
6661 * during execution of the function.
6662 * @return
6663 */
6664HRESULT Medium::i_cloneToEx(const ComObjPtr<Medium> &aTarget, MediumVariant_T aVariant,
6665 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
6666 uint32_t idxSrcImageSame, uint32_t idxDstImageSame, bool aNotify)
6667{
6668 /** @todo r=klaus The code below needs to be double checked with regard
6669 * to lock order violations, it probably causes lock order issues related
6670 * to the AutoCaller usage. */
6671 CheckComArgNotNull(aTarget);
6672 CheckComArgOutPointerValid(aProgress);
6673 ComAssertRet(aTarget != this, E_INVALIDARG);
6674
6675 AutoCaller autoCaller(this);
6676 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6677
6678 HRESULT rc = S_OK;
6679 ComObjPtr<Progress> pProgress;
6680 Medium::Task *pTask = NULL;
6681
6682 try
6683 {
6684 // locking: we need the tree lock first because we access parent pointers
6685 // and we need to write-lock the media involved
6686 uint32_t cHandles = 3;
6687 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
6688 this->lockHandle(),
6689 aTarget->lockHandle() };
6690 /* Only add parent to the lock if it is not null */
6691 if (!aParent.isNull())
6692 pHandles[cHandles++] = aParent->lockHandle();
6693 AutoWriteLock alock(cHandles,
6694 pHandles
6695 COMMA_LOCKVAL_SRC_POS);
6696
6697 if ( aTarget->m->state != MediumState_NotCreated
6698 && aTarget->m->state != MediumState_Created)
6699 throw aTarget->i_setStateError();
6700
6701 /* Build the source lock list. */
6702 MediumLockList *pSourceMediumLockList(new MediumLockList());
6703 alock.release();
6704 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6705 NULL /* pToLockWrite */,
6706 false /* fMediumLockWriteAll */,
6707 NULL,
6708 *pSourceMediumLockList);
6709 alock.acquire();
6710 if (FAILED(rc))
6711 {
6712 delete pSourceMediumLockList;
6713 throw rc;
6714 }
6715
6716 /* Build the target lock list (including the to-be parent chain). */
6717 MediumLockList *pTargetMediumLockList(new MediumLockList());
6718 alock.release();
6719 rc = aTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
6720 aTarget /* pToLockWrite */,
6721 false /* fMediumLockWriteAll */,
6722 aParent,
6723 *pTargetMediumLockList);
6724 alock.acquire();
6725 if (FAILED(rc))
6726 {
6727 delete pSourceMediumLockList;
6728 delete pTargetMediumLockList;
6729 throw rc;
6730 }
6731
6732 alock.release();
6733 rc = pSourceMediumLockList->Lock();
6734 alock.acquire();
6735 if (FAILED(rc))
6736 {
6737 delete pSourceMediumLockList;
6738 delete pTargetMediumLockList;
6739 throw setError(rc,
6740 tr("Failed to lock source media '%s'"),
6741 i_getLocationFull().c_str());
6742 }
6743 alock.release();
6744 rc = pTargetMediumLockList->Lock();
6745 alock.acquire();
6746 if (FAILED(rc))
6747 {
6748 delete pSourceMediumLockList;
6749 delete pTargetMediumLockList;
6750 throw setError(rc,
6751 tr("Failed to lock target media '%s'"),
6752 aTarget->i_getLocationFull().c_str());
6753 }
6754
6755 pProgress.createObject();
6756 rc = pProgress->init(m->pVirtualBox,
6757 static_cast <IMedium *>(this),
6758 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
6759 TRUE /* aCancelable */);
6760 if (FAILED(rc))
6761 {
6762 delete pSourceMediumLockList;
6763 delete pTargetMediumLockList;
6764 throw rc;
6765 }
6766
6767 /* setup task object to carry out the operation asynchronously */
6768 pTask = new Medium::CloneTask(this, pProgress, aTarget, aVariant,
6769 aParent, idxSrcImageSame,
6770 idxDstImageSame, pSourceMediumLockList,
6771 pTargetMediumLockList, false, false, aNotify);
6772 rc = pTask->rc();
6773 AssertComRC(rc);
6774 if (FAILED(rc))
6775 throw rc;
6776
6777 if (aTarget->m->state == MediumState_NotCreated)
6778 aTarget->m->state = MediumState_Creating;
6779 }
6780 catch (HRESULT aRC) { rc = aRC; }
6781
6782 if (SUCCEEDED(rc))
6783 {
6784 rc = pTask->createThread();
6785 pTask = NULL;
6786 if (SUCCEEDED(rc))
6787 pProgress.queryInterfaceTo(aProgress);
6788 }
6789 else if (pTask != NULL)
6790 delete pTask;
6791
6792 return rc;
6793}
6794
6795/**
6796 * Returns the key identifier for this medium if encryption is configured.
6797 *
6798 * @returns Key identifier or empty string if no encryption is configured.
6799 */
6800const Utf8Str& Medium::i_getKeyId()
6801{
6802 ComObjPtr<Medium> pBase = i_getBase();
6803
6804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6805
6806 settings::StringsMap::const_iterator it = pBase->m->mapProperties.find("CRYPT/KeyId");
6807 if (it == pBase->m->mapProperties.end())
6808 return Utf8Str::Empty;
6809
6810 return it->second;
6811}
6812
6813
6814/**
6815 * Returns all filter related properties.
6816 *
6817 * @returns COM status code.
6818 * @param aReturnNames Where to store the properties names on success.
6819 * @param aReturnValues Where to store the properties values on success.
6820 */
6821HRESULT Medium::i_getFilterProperties(std::vector<com::Utf8Str> &aReturnNames,
6822 std::vector<com::Utf8Str> &aReturnValues)
6823{
6824 std::vector<com::Utf8Str> aPropNames;
6825 std::vector<com::Utf8Str> aPropValues;
6826 HRESULT hrc = getProperties(Utf8Str(""), aPropNames, aPropValues);
6827
6828 if (SUCCEEDED(hrc))
6829 {
6830 unsigned cReturnSize = 0;
6831 aReturnNames.resize(0);
6832 aReturnValues.resize(0);
6833 for (unsigned idx = 0; idx < aPropNames.size(); idx++)
6834 {
6835 if (i_isPropertyForFilter(aPropNames[idx]))
6836 {
6837 aReturnNames.resize(cReturnSize + 1);
6838 aReturnValues.resize(cReturnSize + 1);
6839 aReturnNames[cReturnSize] = aPropNames[idx];
6840 aReturnValues[cReturnSize] = aPropValues[idx];
6841 cReturnSize++;
6842 }
6843 }
6844 }
6845
6846 return hrc;
6847}
6848
6849/**
6850 * Preparation to move this medium to a new location
6851 *
6852 * @param aLocation Location of the storage unit. If the location is a FS-path,
6853 * then it can be relative to the VirtualBox home directory.
6854 *
6855 * @note Must be called from under this object's write lock.
6856 */
6857HRESULT Medium::i_preparationForMoving(const Utf8Str &aLocation)
6858{
6859 HRESULT rc = E_FAIL;
6860
6861 if (i_getLocationFull() != aLocation)
6862 {
6863 m->strNewLocationFull = aLocation;
6864 m->fMoveThisMedium = true;
6865 rc = S_OK;
6866 }
6867
6868 return rc;
6869}
6870
6871/**
6872 * Checking whether current operation "moving" or not
6873 */
6874bool Medium::i_isMoveOperation(const ComObjPtr<Medium> &aTarget) const
6875{
6876 RT_NOREF(aTarget);
6877 return (m->fMoveThisMedium == true) ? true:false; /** @todo r=bird: this is not an obfuscation contest! */
6878}
6879
6880bool Medium::i_resetMoveOperationData()
6881{
6882 m->strNewLocationFull.setNull();
6883 m->fMoveThisMedium = false;
6884 return true;
6885}
6886
6887Utf8Str Medium::i_getNewLocationForMoving() const
6888{
6889 if (m->fMoveThisMedium == true)
6890 return m->strNewLocationFull;
6891 else
6892 return Utf8Str();
6893}
6894////////////////////////////////////////////////////////////////////////////////
6895//
6896// Private methods
6897//
6898////////////////////////////////////////////////////////////////////////////////
6899
6900/**
6901 * Queries information from the medium.
6902 *
6903 * As a result of this call, the accessibility state and data members such as
6904 * size and description will be updated with the current information.
6905 *
6906 * @note This method may block during a system I/O call that checks storage
6907 * accessibility.
6908 *
6909 * @note Caller MUST NOT hold the media tree or medium lock.
6910 *
6911 * @note Locks m->pParent for reading. Locks this object for writing.
6912 *
6913 * @param fSetImageId Whether to reset the UUID contained in the image file
6914 * to the UUID in the medium instance data (see SetIDs())
6915 * @param fSetParentId Whether to reset the parent UUID contained in the image
6916 * file to the parent UUID in the medium instance data (see
6917 * SetIDs())
6918 * @param autoCaller
6919 * @return
6920 */
6921HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
6922{
6923 Assert(!isWriteLockOnCurrentThread());
6924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6925
6926 if ( ( m->state != MediumState_Created
6927 && m->state != MediumState_Inaccessible
6928 && m->state != MediumState_LockedRead)
6929 || m->fClosing)
6930 return E_FAIL;
6931
6932 HRESULT rc = S_OK;
6933
6934 int vrc = VINF_SUCCESS;
6935
6936 /* check if a blocking i_queryInfo() call is in progress on some other thread,
6937 * and wait for it to finish if so instead of querying data ourselves */
6938 if (m->queryInfoRunning)
6939 {
6940 Assert( m->state == MediumState_LockedRead
6941 || m->state == MediumState_LockedWrite);
6942
6943 while (m->queryInfoRunning)
6944 {
6945 alock.release();
6946 /* must not hold the object lock now */
6947 Assert(!isWriteLockOnCurrentThread());
6948 {
6949 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
6950 }
6951 alock.acquire();
6952 }
6953
6954 return S_OK;
6955 }
6956
6957 bool success = false;
6958 Utf8Str lastAccessError;
6959
6960 /* are we dealing with a new medium constructed using the existing
6961 * location? */
6962 bool isImport = m->id.isZero();
6963 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
6964
6965 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
6966 * media because that would prevent necessary modifications
6967 * when opening media of some third-party formats for the first
6968 * time in VirtualBox (such as VMDK for which VDOpen() needs to
6969 * generate an UUID if it is missing) */
6970 if ( m->hddOpenMode == OpenReadOnly
6971 || m->type == MediumType_Readonly
6972 || (!isImport && !fSetImageId && !fSetParentId)
6973 )
6974 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
6975
6976 /* Open shareable medium with the appropriate flags */
6977 if (m->type == MediumType_Shareable)
6978 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6979
6980 /* Lock the medium, which makes the behavior much more consistent, must be
6981 * done before dropping the object lock and setting queryInfoRunning. */
6982 ComPtr<IToken> pToken;
6983 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
6984 rc = LockRead(pToken.asOutParam());
6985 else
6986 rc = LockWrite(pToken.asOutParam());
6987 if (FAILED(rc)) return rc;
6988
6989 /* Copies of the input state fields which are not read-only,
6990 * as we're dropping the lock. CAUTION: be extremely careful what
6991 * you do with the contents of this medium object, as you will
6992 * create races if there are concurrent changes. */
6993 Utf8Str format(m->strFormat);
6994 Utf8Str location(m->strLocationFull);
6995 ComObjPtr<MediumFormat> formatObj = m->formatObj;
6996
6997 /* "Output" values which can't be set because the lock isn't held
6998 * at the time the values are determined. */
6999 Guid mediumId = m->id;
7000 uint64_t mediumSize = 0;
7001 uint64_t mediumLogicalSize = 0;
7002
7003 /* Flag whether a base image has a non-zero parent UUID and thus
7004 * need repairing after it was closed again. */
7005 bool fRepairImageZeroParentUuid = false;
7006
7007 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
7008
7009 /* must be set before leaving the object lock the first time */
7010 m->queryInfoRunning = true;
7011
7012 /* must leave object lock now, because a lock from a higher lock class
7013 * is needed and also a lengthy operation is coming */
7014 alock.release();
7015 autoCaller.release();
7016
7017 /* Note that taking the queryInfoSem after leaving the object lock above
7018 * can lead to short spinning of the loops waiting for i_queryInfo() to
7019 * complete. This is unavoidable since the other order causes a lock order
7020 * violation: here it would be requesting the object lock (at the beginning
7021 * of the method), then queryInfoSem, and below the other way round. */
7022 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
7023
7024 /* take the opportunity to have a media tree lock, released initially */
7025 Assert(!isWriteLockOnCurrentThread());
7026 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7027 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7028 treeLock.release();
7029
7030 /* re-take the caller, but not the object lock, to keep uninit away */
7031 autoCaller.add();
7032 if (FAILED(autoCaller.rc()))
7033 {
7034 m->queryInfoRunning = false;
7035 return autoCaller.rc();
7036 }
7037
7038 try
7039 {
7040 /* skip accessibility checks for host drives */
7041 if (m->hostDrive)
7042 {
7043 success = true;
7044 throw S_OK;
7045 }
7046
7047 PVDISK hdd;
7048 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7049 ComAssertRCThrow(vrc, E_FAIL);
7050
7051 try
7052 {
7053 /** @todo This kind of opening of media is assuming that diff
7054 * media can be opened as base media. Should be documented that
7055 * it must work for all medium format backends. */
7056 vrc = VDOpen(hdd,
7057 format.c_str(),
7058 location.c_str(),
7059 uOpenFlags | m->uOpenFlagsDef,
7060 m->vdImageIfaces);
7061 if (RT_FAILURE(vrc))
7062 {
7063 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
7064 location.c_str(), i_vdError(vrc).c_str());
7065 throw S_OK;
7066 }
7067
7068 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
7069 {
7070 /* Modify the UUIDs if necessary. The associated fields are
7071 * not modified by other code, so no need to copy. */
7072 if (fSetImageId)
7073 {
7074 alock.acquire();
7075 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
7076 alock.release();
7077 if (RT_FAILURE(vrc))
7078 {
7079 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
7080 location.c_str(), i_vdError(vrc).c_str());
7081 throw S_OK;
7082 }
7083 mediumId = m->uuidImage;
7084 }
7085 if (fSetParentId)
7086 {
7087 alock.acquire();
7088 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
7089 alock.release();
7090 if (RT_FAILURE(vrc))
7091 {
7092 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
7093 location.c_str(), i_vdError(vrc).c_str());
7094 throw S_OK;
7095 }
7096 }
7097 /* zap the information, these are no long-term members */
7098 alock.acquire();
7099 unconst(m->uuidImage).clear();
7100 unconst(m->uuidParentImage).clear();
7101 alock.release();
7102
7103 /* check the UUID */
7104 RTUUID uuid;
7105 vrc = VDGetUuid(hdd, 0, &uuid);
7106 ComAssertRCThrow(vrc, E_FAIL);
7107
7108 if (isImport)
7109 {
7110 mediumId = uuid;
7111
7112 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
7113 // only when importing a VDMK that has no UUID, create one in memory
7114 mediumId.create();
7115 }
7116 else
7117 {
7118 Assert(!mediumId.isZero());
7119
7120 if (mediumId != uuid)
7121 {
7122 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7123 lastAccessError = Utf8StrFmt(
7124 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
7125 &uuid,
7126 location.c_str(),
7127 mediumId.raw(),
7128 pVirtualBox->i_settingsFilePath().c_str());
7129 throw S_OK;
7130 }
7131 }
7132 }
7133 else
7134 {
7135 /* the backend does not support storing UUIDs within the
7136 * underlying storage so use what we store in XML */
7137
7138 if (fSetImageId)
7139 {
7140 /* set the UUID if an API client wants to change it */
7141 alock.acquire();
7142 mediumId = m->uuidImage;
7143 alock.release();
7144 }
7145 else if (isImport)
7146 {
7147 /* generate an UUID for an imported UUID-less medium */
7148 mediumId.create();
7149 }
7150 }
7151
7152 /* set the image uuid before the below parent uuid handling code
7153 * might place it somewhere in the media tree, so that the medium
7154 * UUID is valid at this point */
7155 alock.acquire();
7156 if (isImport || fSetImageId)
7157 unconst(m->id) = mediumId;
7158 alock.release();
7159
7160 /* get the medium variant */
7161 unsigned uImageFlags;
7162 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7163 ComAssertRCThrow(vrc, E_FAIL);
7164 alock.acquire();
7165 m->variant = (MediumVariant_T)uImageFlags;
7166 alock.release();
7167
7168 /* check/get the parent uuid and update corresponding state */
7169 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
7170 {
7171 RTUUID parentId;
7172 vrc = VDGetParentUuid(hdd, 0, &parentId);
7173 ComAssertRCThrow(vrc, E_FAIL);
7174
7175 /* streamOptimized VMDK images are only accepted as base
7176 * images, as this allows automatic repair of OVF appliances.
7177 * Since such images don't support random writes they will not
7178 * be created for diff images. Only an overly smart user might
7179 * manually create this case. Too bad for him. */
7180 if ( (isImport || fSetParentId)
7181 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
7182 {
7183 /* the parent must be known to us. Note that we freely
7184 * call locking methods of mVirtualBox and parent, as all
7185 * relevant locks must be already held. There may be no
7186 * concurrent access to the just opened medium on other
7187 * threads yet (and init() will fail if this method reports
7188 * MediumState_Inaccessible) */
7189
7190 ComObjPtr<Medium> pParent;
7191 if (RTUuidIsNull(&parentId))
7192 rc = VBOX_E_OBJECT_NOT_FOUND;
7193 else
7194 rc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
7195 if (FAILED(rc))
7196 {
7197 if (fSetImageId && !fSetParentId)
7198 {
7199 /* If the image UUID gets changed for an existing
7200 * image then the parent UUID can be stale. In such
7201 * cases clear the parent information. The parent
7202 * information may/will be re-set later if the
7203 * API client wants to adjust a complete medium
7204 * hierarchy one by one. */
7205 rc = S_OK;
7206 alock.acquire();
7207 RTUuidClear(&parentId);
7208 vrc = VDSetParentUuid(hdd, 0, &parentId);
7209 alock.release();
7210 ComAssertRCThrow(vrc, E_FAIL);
7211 }
7212 else
7213 {
7214 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
7215 &parentId, location.c_str(),
7216 pVirtualBox->i_settingsFilePath().c_str());
7217 throw S_OK;
7218 }
7219 }
7220
7221 /* must drop the caller before taking the tree lock */
7222 autoCaller.release();
7223 /* we set m->pParent & children() */
7224 treeLock.acquire();
7225 autoCaller.add();
7226 if (FAILED(autoCaller.rc()))
7227 throw autoCaller.rc();
7228
7229 if (m->pParent)
7230 i_deparent();
7231
7232 if (!pParent.isNull())
7233 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7234 {
7235 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
7236 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7237 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"),
7238 pParent->m->strLocationFull.c_str());
7239 }
7240 i_setParent(pParent);
7241
7242 treeLock.release();
7243 }
7244 else
7245 {
7246 /* must drop the caller before taking the tree lock */
7247 autoCaller.release();
7248 /* we access m->pParent */
7249 treeLock.acquire();
7250 autoCaller.add();
7251 if (FAILED(autoCaller.rc()))
7252 throw autoCaller.rc();
7253
7254 /* check that parent UUIDs match. Note that there's no need
7255 * for the parent's AutoCaller (our lifetime is bound to
7256 * it) */
7257
7258 if (m->pParent.isNull())
7259 {
7260 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
7261 * and 3.1.0-3.1.8 there are base images out there
7262 * which have a non-zero parent UUID. No point in
7263 * complaining about them, instead automatically
7264 * repair the problem. Later we can bring back the
7265 * error message, but we should wait until really
7266 * most users have repaired their images, either with
7267 * VBoxFixHdd or this way. */
7268#if 1
7269 fRepairImageZeroParentUuid = true;
7270#else /* 0 */
7271 lastAccessError = Utf8StrFmt(
7272 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
7273 location.c_str(),
7274 pVirtualBox->settingsFilePath().c_str());
7275 treeLock.release();
7276 throw S_OK;
7277#endif /* 0 */
7278 }
7279
7280 {
7281 autoCaller.release();
7282 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
7283 autoCaller.add();
7284 if (FAILED(autoCaller.rc()))
7285 throw autoCaller.rc();
7286
7287 if ( !fRepairImageZeroParentUuid
7288 && m->pParent->i_getState() != MediumState_Inaccessible
7289 && m->pParent->i_getId() != parentId)
7290 {
7291 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7292 lastAccessError = Utf8StrFmt(
7293 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
7294 &parentId, location.c_str(),
7295 m->pParent->i_getId().raw(),
7296 pVirtualBox->i_settingsFilePath().c_str());
7297 parentLock.release();
7298 treeLock.release();
7299 throw S_OK;
7300 }
7301 }
7302
7303 /// @todo NEWMEDIA what to do if the parent is not
7304 /// accessible while the diff is? Probably nothing. The
7305 /// real code will detect the mismatch anyway.
7306
7307 treeLock.release();
7308 }
7309 }
7310
7311 mediumSize = VDGetFileSize(hdd, 0);
7312 mediumLogicalSize = VDGetSize(hdd, 0);
7313
7314 success = true;
7315 }
7316 catch (HRESULT aRC)
7317 {
7318 rc = aRC;
7319 }
7320
7321 vrc = VDDestroy(hdd);
7322 if (RT_FAILURE(vrc))
7323 {
7324 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
7325 location.c_str(), i_vdError(vrc).c_str());
7326 success = false;
7327 throw S_OK;
7328 }
7329 }
7330 catch (HRESULT aRC)
7331 {
7332 rc = aRC;
7333 }
7334
7335 autoCaller.release();
7336 treeLock.acquire();
7337 autoCaller.add();
7338 if (FAILED(autoCaller.rc()))
7339 {
7340 m->queryInfoRunning = false;
7341 return autoCaller.rc();
7342 }
7343 alock.acquire();
7344
7345 if (success)
7346 {
7347 m->size = mediumSize;
7348 m->logicalSize = mediumLogicalSize;
7349 m->strLastAccessError.setNull();
7350 }
7351 else
7352 {
7353 m->strLastAccessError = lastAccessError;
7354 Log1WarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
7355 location.c_str(), m->strLastAccessError.c_str(), rc, vrc));
7356 }
7357
7358 /* Set the proper state according to the result of the check */
7359 if (success)
7360 m->preLockState = MediumState_Created;
7361 else
7362 m->preLockState = MediumState_Inaccessible;
7363
7364 /* unblock anyone waiting for the i_queryInfo results */
7365 qlock.release();
7366 m->queryInfoRunning = false;
7367
7368 pToken->Abandon();
7369 pToken.setNull();
7370
7371 if (FAILED(rc))
7372 return rc;
7373
7374 /* If this is a base image which incorrectly has a parent UUID set,
7375 * repair the image now by zeroing the parent UUID. This is only done
7376 * when we have structural information from a config file, on import
7377 * this is not possible. If someone would accidentally call openMedium
7378 * with a diff image before the base is registered this would destroy
7379 * the diff. Not acceptable. */
7380 do
7381 {
7382 if (fRepairImageZeroParentUuid)
7383 {
7384 rc = LockWrite(pToken.asOutParam());
7385 if (FAILED(rc))
7386 break;
7387
7388 alock.release();
7389
7390 try
7391 {
7392 PVDISK hdd;
7393 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7394 ComAssertRCThrow(vrc, E_FAIL);
7395
7396 try
7397 {
7398 vrc = VDOpen(hdd,
7399 format.c_str(),
7400 location.c_str(),
7401 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
7402 m->vdImageIfaces);
7403 if (RT_FAILURE(vrc))
7404 throw S_OK;
7405
7406 RTUUID zeroParentUuid;
7407 RTUuidClear(&zeroParentUuid);
7408 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
7409 ComAssertRCThrow(vrc, E_FAIL);
7410 }
7411 catch (HRESULT aRC)
7412 {
7413 rc = aRC;
7414 }
7415
7416 VDDestroy(hdd);
7417 }
7418 catch (HRESULT aRC)
7419 {
7420 rc = aRC;
7421 }
7422
7423 pToken->Abandon();
7424 pToken.setNull();
7425 if (FAILED(rc))
7426 break;
7427 }
7428 } while(0);
7429
7430 return rc;
7431}
7432
7433/**
7434 * Performs extra checks if the medium can be closed and returns S_OK in
7435 * this case. Otherwise, returns a respective error message. Called by
7436 * Close() under the medium tree lock and the medium lock.
7437 *
7438 * @note Also reused by Medium::Reset().
7439 *
7440 * @note Caller must hold the media tree write lock!
7441 */
7442HRESULT Medium::i_canClose()
7443{
7444 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7445
7446 if (i_getChildren().size() != 0)
7447 return setError(VBOX_E_OBJECT_IN_USE,
7448 tr("Cannot close medium '%s' because it has %d child media"),
7449 m->strLocationFull.c_str(), i_getChildren().size());
7450
7451 return S_OK;
7452}
7453
7454/**
7455 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
7456 *
7457 * @note Caller must have locked the media tree lock for writing!
7458 */
7459HRESULT Medium::i_unregisterWithVirtualBox()
7460{
7461 /* Note that we need to de-associate ourselves from the parent to let
7462 * VirtualBox::i_unregisterMedium() properly save the registry */
7463
7464 /* we modify m->pParent and access children */
7465 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7466
7467 Medium *pParentBackup = m->pParent;
7468 AssertReturn(i_getChildren().size() == 0, E_FAIL);
7469 if (m->pParent)
7470 i_deparent();
7471
7472 HRESULT rc = m->pVirtualBox->i_unregisterMedium(this);
7473 if (FAILED(rc))
7474 {
7475 if (pParentBackup)
7476 {
7477 // re-associate with the parent as we are still relatives in the registry
7478 i_setParent(pParentBackup);
7479 }
7480 }
7481
7482 return rc;
7483}
7484
7485/**
7486 * Like SetProperty but do not trigger a settings store. Only for internal use!
7487 */
7488HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
7489{
7490 AutoCaller autoCaller(this);
7491 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7492
7493 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
7494
7495 switch (m->state)
7496 {
7497 case MediumState_Created:
7498 case MediumState_Inaccessible:
7499 break;
7500 default:
7501 return i_setStateError();
7502 }
7503
7504 m->mapProperties[aName] = aValue;
7505
7506 return S_OK;
7507}
7508
7509/**
7510 * Sets the extended error info according to the current media state.
7511 *
7512 * @note Must be called from under this object's write or read lock.
7513 */
7514HRESULT Medium::i_setStateError()
7515{
7516 HRESULT rc = E_FAIL;
7517
7518 switch (m->state)
7519 {
7520 case MediumState_NotCreated:
7521 {
7522 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7523 tr("Storage for the medium '%s' is not created"),
7524 m->strLocationFull.c_str());
7525 break;
7526 }
7527 case MediumState_Created:
7528 {
7529 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7530 tr("Storage for the medium '%s' is already created"),
7531 m->strLocationFull.c_str());
7532 break;
7533 }
7534 case MediumState_LockedRead:
7535 {
7536 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7537 tr("Medium '%s' is locked for reading by another task"),
7538 m->strLocationFull.c_str());
7539 break;
7540 }
7541 case MediumState_LockedWrite:
7542 {
7543 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7544 tr("Medium '%s' is locked for writing by another task"),
7545 m->strLocationFull.c_str());
7546 break;
7547 }
7548 case MediumState_Inaccessible:
7549 {
7550 /* be in sync with Console::powerUpThread() */
7551 if (!m->strLastAccessError.isEmpty())
7552 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7553 tr("Medium '%s' is not accessible. %s"),
7554 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
7555 else
7556 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7557 tr("Medium '%s' is not accessible"),
7558 m->strLocationFull.c_str());
7559 break;
7560 }
7561 case MediumState_Creating:
7562 {
7563 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7564 tr("Storage for the medium '%s' is being created"),
7565 m->strLocationFull.c_str());
7566 break;
7567 }
7568 case MediumState_Deleting:
7569 {
7570 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7571 tr("Storage for the medium '%s' is being deleted"),
7572 m->strLocationFull.c_str());
7573 break;
7574 }
7575 default:
7576 {
7577 AssertFailed();
7578 break;
7579 }
7580 }
7581
7582 return rc;
7583}
7584
7585/**
7586 * Sets the value of m->strLocationFull. The given location must be a fully
7587 * qualified path; relative paths are not supported here.
7588 *
7589 * As a special exception, if the specified location is a file path that ends with '/'
7590 * then the file name part will be generated by this method automatically in the format
7591 * '{\<uuid\>}.\<ext\>' where \<uuid\> is a fresh UUID that this method will generate
7592 * and assign to this medium, and \<ext\> is the default extension for this
7593 * medium's storage format. Note that this procedure requires the media state to
7594 * be NotCreated and will return a failure otherwise.
7595 *
7596 * @param aLocation Location of the storage unit. If the location is a FS-path,
7597 * then it can be relative to the VirtualBox home directory.
7598 * @param aFormat Optional fallback format if it is an import and the format
7599 * cannot be determined.
7600 *
7601 * @note Must be called from under this object's write lock.
7602 */
7603HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
7604 const Utf8Str &aFormat /* = Utf8Str::Empty */)
7605{
7606 AssertReturn(!aLocation.isEmpty(), E_FAIL);
7607
7608 AutoCaller autoCaller(this);
7609 AssertComRCReturnRC(autoCaller.rc());
7610
7611 /* formatObj may be null only when initializing from an existing path and
7612 * no format is known yet */
7613 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
7614 || ( getObjectState().getState() == ObjectState::InInit
7615 && m->state != MediumState_NotCreated
7616 && m->id.isZero()
7617 && m->strFormat.isEmpty()
7618 && m->formatObj.isNull()),
7619 E_FAIL);
7620
7621 /* are we dealing with a new medium constructed using the existing
7622 * location? */
7623 bool isImport = m->strFormat.isEmpty();
7624
7625 if ( isImport
7626 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7627 && !m->hostDrive))
7628 {
7629 Guid id;
7630
7631 Utf8Str locationFull(aLocation);
7632
7633 if (m->state == MediumState_NotCreated)
7634 {
7635 /* must be a file (formatObj must be already known) */
7636 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
7637
7638 if (RTPathFilename(aLocation.c_str()) == NULL)
7639 {
7640 /* no file name is given (either an empty string or ends with a
7641 * slash), generate a new UUID + file name if the state allows
7642 * this */
7643
7644 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
7645 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
7646 E_FAIL);
7647
7648 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
7649 ComAssertMsgRet(!strExt.isEmpty(),
7650 ("Default extension must not be empty\n"),
7651 E_FAIL);
7652
7653 id.create();
7654
7655 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
7656 aLocation.c_str(), id.raw(), strExt.c_str());
7657 }
7658 }
7659
7660 // we must always have full paths now (if it refers to a file)
7661 if ( ( m->formatObj.isNull()
7662 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7663 && !RTPathStartsWithRoot(locationFull.c_str()))
7664 return setError(VBOX_E_FILE_ERROR,
7665 tr("The given path '%s' is not fully qualified"),
7666 locationFull.c_str());
7667
7668 /* detect the backend from the storage unit if importing */
7669 if (isImport)
7670 {
7671 VDTYPE const enmDesiredType = i_convertDeviceType();
7672 VDTYPE enmType = VDTYPE_INVALID;
7673 char *backendName = NULL;
7674
7675 /* is it a file? */
7676 RTFILE hFile;
7677 int vrc = RTFileOpen(&hFile, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
7678 if (RT_SUCCESS(vrc))
7679 {
7680 RTFileClose(hFile);
7681 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7682 locationFull.c_str(), enmDesiredType, &backendName, &enmType);
7683 }
7684 else if ( vrc != VERR_FILE_NOT_FOUND
7685 && vrc != VERR_PATH_NOT_FOUND
7686 && vrc != VERR_ACCESS_DENIED
7687 && locationFull != aLocation)
7688 {
7689 /* assume it's not a file, restore the original location */
7690 locationFull = aLocation;
7691 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7692 locationFull.c_str(), enmDesiredType, &backendName, &enmType);
7693 }
7694
7695 if (RT_FAILURE(vrc))
7696 {
7697 if (vrc == VERR_ACCESS_DENIED)
7698 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
7699 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
7700 locationFull.c_str(), vrc);
7701 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
7702 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
7703 tr("Could not find file for the medium '%s' (%Rrc)"),
7704 locationFull.c_str(), vrc);
7705 if (aFormat.isEmpty())
7706 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7707 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
7708 locationFull.c_str(), vrc);
7709 HRESULT rc = i_setFormat(aFormat);
7710 /* setFormat() must not fail since we've just used the backend so
7711 * the format object must be there */
7712 AssertComRCReturnRC(rc);
7713 }
7714 else if ( enmType == VDTYPE_INVALID
7715 || m->devType != i_convertToDeviceType(enmType))
7716 {
7717 /*
7718 * The user tried to use a image as a device which is not supported
7719 * by the backend.
7720 */
7721 RTStrFree(backendName);
7722 return setError(E_FAIL,
7723 tr("The medium '%s' can't be used as the requested device type (%s, detected %s)"),
7724 locationFull.c_str(), getDeviceTypeName(m->devType), getVDTypeName(enmType));
7725 }
7726 else
7727 {
7728 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
7729
7730 HRESULT rc = i_setFormat(backendName);
7731 RTStrFree(backendName);
7732
7733 /* setFormat() must not fail since we've just used the backend so
7734 * the format object must be there */
7735 AssertComRCReturnRC(rc);
7736 }
7737 }
7738
7739 m->strLocationFull = locationFull;
7740
7741 /* is it still a file? */
7742 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7743 && (m->state == MediumState_NotCreated)
7744 )
7745 /* assign a new UUID (this UUID will be used when calling
7746 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
7747 * also do that if we didn't generate it to make sure it is
7748 * either generated by us or reset to null */
7749 unconst(m->id) = id;
7750 }
7751 else
7752 m->strLocationFull = aLocation;
7753
7754 return S_OK;
7755}
7756
7757/**
7758 * Checks that the format ID is valid and sets it on success.
7759 *
7760 * Note that this method will caller-reference the format object on success!
7761 * This reference must be released somewhere to let the MediumFormat object be
7762 * uninitialized.
7763 *
7764 * @note Must be called from under this object's write lock.
7765 */
7766HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
7767{
7768 /* get the format object first */
7769 {
7770 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
7771 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
7772
7773 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
7774 if (m->formatObj.isNull())
7775 return setError(E_INVALIDARG,
7776 tr("Invalid medium storage format '%s'"),
7777 aFormat.c_str());
7778
7779 /* get properties (preinsert them as keys in the map). Note that the
7780 * map doesn't grow over the object life time since the set of
7781 * properties is meant to be constant. */
7782
7783 Assert(m->mapProperties.empty());
7784
7785 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
7786 it != m->formatObj->i_getProperties().end();
7787 ++it)
7788 {
7789 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
7790 }
7791 }
7792
7793 unconst(m->strFormat) = aFormat;
7794
7795 return S_OK;
7796}
7797
7798/**
7799 * Converts the Medium device type to the VD type.
7800 */
7801VDTYPE Medium::i_convertDeviceType()
7802{
7803 VDTYPE enmType;
7804
7805 switch (m->devType)
7806 {
7807 case DeviceType_HardDisk:
7808 enmType = VDTYPE_HDD;
7809 break;
7810 case DeviceType_DVD:
7811 enmType = VDTYPE_OPTICAL_DISC;
7812 break;
7813 case DeviceType_Floppy:
7814 enmType = VDTYPE_FLOPPY;
7815 break;
7816 default:
7817 ComAssertFailedRet(VDTYPE_INVALID);
7818 }
7819
7820 return enmType;
7821}
7822
7823/**
7824 * Converts from the VD type to the medium type.
7825 */
7826DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
7827{
7828 DeviceType_T devType;
7829
7830 switch (enmType)
7831 {
7832 case VDTYPE_HDD:
7833 devType = DeviceType_HardDisk;
7834 break;
7835 case VDTYPE_OPTICAL_DISC:
7836 devType = DeviceType_DVD;
7837 break;
7838 case VDTYPE_FLOPPY:
7839 devType = DeviceType_Floppy;
7840 break;
7841 default:
7842 ComAssertFailedRet(DeviceType_Null);
7843 }
7844
7845 return devType;
7846}
7847
7848/**
7849 * Internal method which checks whether a property name is for a filter plugin.
7850 */
7851bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
7852{
7853 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
7854 size_t offSlash;
7855 if ((offSlash = aName.find("/", 0)) != aName.npos)
7856 {
7857 com::Utf8Str strFilter;
7858 com::Utf8Str strKey;
7859
7860 HRESULT rc = strFilter.assignEx(aName, 0, offSlash);
7861 if (FAILED(rc))
7862 return false;
7863
7864 rc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
7865 if (FAILED(rc))
7866 return false;
7867
7868 VDFILTERINFO FilterInfo;
7869 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
7870 if (RT_SUCCESS(vrc))
7871 {
7872 /* Check that the property exists. */
7873 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
7874 while (paConfig->pszKey)
7875 {
7876 if (strKey.equals(paConfig->pszKey))
7877 return true;
7878 paConfig++;
7879 }
7880 }
7881 }
7882
7883 return false;
7884}
7885
7886/**
7887 * Returns the last error message collected by the i_vdErrorCall callback and
7888 * resets it.
7889 *
7890 * The error message is returned prepended with a dot and a space, like this:
7891 * <code>
7892 * ". <error_text> (%Rrc)"
7893 * </code>
7894 * to make it easily appendable to a more general error message. The @c %Rrc
7895 * format string is given @a aVRC as an argument.
7896 *
7897 * If there is no last error message collected by i_vdErrorCall or if it is a
7898 * null or empty string, then this function returns the following text:
7899 * <code>
7900 * " (%Rrc)"
7901 * </code>
7902 *
7903 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7904 * the callback isn't called by more than one thread at a time.
7905 *
7906 * @param aVRC VBox error code to use when no error message is provided.
7907 */
7908Utf8Str Medium::i_vdError(int aVRC)
7909{
7910 Utf8Str error;
7911
7912 if (m->vdError.isEmpty())
7913 error = Utf8StrFmt(" (%Rrc)", aVRC);
7914 else
7915 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
7916
7917 m->vdError.setNull();
7918
7919 return error;
7920}
7921
7922/**
7923 * Error message callback.
7924 *
7925 * Puts the reported error message to the m->vdError field.
7926 *
7927 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7928 * the callback isn't called by more than one thread at a time.
7929 *
7930 * @param pvUser The opaque data passed on container creation.
7931 * @param rc The VBox error code.
7932 * @param SRC_POS Use RT_SRC_POS.
7933 * @param pszFormat Error message format string.
7934 * @param va Error message arguments.
7935 */
7936/*static*/
7937DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
7938 const char *pszFormat, va_list va)
7939{
7940 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
7941
7942 Medium *that = static_cast<Medium*>(pvUser);
7943 AssertReturnVoid(that != NULL);
7944
7945 if (that->m->vdError.isEmpty())
7946 that->m->vdError =
7947 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
7948 else
7949 that->m->vdError =
7950 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
7951 Utf8Str(pszFormat, va).c_str(), rc);
7952}
7953
7954/* static */
7955DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
7956 const char * /* pszzValid */)
7957{
7958 Medium *that = static_cast<Medium*>(pvUser);
7959 AssertReturn(that != NULL, false);
7960
7961 /* we always return true since the only keys we have are those found in
7962 * VDBACKENDINFO */
7963 return true;
7964}
7965
7966/* static */
7967DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
7968 const char *pszName,
7969 size_t *pcbValue)
7970{
7971 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
7972
7973 Medium *that = static_cast<Medium*>(pvUser);
7974 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7975
7976 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
7977 if (it == that->m->mapProperties.end())
7978 return VERR_CFGM_VALUE_NOT_FOUND;
7979
7980 /* we interpret null values as "no value" in Medium */
7981 if (it->second.isEmpty())
7982 return VERR_CFGM_VALUE_NOT_FOUND;
7983
7984 *pcbValue = it->second.length() + 1 /* include terminator */;
7985
7986 return VINF_SUCCESS;
7987}
7988
7989/* static */
7990DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
7991 const char *pszName,
7992 char *pszValue,
7993 size_t cchValue)
7994{
7995 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
7996
7997 Medium *that = static_cast<Medium*>(pvUser);
7998 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7999
8000 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
8001 if (it == that->m->mapProperties.end())
8002 return VERR_CFGM_VALUE_NOT_FOUND;
8003
8004 /* we interpret null values as "no value" in Medium */
8005 if (it->second.isEmpty())
8006 return VERR_CFGM_VALUE_NOT_FOUND;
8007
8008 const Utf8Str &value = it->second;
8009 if (value.length() >= cchValue)
8010 return VERR_CFGM_NOT_ENOUGH_SPACE;
8011
8012 memcpy(pszValue, value.c_str(), value.length() + 1);
8013
8014 return VINF_SUCCESS;
8015}
8016
8017DECLCALLBACK(bool) Medium::i_vdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid)
8018{
8019 /* Just return always true here. */
8020 NOREF(pvUser);
8021 NOREF(pszzValid);
8022 return true;
8023}
8024
8025DECLCALLBACK(int) Medium::i_vdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
8026{
8027 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8028 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8029 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
8030
8031 size_t cbValue = 0;
8032 if (!strcmp(pszName, "Algorithm"))
8033 cbValue = strlen(pSettings->pszCipher) + 1;
8034 else if (!strcmp(pszName, "KeyId"))
8035 cbValue = sizeof("irrelevant");
8036 else if (!strcmp(pszName, "KeyStore"))
8037 {
8038 if (!pSettings->pszKeyStoreLoad)
8039 return VERR_CFGM_VALUE_NOT_FOUND;
8040 cbValue = strlen(pSettings->pszKeyStoreLoad) + 1;
8041 }
8042 else if (!strcmp(pszName, "CreateKeyStore"))
8043 cbValue = 2; /* Single digit + terminator. */
8044 else
8045 return VERR_CFGM_VALUE_NOT_FOUND;
8046
8047 *pcbValue = cbValue + 1 /* include terminator */;
8048
8049 return VINF_SUCCESS;
8050}
8051
8052DECLCALLBACK(int) Medium::i_vdCryptoConfigQuery(void *pvUser, const char *pszName,
8053 char *pszValue, size_t cchValue)
8054{
8055 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8056 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8057 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
8058
8059 const char *psz = NULL;
8060 if (!strcmp(pszName, "Algorithm"))
8061 psz = pSettings->pszCipher;
8062 else if (!strcmp(pszName, "KeyId"))
8063 psz = "irrelevant";
8064 else if (!strcmp(pszName, "KeyStore"))
8065 psz = pSettings->pszKeyStoreLoad;
8066 else if (!strcmp(pszName, "CreateKeyStore"))
8067 {
8068 if (pSettings->fCreateKeyStore)
8069 psz = "1";
8070 else
8071 psz = "0";
8072 }
8073 else
8074 return VERR_CFGM_VALUE_NOT_FOUND;
8075
8076 size_t cch = strlen(psz);
8077 if (cch >= cchValue)
8078 return VERR_CFGM_NOT_ENOUGH_SPACE;
8079
8080 memcpy(pszValue, psz, cch + 1);
8081 return VINF_SUCCESS;
8082}
8083
8084DECLCALLBACK(int) Medium::i_vdConfigUpdate(void *pvUser,
8085 bool fCreate,
8086 const char *pszName,
8087 const char *pszValue)
8088{
8089 Medium *that = (Medium *)pvUser;
8090
8091 // Detect if this runs inside i_queryInfo() on the current thread.
8092 // Skip if not. Check does not need synchronization.
8093 if (!that->m || !that->m->queryInfoRunning || !that->m->queryInfoSem.isWriteLockOnCurrentThread())
8094 return VINF_SUCCESS;
8095
8096 // It's guaranteed that this code is executing inside Medium::i_queryInfo,
8097 // can assume it took care of synchronization.
8098 int rv = VINF_SUCCESS;
8099 Utf8Str strName(pszName);
8100 settings::StringsMap::const_iterator it = that->m->mapProperties.find(strName);
8101 if (it == that->m->mapProperties.end() && !fCreate)
8102 rv = VERR_CFGM_VALUE_NOT_FOUND;
8103 else
8104 that->m->mapProperties[strName] = Utf8Str(pszValue);
8105 return rv;
8106}
8107
8108DECLCALLBACK(int) Medium::i_vdCryptoKeyRetain(void *pvUser, const char *pszId,
8109 const uint8_t **ppbKey, size_t *pcbKey)
8110{
8111 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8112 NOREF(pszId);
8113 NOREF(ppbKey);
8114 NOREF(pcbKey);
8115 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8116 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8117}
8118
8119DECLCALLBACK(int) Medium::i_vdCryptoKeyRelease(void *pvUser, const char *pszId)
8120{
8121 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8122 NOREF(pszId);
8123 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8124 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8125}
8126
8127DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
8128{
8129 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8130 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8131
8132 NOREF(pszId);
8133 *ppszPassword = pSettings->pszPassword;
8134 return VINF_SUCCESS;
8135}
8136
8137DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
8138{
8139 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8140 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8141 NOREF(pszId);
8142 return VINF_SUCCESS;
8143}
8144
8145DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore)
8146{
8147 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8148 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8149
8150 pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore);
8151 if (!pSettings->pszKeyStore)
8152 return VERR_NO_MEMORY;
8153
8154 memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore);
8155 return VINF_SUCCESS;
8156}
8157
8158DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher,
8159 const uint8_t *pbDek, size_t cbDek)
8160{
8161 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8162 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8163
8164 pSettings->pszCipherReturned = RTStrDup(pszCipher);
8165 pSettings->pbDek = pbDek;
8166 pSettings->cbDek = cbDek;
8167
8168 return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY;
8169}
8170
8171/**
8172 * Creates a VDISK instance for this medium.
8173 *
8174 * @note Caller should not hold any medium related locks as this method will
8175 * acquire the medium lock for writing and others (VirtualBox).
8176 *
8177 * @returns COM status code.
8178 * @param fWritable Whether to return a writable VDISK instance
8179 * (true) or a read-only one (false).
8180 * @param pKeyStore The key store.
8181 * @param ppHdd Where to return the pointer to the VDISK on
8182 * success.
8183 * @param pMediumLockList The lock list to populate and lock. Caller
8184 * is responsible for calling the destructor or
8185 * MediumLockList::Clear() after destroying
8186 * @a *ppHdd
8187 * @param pCryptoSettings The crypto settings to use for setting up
8188 * decryption/encryption of the VDISK. This object
8189 * must be alive until the VDISK is destroyed!
8190 */
8191HRESULT Medium::i_openForIO(bool fWritable, SecretKeyStore *pKeyStore, PVDISK *ppHdd, MediumLockList *pMediumLockList,
8192 MediumCryptoFilterSettings *pCryptoSettings)
8193{
8194 /*
8195 * Create the media lock list and lock the media.
8196 */
8197 HRESULT hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
8198 fWritable ? this : NULL /* pToLockWrite */,
8199 false /* fMediumLockWriteAll */,
8200 NULL,
8201 *pMediumLockList);
8202 if (SUCCEEDED(hrc))
8203 hrc = pMediumLockList->Lock();
8204 if (FAILED(hrc))
8205 return hrc;
8206
8207 /*
8208 * Get the base medium before write locking this medium.
8209 */
8210 ComObjPtr<Medium> pBase = i_getBase();
8211 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8212
8213 /*
8214 * Create the VDISK instance.
8215 */
8216 PVDISK pHdd;
8217 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pHdd);
8218 AssertRCReturn(vrc, E_FAIL);
8219
8220 /*
8221 * Goto avoidance using try/catch/throw(HRESULT).
8222 */
8223 try
8224 {
8225 settings::StringsMap::iterator itKeyStore = pBase->m->mapProperties.find("CRYPT/KeyStore");
8226 if (itKeyStore != pBase->m->mapProperties.end())
8227 {
8228#ifdef VBOX_WITH_EXTPACK
8229 settings::StringsMap::iterator itKeyId = pBase->m->mapProperties.find("CRYPT/KeyId");
8230
8231 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
8232 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
8233 {
8234 /* Load the plugin */
8235 Utf8Str strPlugin;
8236 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
8237 if (SUCCEEDED(hrc))
8238 {
8239 vrc = VDPluginLoadFromFilename(strPlugin.c_str());
8240 if (RT_FAILURE(vrc))
8241 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
8242 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
8243 i_vdError(vrc).c_str());
8244 }
8245 else
8246 throw setError(VBOX_E_NOT_SUPPORTED,
8247 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
8248 ORACLE_PUEL_EXTPACK_NAME);
8249 }
8250 else
8251 throw setError(VBOX_E_NOT_SUPPORTED,
8252 tr("Encryption is not supported because the extension pack '%s' is missing"),
8253 ORACLE_PUEL_EXTPACK_NAME);
8254
8255 if (itKeyId == pBase->m->mapProperties.end())
8256 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8257 tr("Image '%s' is configured for encryption but doesn't has a key identifier set"),
8258 pBase->m->strLocationFull.c_str());
8259
8260 /* Find the proper secret key in the key store. */
8261 if (!pKeyStore)
8262 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8263 tr("Image '%s' is configured for encryption but there is no key store to retrieve the password from"),
8264 pBase->m->strLocationFull.c_str());
8265
8266 SecretKey *pKey = NULL;
8267 vrc = pKeyStore->retainSecretKey(itKeyId->second, &pKey);
8268 if (RT_FAILURE(vrc))
8269 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
8270 tr("Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)"),
8271 itKeyId->second.c_str(), vrc);
8272
8273 i_taskEncryptSettingsSetup(pCryptoSettings, NULL, itKeyStore->second.c_str(), (const char *)pKey->getKeyBuffer(),
8274 false /* fCreateKeyStore */);
8275 vrc = VDFilterAdd(pHdd, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pCryptoSettings->vdFilterIfaces);
8276 pKeyStore->releaseSecretKey(itKeyId->second);
8277 if (vrc == VERR_VD_PASSWORD_INCORRECT)
8278 throw setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc, tr("The password to decrypt the image is incorrect"));
8279 if (RT_FAILURE(vrc))
8280 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc, tr("Failed to load the decryption filter: %s"),
8281 i_vdError(vrc).c_str());
8282#else
8283 RT_NOREF(pKeyStore, pCryptoSettings);
8284 throw setError(VBOX_E_NOT_SUPPORTED,
8285 tr("Encryption is not supported because extension pack support is not built in"));
8286#endif /* VBOX_WITH_EXTPACK */
8287 }
8288
8289 /*
8290 * Open all media in the source chain.
8291 */
8292 MediumLockList::Base::const_iterator sourceListBegin = pMediumLockList->GetBegin();
8293 MediumLockList::Base::const_iterator sourceListEnd = pMediumLockList->GetEnd();
8294 MediumLockList::Base::const_iterator mediumListLast = sourceListEnd;
8295 --mediumListLast;
8296
8297 for (MediumLockList::Base::const_iterator it = sourceListBegin; it != sourceListEnd; ++it)
8298 {
8299 const MediumLock &mediumLock = *it;
8300 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8301 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8302
8303 /* sanity check */
8304 Assert(pMedium->m->state == (fWritable && it == mediumListLast ? MediumState_LockedWrite : MediumState_LockedRead));
8305
8306 /* Open all media in read-only mode. */
8307 vrc = VDOpen(pHdd,
8308 pMedium->m->strFormat.c_str(),
8309 pMedium->m->strLocationFull.c_str(),
8310 m->uOpenFlagsDef | (fWritable && it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
8311 pMedium->m->vdImageIfaces);
8312 if (RT_FAILURE(vrc))
8313 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8314 tr("Could not open the medium storage unit '%s'%s"),
8315 pMedium->m->strLocationFull.c_str(),
8316 i_vdError(vrc).c_str());
8317 }
8318
8319 Assert(m->state == (fWritable ? MediumState_LockedWrite : MediumState_LockedRead));
8320
8321 /*
8322 * Done!
8323 */
8324 *ppHdd = pHdd;
8325 return S_OK;
8326 }
8327 catch (HRESULT hrc2)
8328 {
8329 hrc = hrc2;
8330 }
8331
8332 VDDestroy(pHdd);
8333 return hrc;
8334
8335}
8336
8337/**
8338 * Implementation code for the "create base" task.
8339 *
8340 * This only gets started from Medium::CreateBaseStorage() and always runs
8341 * asynchronously. As a result, we always save the VirtualBox.xml file when
8342 * we're done here.
8343 *
8344 * @param task
8345 * @return
8346 */
8347HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
8348{
8349 /** @todo r=klaus The code below needs to be double checked with regard
8350 * to lock order violations, it probably causes lock order issues related
8351 * to the AutoCaller usage. */
8352 HRESULT rc = S_OK;
8353
8354 /* these parameters we need after creation */
8355 uint64_t size = 0, logicalSize = 0;
8356 MediumVariant_T variant = MediumVariant_Standard;
8357 bool fGenerateUuid = false;
8358
8359 try
8360 {
8361 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8362
8363 /* The object may request a specific UUID (through a special form of
8364 * the moveTo() argument). Otherwise we have to generate it */
8365 Guid id = m->id;
8366
8367 fGenerateUuid = id.isZero();
8368 if (fGenerateUuid)
8369 {
8370 id.create();
8371 /* VirtualBox::i_registerMedium() will need UUID */
8372 unconst(m->id) = id;
8373 }
8374
8375 Utf8Str format(m->strFormat);
8376 Utf8Str location(m->strLocationFull);
8377 uint64_t capabilities = m->formatObj->i_getCapabilities();
8378 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
8379 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
8380 Assert(m->state == MediumState_Creating);
8381
8382 PVDISK hdd;
8383 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8384 ComAssertRCThrow(vrc, E_FAIL);
8385
8386 /* unlock before the potentially lengthy operation */
8387 thisLock.release();
8388
8389 try
8390 {
8391 /* ensure the directory exists */
8392 if (capabilities & MediumFormatCapabilities_File)
8393 {
8394 rc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8395 if (FAILED(rc))
8396 throw rc;
8397 }
8398
8399 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
8400
8401 vrc = VDCreateBase(hdd,
8402 format.c_str(),
8403 location.c_str(),
8404 task.mSize,
8405 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
8406 NULL,
8407 &geo,
8408 &geo,
8409 id.raw(),
8410 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8411 m->vdImageIfaces,
8412 task.mVDOperationIfaces);
8413 if (RT_FAILURE(vrc))
8414 {
8415 if (vrc == VERR_VD_INVALID_TYPE)
8416 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8417 tr("Parameters for creating the medium storage unit '%s' are invalid%s"),
8418 location.c_str(), i_vdError(vrc).c_str());
8419 else
8420 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8421 tr("Could not create the medium storage unit '%s'%s"),
8422 location.c_str(), i_vdError(vrc).c_str());
8423 }
8424
8425 if (task.mVariant & MediumVariant_Formatted)
8426 {
8427 RTVFSFILE hVfsFile;
8428 vrc = VDCreateVfsFileFromDisk(hdd, 0 /*fFlags*/, &hVfsFile);
8429 if (RT_FAILURE(vrc))
8430 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Opening medium storage unit '%s' failed%s"),
8431 location.c_str(), i_vdError(vrc).c_str());
8432 RTERRINFOSTATIC ErrInfo;
8433 vrc = RTFsFatVolFormat(hVfsFile, 0 /* offVol */, 0 /* cbVol */, RTFSFATVOL_FMT_F_FULL,
8434 0 /* cbSector */, 0 /* cbSectorPerCluster */, RTFSFATTYPE_INVALID,
8435 0 /* cHeads */, 0 /* cSectorsPerTrack*/, 0 /* bMedia */,
8436 0 /* cRootDirEntries */, 0 /* cHiddenSectors */,
8437 RTErrInfoInitStatic(&ErrInfo));
8438 RTVfsFileRelease(hVfsFile);
8439 if (RT_FAILURE(vrc) && RTErrInfoIsSet(&ErrInfo.Core))
8440 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed: %s"),
8441 location.c_str(), ErrInfo.Core.pszMsg);
8442 if (RT_FAILURE(vrc))
8443 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed%s"),
8444 location.c_str(), i_vdError(vrc).c_str());
8445 }
8446
8447 size = VDGetFileSize(hdd, 0);
8448 logicalSize = VDGetSize(hdd, 0);
8449 unsigned uImageFlags;
8450 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8451 if (RT_SUCCESS(vrc))
8452 variant = (MediumVariant_T)uImageFlags;
8453 }
8454 catch (HRESULT aRC) { rc = aRC; }
8455
8456 VDDestroy(hdd);
8457 }
8458 catch (HRESULT aRC) { rc = aRC; }
8459
8460 if (SUCCEEDED(rc))
8461 {
8462 /* register with mVirtualBox as the last step and move to
8463 * Created state only on success (leaving an orphan file is
8464 * better than breaking media registry consistency) */
8465 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8466 ComObjPtr<Medium> pMedium;
8467 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
8468 Assert(pMedium == NULL || this == pMedium);
8469 }
8470
8471 // re-acquire the lock before changing state
8472 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8473
8474 if (SUCCEEDED(rc))
8475 {
8476 m->state = MediumState_Created;
8477
8478 m->size = size;
8479 m->logicalSize = logicalSize;
8480 m->variant = variant;
8481
8482 thisLock.release();
8483 i_markRegistriesModified();
8484 if (task.isAsync())
8485 {
8486 // in asynchronous mode, save settings now
8487 m->pVirtualBox->i_saveModifiedRegistries();
8488 }
8489 }
8490 else
8491 {
8492 /* back to NotCreated on failure */
8493 m->state = MediumState_NotCreated;
8494
8495 /* reset UUID to prevent it from being reused next time */
8496 if (fGenerateUuid)
8497 unconst(m->id).clear();
8498 }
8499
8500 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
8501 {
8502 m->pVirtualBox->i_onMediumConfigChanged(this);
8503 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
8504 }
8505
8506 return rc;
8507}
8508
8509/**
8510 * Implementation code for the "create diff" task.
8511 *
8512 * This task always gets started from Medium::createDiffStorage() and can run
8513 * synchronously or asynchronously depending on the "wait" parameter passed to
8514 * that function. If we run synchronously, the caller expects the medium
8515 * registry modification to be set before returning; otherwise (in asynchronous
8516 * mode), we save the settings ourselves.
8517 *
8518 * @param task
8519 * @return
8520 */
8521HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
8522{
8523 /** @todo r=klaus The code below needs to be double checked with regard
8524 * to lock order violations, it probably causes lock order issues related
8525 * to the AutoCaller usage. */
8526 HRESULT rcTmp = S_OK;
8527
8528 const ComObjPtr<Medium> &pTarget = task.mTarget;
8529
8530 uint64_t size = 0, logicalSize = 0;
8531 MediumVariant_T variant = MediumVariant_Standard;
8532 bool fGenerateUuid = false;
8533
8534 try
8535 {
8536 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8537 {
8538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8539 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8540 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"),
8541 m->strLocationFull.c_str());
8542 }
8543
8544 /* Lock both in {parent,child} order. */
8545 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8546
8547 /* The object may request a specific UUID (through a special form of
8548 * the moveTo() argument). Otherwise we have to generate it */
8549 Guid targetId = pTarget->m->id;
8550
8551 fGenerateUuid = targetId.isZero();
8552 if (fGenerateUuid)
8553 {
8554 targetId.create();
8555 /* VirtualBox::i_registerMedium() will need UUID */
8556 unconst(pTarget->m->id) = targetId;
8557 }
8558
8559 Guid id = m->id;
8560
8561 Utf8Str targetFormat(pTarget->m->strFormat);
8562 Utf8Str targetLocation(pTarget->m->strLocationFull);
8563 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
8564 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
8565
8566 Assert(pTarget->m->state == MediumState_Creating);
8567 Assert(m->state == MediumState_LockedRead);
8568
8569 PVDISK hdd;
8570 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8571 ComAssertRCThrow(vrc, E_FAIL);
8572
8573 /* the two media are now protected by their non-default states;
8574 * unlock the media before the potentially lengthy operation */
8575 mediaLock.release();
8576
8577 try
8578 {
8579 /* Open all media in the target chain but the last. */
8580 MediumLockList::Base::const_iterator targetListBegin =
8581 task.mpMediumLockList->GetBegin();
8582 MediumLockList::Base::const_iterator targetListEnd =
8583 task.mpMediumLockList->GetEnd();
8584 for (MediumLockList::Base::const_iterator it = targetListBegin;
8585 it != targetListEnd;
8586 ++it)
8587 {
8588 const MediumLock &mediumLock = *it;
8589 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8590
8591 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8592
8593 /* Skip over the target diff medium */
8594 if (pMedium->m->state == MediumState_Creating)
8595 continue;
8596
8597 /* sanity check */
8598 Assert(pMedium->m->state == MediumState_LockedRead);
8599
8600 /* Open all media in appropriate mode. */
8601 vrc = VDOpen(hdd,
8602 pMedium->m->strFormat.c_str(),
8603 pMedium->m->strLocationFull.c_str(),
8604 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8605 pMedium->m->vdImageIfaces);
8606 if (RT_FAILURE(vrc))
8607 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8608 tr("Could not open the medium storage unit '%s'%s"),
8609 pMedium->m->strLocationFull.c_str(),
8610 i_vdError(vrc).c_str());
8611 }
8612
8613 /* ensure the target directory exists */
8614 if (capabilities & MediumFormatCapabilities_File)
8615 {
8616 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8617 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8618 if (FAILED(rc))
8619 throw rc;
8620 }
8621
8622 vrc = VDCreateDiff(hdd,
8623 targetFormat.c_str(),
8624 targetLocation.c_str(),
8625 (task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX))
8626 | VD_IMAGE_FLAGS_DIFF,
8627 NULL,
8628 targetId.raw(),
8629 id.raw(),
8630 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8631 pTarget->m->vdImageIfaces,
8632 task.mVDOperationIfaces);
8633 if (RT_FAILURE(vrc))
8634 {
8635 if (vrc == VERR_VD_INVALID_TYPE)
8636 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8637 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
8638 targetLocation.c_str(), i_vdError(vrc).c_str());
8639 else
8640 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8641 tr("Could not create the differencing medium storage unit '%s'%s"),
8642 targetLocation.c_str(), i_vdError(vrc).c_str());
8643 }
8644
8645 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
8646 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
8647 unsigned uImageFlags;
8648 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8649 if (RT_SUCCESS(vrc))
8650 variant = (MediumVariant_T)uImageFlags;
8651 }
8652 catch (HRESULT aRC) { rcTmp = aRC; }
8653
8654 VDDestroy(hdd);
8655 }
8656 catch (HRESULT aRC) { rcTmp = aRC; }
8657
8658 MultiResult mrc(rcTmp);
8659
8660 if (SUCCEEDED(mrc))
8661 {
8662 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8663
8664 Assert(pTarget->m->pParent.isNull());
8665
8666 /* associate child with the parent, maximum depth was checked above */
8667 pTarget->i_setParent(this);
8668
8669 /* diffs for immutable media are auto-reset by default */
8670 bool fAutoReset;
8671 {
8672 ComObjPtr<Medium> pBase = i_getBase();
8673 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
8674 fAutoReset = (pBase->m->type == MediumType_Immutable);
8675 }
8676 {
8677 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
8678 pTarget->m->autoReset = fAutoReset;
8679 }
8680
8681 /* register with mVirtualBox as the last step and move to
8682 * Created state only on success (leaving an orphan file is
8683 * better than breaking media registry consistency) */
8684 ComObjPtr<Medium> pMedium;
8685 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
8686 Assert(pTarget == pMedium);
8687
8688 if (FAILED(mrc))
8689 /* break the parent association on failure to register */
8690 i_deparent();
8691 }
8692
8693 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8694
8695 if (SUCCEEDED(mrc))
8696 {
8697 pTarget->m->state = MediumState_Created;
8698
8699 pTarget->m->size = size;
8700 pTarget->m->logicalSize = logicalSize;
8701 pTarget->m->variant = variant;
8702 }
8703 else
8704 {
8705 /* back to NotCreated on failure */
8706 pTarget->m->state = MediumState_NotCreated;
8707
8708 pTarget->m->autoReset = false;
8709
8710 /* reset UUID to prevent it from being reused next time */
8711 if (fGenerateUuid)
8712 unconst(pTarget->m->id).clear();
8713 }
8714
8715 // deregister the task registered in createDiffStorage()
8716 Assert(m->numCreateDiffTasks != 0);
8717 --m->numCreateDiffTasks;
8718
8719 mediaLock.release();
8720 i_markRegistriesModified();
8721 if (task.isAsync())
8722 {
8723 // in asynchronous mode, save settings now
8724 m->pVirtualBox->i_saveModifiedRegistries();
8725 }
8726
8727 /* Note that in sync mode, it's the caller's responsibility to
8728 * unlock the medium. */
8729
8730 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
8731 {
8732 m->pVirtualBox->i_onMediumConfigChanged(this);
8733 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
8734 }
8735
8736 return mrc;
8737}
8738
8739/**
8740 * Implementation code for the "merge" task.
8741 *
8742 * This task always gets started from Medium::mergeTo() and can run
8743 * synchronously or asynchronously depending on the "wait" parameter passed to
8744 * that function. If we run synchronously, the caller expects the medium
8745 * registry modification to be set before returning; otherwise (in asynchronous
8746 * mode), we save the settings ourselves.
8747 *
8748 * @param task
8749 * @return
8750 */
8751HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
8752{
8753 /** @todo r=klaus The code below needs to be double checked with regard
8754 * to lock order violations, it probably causes lock order issues related
8755 * to the AutoCaller usage. */
8756 HRESULT rcTmp = S_OK;
8757
8758 const ComObjPtr<Medium> &pTarget = task.mTarget;
8759
8760 try
8761 {
8762 if (!task.mParentForTarget.isNull())
8763 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8764 {
8765 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
8766 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8767 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
8768 task.mParentForTarget->m->strLocationFull.c_str());
8769 }
8770
8771 // Resize target to source size, if possible. Otherwise throw an error.
8772 // It's offline resizing. Online resizing will be called in the
8773 // SessionMachine::onlineMergeMedium.
8774
8775 uint64_t sourceSize = 0;
8776 Utf8Str sourceName;
8777 {
8778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8779 sourceSize = i_getLogicalSize();
8780 sourceName = i_getName();
8781 }
8782 uint64_t targetSize = 0;
8783 Utf8Str targetName;
8784 {
8785 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
8786 targetSize = pTarget->i_getLogicalSize();
8787 targetName = pTarget->i_getName();
8788 }
8789
8790 //reducing vm disks are not implemented yet
8791 if (sourceSize > targetSize)
8792 {
8793 if (i_isMediumFormatFile())
8794 {
8795 // Have to make own lock list, because "resize" method resizes only last image
8796 // in the lock chain. The lock chain already in the task.mpMediumLockList, so
8797 // just make new lock list based on it. In fact the own lock list neither makes
8798 // double locking of mediums nor unlocks them during delete, because medium
8799 // already locked by task.mpMediumLockList and own list is used just to specify
8800 // what "resize" method should resize.
8801
8802 MediumLockList* pMediumLockListForResize = new MediumLockList();
8803
8804 for (MediumLockList::Base::iterator it = task.mpMediumLockList->GetBegin();
8805 it != task.mpMediumLockList->GetEnd();
8806 ++it)
8807 {
8808 ComObjPtr<Medium> pMedium = it->GetMedium();
8809 pMediumLockListForResize->Append(pMedium, pMedium->m->state == MediumState_LockedWrite);
8810 if (pMedium == pTarget)
8811 break;
8812 }
8813
8814 // just to switch internal state of the lock list to avoid errors during list deletion,
8815 // because all meduims in the list already locked by task.mpMediumLockList
8816 HRESULT rc = pMediumLockListForResize->Lock(true /* fSkipOverLockedMedia */);
8817 if (FAILED(rc))
8818 {
8819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8820 rc = setError(rc,
8821 tr("Failed to lock the medium '%s' to resize before merge"),
8822 targetName.c_str());
8823 delete pMediumLockListForResize;
8824 throw rc;
8825 }
8826
8827 ComObjPtr<Progress> pProgress(task.GetProgressObject());
8828 rc = pTarget->i_resize(sourceSize, pMediumLockListForResize, &pProgress, true, false);
8829 if (FAILED(rc))
8830 {
8831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8832 rc = setError(rc,
8833 tr("Failed to set size of '%s' to size of '%s'"),
8834 targetName.c_str(), sourceName.c_str());
8835 delete pMediumLockListForResize;
8836 throw rc;
8837 }
8838 delete pMediumLockListForResize;
8839 }
8840 else
8841 {
8842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8843 HRESULT rc = setError(VBOX_E_NOT_SUPPORTED,
8844 tr("Sizes of '%s' and '%s' are different and medium format does not support resing"),
8845 sourceName.c_str(), targetName.c_str());
8846 throw rc;
8847 }
8848 }
8849
8850 task.GetProgressObject()->SetNextOperation(BstrFmt(tr("Merging medium '%s' to '%s'"),
8851 i_getName().c_str(),
8852 targetName.c_str()).raw(),
8853 1);
8854
8855 PVDISK hdd;
8856 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8857 ComAssertRCThrow(vrc, E_FAIL);
8858
8859 try
8860 {
8861 // Similar code appears in SessionMachine::onlineMergeMedium, so
8862 // if you make any changes below check whether they are applicable
8863 // in that context as well.
8864
8865 unsigned uTargetIdx = VD_LAST_IMAGE;
8866 unsigned uSourceIdx = VD_LAST_IMAGE;
8867 /* Open all media in the chain. */
8868 MediumLockList::Base::iterator lockListBegin =
8869 task.mpMediumLockList->GetBegin();
8870 MediumLockList::Base::iterator lockListEnd =
8871 task.mpMediumLockList->GetEnd();
8872 unsigned i = 0;
8873 for (MediumLockList::Base::iterator it = lockListBegin;
8874 it != lockListEnd;
8875 ++it)
8876 {
8877 MediumLock &mediumLock = *it;
8878 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8879
8880 if (pMedium == this)
8881 uSourceIdx = i;
8882 else if (pMedium == pTarget)
8883 uTargetIdx = i;
8884
8885 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8886
8887 /*
8888 * complex sanity (sane complexity)
8889 *
8890 * The current medium must be in the Deleting (medium is merged)
8891 * or LockedRead (parent medium) state if it is not the target.
8892 * If it is the target it must be in the LockedWrite state.
8893 */
8894 Assert( ( pMedium != pTarget
8895 && ( pMedium->m->state == MediumState_Deleting
8896 || pMedium->m->state == MediumState_LockedRead))
8897 || ( pMedium == pTarget
8898 && pMedium->m->state == MediumState_LockedWrite));
8899 /*
8900 * Medium must be the target, in the LockedRead state
8901 * or Deleting state where it is not allowed to be attached
8902 * to a virtual machine.
8903 */
8904 Assert( pMedium == pTarget
8905 || pMedium->m->state == MediumState_LockedRead
8906 || ( pMedium->m->backRefs.size() == 0
8907 && pMedium->m->state == MediumState_Deleting));
8908 /* The source medium must be in Deleting state. */
8909 Assert( pMedium != this
8910 || pMedium->m->state == MediumState_Deleting);
8911
8912 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8913
8914 if ( pMedium->m->state == MediumState_LockedRead
8915 || pMedium->m->state == MediumState_Deleting)
8916 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8917 if (pMedium->m->type == MediumType_Shareable)
8918 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8919
8920 /* Open the medium */
8921 vrc = VDOpen(hdd,
8922 pMedium->m->strFormat.c_str(),
8923 pMedium->m->strLocationFull.c_str(),
8924 uOpenFlags | m->uOpenFlagsDef,
8925 pMedium->m->vdImageIfaces);
8926 if (RT_FAILURE(vrc))
8927 throw vrc;
8928
8929 i++;
8930 }
8931
8932 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
8933 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
8934
8935 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
8936 task.mVDOperationIfaces);
8937 if (RT_FAILURE(vrc))
8938 throw vrc;
8939
8940 /* update parent UUIDs */
8941 if (!task.mfMergeForward)
8942 {
8943 /* we need to update UUIDs of all source's children
8944 * which cannot be part of the container at once so
8945 * add each one in there individually */
8946 if (task.mpChildrenToReparent)
8947 {
8948 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
8949 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
8950 for (MediumLockList::Base::iterator it = childrenBegin;
8951 it != childrenEnd;
8952 ++it)
8953 {
8954 Medium *pMedium = it->GetMedium();
8955 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
8956 vrc = VDOpen(hdd,
8957 pMedium->m->strFormat.c_str(),
8958 pMedium->m->strLocationFull.c_str(),
8959 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8960 pMedium->m->vdImageIfaces);
8961 if (RT_FAILURE(vrc))
8962 throw vrc;
8963
8964 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
8965 pTarget->m->id.raw());
8966 if (RT_FAILURE(vrc))
8967 throw vrc;
8968
8969 vrc = VDClose(hdd, false /* fDelete */);
8970 if (RT_FAILURE(vrc))
8971 throw vrc;
8972 }
8973 }
8974 }
8975 }
8976 catch (HRESULT aRC) { rcTmp = aRC; }
8977 catch (int aVRC)
8978 {
8979 rcTmp = setErrorBoth(VBOX_E_FILE_ERROR, aVRC,
8980 tr("Could not merge the medium '%s' to '%s'%s"),
8981 m->strLocationFull.c_str(),
8982 pTarget->m->strLocationFull.c_str(),
8983 i_vdError(aVRC).c_str());
8984 }
8985
8986 VDDestroy(hdd);
8987 }
8988 catch (HRESULT aRC) { rcTmp = aRC; }
8989
8990 ErrorInfoKeeper eik;
8991 MultiResult mrc(rcTmp);
8992 HRESULT rc2;
8993
8994 std::set<ComObjPtr<Medium> > pMediumsForNotify;
8995 std::map<Guid, DeviceType_T> uIdsForNotify;
8996
8997 if (SUCCEEDED(mrc))
8998 {
8999 /* all media but the target were successfully deleted by
9000 * VDMerge; reparent the last one and uninitialize deleted media. */
9001
9002 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9003
9004 if (task.mfMergeForward)
9005 {
9006 /* first, unregister the target since it may become a base
9007 * medium which needs re-registration */
9008 rc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
9009 AssertComRC(rc2);
9010
9011 /* then, reparent it and disconnect the deleted branch at both ends
9012 * (chain->parent() is source's parent). Depth check above. */
9013 pTarget->i_deparent();
9014 pTarget->i_setParent(task.mParentForTarget);
9015 if (task.mParentForTarget)
9016 {
9017 i_deparent();
9018 if (task.NotifyAboutChanges())
9019 pMediumsForNotify.insert(task.mParentForTarget);
9020 }
9021
9022 /* then, register again */
9023 ComObjPtr<Medium> pMedium;
9024 rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9025 treeLock);
9026 AssertComRC(rc2);
9027 }
9028 else
9029 {
9030 Assert(pTarget->i_getChildren().size() == 1);
9031 Medium *targetChild = pTarget->i_getChildren().front();
9032
9033 /* disconnect the deleted branch at the elder end */
9034 targetChild->i_deparent();
9035
9036 /* reparent source's children and disconnect the deleted
9037 * branch at the younger end */
9038 if (task.mpChildrenToReparent)
9039 {
9040 /* obey {parent,child} lock order */
9041 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
9042
9043 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9044 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9045 for (MediumLockList::Base::iterator it = childrenBegin;
9046 it != childrenEnd;
9047 ++it)
9048 {
9049 Medium *pMedium = it->GetMedium();
9050 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
9051
9052 pMedium->i_deparent(); // removes pMedium from source
9053 // no depth check, reduces depth
9054 pMedium->i_setParent(pTarget);
9055
9056 if (task.NotifyAboutChanges())
9057 pMediumsForNotify.insert(pMedium);
9058 }
9059 }
9060 pMediumsForNotify.insert(pTarget);
9061 }
9062
9063 /* unregister and uninitialize all media removed by the merge */
9064 MediumLockList::Base::iterator lockListBegin =
9065 task.mpMediumLockList->GetBegin();
9066 MediumLockList::Base::iterator lockListEnd =
9067 task.mpMediumLockList->GetEnd();
9068 for (MediumLockList::Base::iterator it = lockListBegin;
9069 it != lockListEnd;
9070 )
9071 {
9072 MediumLock &mediumLock = *it;
9073 /* Create a real copy of the medium pointer, as the medium
9074 * lock deletion below would invalidate the referenced object. */
9075 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
9076
9077 /* The target and all media not merged (readonly) are skipped */
9078 if ( pMedium == pTarget
9079 || pMedium->m->state == MediumState_LockedRead)
9080 {
9081 ++it;
9082 continue;
9083 }
9084
9085 uIdsForNotify[pMedium->i_getId()] = pMedium->i_getDeviceType();
9086 rc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
9087 AssertComRC(rc2);
9088
9089 /* now, uninitialize the deleted medium (note that
9090 * due to the Deleting state, uninit() will not touch
9091 * the parent-child relationship so we need to
9092 * uninitialize each disk individually) */
9093
9094 /* note that the operation initiator medium (which is
9095 * normally also the source medium) is a special case
9096 * -- there is one more caller added by Task to it which
9097 * we must release. Also, if we are in sync mode, the
9098 * caller may still hold an AutoCaller instance for it
9099 * and therefore we cannot uninit() it (it's therefore
9100 * the caller's responsibility) */
9101 if (pMedium == this)
9102 {
9103 Assert(i_getChildren().size() == 0);
9104 Assert(m->backRefs.size() == 0);
9105 task.mMediumCaller.release();
9106 }
9107
9108 /* Delete the medium lock list entry, which also releases the
9109 * caller added by MergeChain before uninit() and updates the
9110 * iterator to point to the right place. */
9111 rc2 = task.mpMediumLockList->RemoveByIterator(it);
9112 AssertComRC(rc2);
9113
9114 if (task.isAsync() || pMedium != this)
9115 {
9116 treeLock.release();
9117 pMedium->uninit();
9118 treeLock.acquire();
9119 }
9120 }
9121 }
9122
9123 i_markRegistriesModified();
9124 if (task.isAsync())
9125 {
9126 // in asynchronous mode, save settings now
9127 eik.restore();
9128 m->pVirtualBox->i_saveModifiedRegistries();
9129 eik.fetch();
9130 }
9131
9132 if (FAILED(mrc))
9133 {
9134 /* Here we come if either VDMerge() failed (in which case we
9135 * assume that it tried to do everything to make a further
9136 * retry possible -- e.g. not deleted intermediate media
9137 * and so on) or VirtualBox::saveRegistries() failed (where we
9138 * should have the original tree but with intermediate storage
9139 * units deleted by VDMerge()). We have to only restore states
9140 * (through the MergeChain dtor) unless we are run synchronously
9141 * in which case it's the responsibility of the caller as stated
9142 * in the mergeTo() docs. The latter also implies that we
9143 * don't own the merge chain, so release it in this case. */
9144 if (task.isAsync())
9145 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
9146 }
9147 else if (task.NotifyAboutChanges())
9148 {
9149 for (std::set<ComObjPtr<Medium> >::const_iterator it = pMediumsForNotify.begin();
9150 it != pMediumsForNotify.end();
9151 ++it)
9152 {
9153 if (it->isNotNull())
9154 m->pVirtualBox->i_onMediumConfigChanged(*it);
9155 }
9156 for (std::map<Guid, DeviceType_T>::const_iterator it = uIdsForNotify.begin();
9157 it != uIdsForNotify.end();
9158 ++it)
9159 {
9160 m->pVirtualBox->i_onMediumRegistered(it->first, it->second, FALSE);
9161 }
9162 }
9163
9164 return mrc;
9165}
9166
9167/**
9168 * Implementation code for the "clone" task.
9169 *
9170 * This only gets started from Medium::CloneTo() and always runs asynchronously.
9171 * As a result, we always save the VirtualBox.xml file when we're done here.
9172 *
9173 * @param task
9174 * @return
9175 */
9176HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
9177{
9178 /** @todo r=klaus The code below needs to be double checked with regard
9179 * to lock order violations, it probably causes lock order issues related
9180 * to the AutoCaller usage. */
9181 HRESULT rcTmp = S_OK;
9182
9183 const ComObjPtr<Medium> &pTarget = task.mTarget;
9184 const ComObjPtr<Medium> &pParent = task.mParent;
9185
9186 bool fCreatingTarget = false;
9187
9188 uint64_t size = 0, logicalSize = 0;
9189 MediumVariant_T variant = MediumVariant_Standard;
9190 bool fGenerateUuid = false;
9191
9192 try
9193 {
9194 if (!pParent.isNull())
9195 {
9196
9197 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9198 {
9199 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
9200 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9201 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9202 pParent->m->strLocationFull.c_str());
9203 }
9204 }
9205
9206 /* Lock all in {parent,child} order. The lock is also used as a
9207 * signal from the task initiator (which releases it only after
9208 * RTThreadCreate()) that we can start the job. */
9209 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
9210
9211 fCreatingTarget = pTarget->m->state == MediumState_Creating;
9212
9213 /* The object may request a specific UUID (through a special form of
9214 * the moveTo() argument). Otherwise we have to generate it */
9215 Guid targetId = pTarget->m->id;
9216
9217 fGenerateUuid = targetId.isZero();
9218 if (fGenerateUuid)
9219 {
9220 targetId.create();
9221 /* VirtualBox::registerMedium() will need UUID */
9222 unconst(pTarget->m->id) = targetId;
9223 }
9224
9225 PVDISK hdd;
9226 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9227 ComAssertRCThrow(vrc, E_FAIL);
9228
9229 try
9230 {
9231 /* Open all media in the source chain. */
9232 MediumLockList::Base::const_iterator sourceListBegin =
9233 task.mpSourceMediumLockList->GetBegin();
9234 MediumLockList::Base::const_iterator sourceListEnd =
9235 task.mpSourceMediumLockList->GetEnd();
9236 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9237 it != sourceListEnd;
9238 ++it)
9239 {
9240 const MediumLock &mediumLock = *it;
9241 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9242 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9243
9244 /* sanity check */
9245 Assert(pMedium->m->state == MediumState_LockedRead);
9246
9247 /** Open all media in read-only mode. */
9248 vrc = VDOpen(hdd,
9249 pMedium->m->strFormat.c_str(),
9250 pMedium->m->strLocationFull.c_str(),
9251 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9252 pMedium->m->vdImageIfaces);
9253 if (RT_FAILURE(vrc))
9254 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9255 tr("Could not open the medium storage unit '%s'%s"),
9256 pMedium->m->strLocationFull.c_str(),
9257 i_vdError(vrc).c_str());
9258 }
9259
9260 Utf8Str targetFormat(pTarget->m->strFormat);
9261 Utf8Str targetLocation(pTarget->m->strLocationFull);
9262 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
9263
9264 Assert( pTarget->m->state == MediumState_Creating
9265 || pTarget->m->state == MediumState_LockedWrite);
9266 Assert(m->state == MediumState_LockedRead);
9267 Assert( pParent.isNull()
9268 || pParent->m->state == MediumState_LockedRead);
9269
9270 /* unlock before the potentially lengthy operation */
9271 thisLock.release();
9272
9273 /* ensure the target directory exists */
9274 if (capabilities & MediumFormatCapabilities_File)
9275 {
9276 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9277 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9278 if (FAILED(rc))
9279 throw rc;
9280 }
9281
9282 PVDISK targetHdd;
9283 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9284 ComAssertRCThrow(vrc, E_FAIL);
9285
9286 try
9287 {
9288 /* Open all media in the target chain. */
9289 MediumLockList::Base::const_iterator targetListBegin =
9290 task.mpTargetMediumLockList->GetBegin();
9291 MediumLockList::Base::const_iterator targetListEnd =
9292 task.mpTargetMediumLockList->GetEnd();
9293 for (MediumLockList::Base::const_iterator it = targetListBegin;
9294 it != targetListEnd;
9295 ++it)
9296 {
9297 const MediumLock &mediumLock = *it;
9298 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9299
9300 /* If the target medium is not created yet there's no
9301 * reason to open it. */
9302 if (pMedium == pTarget && fCreatingTarget)
9303 continue;
9304
9305 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9306
9307 /* sanity check */
9308 Assert( pMedium->m->state == MediumState_LockedRead
9309 || pMedium->m->state == MediumState_LockedWrite);
9310
9311 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9312 if (pMedium->m->state != MediumState_LockedWrite)
9313 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9314 if (pMedium->m->type == MediumType_Shareable)
9315 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9316
9317 /* Open all media in appropriate mode. */
9318 vrc = VDOpen(targetHdd,
9319 pMedium->m->strFormat.c_str(),
9320 pMedium->m->strLocationFull.c_str(),
9321 uOpenFlags | m->uOpenFlagsDef,
9322 pMedium->m->vdImageIfaces);
9323 if (RT_FAILURE(vrc))
9324 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9325 tr("Could not open the medium storage unit '%s'%s"),
9326 pMedium->m->strLocationFull.c_str(),
9327 i_vdError(vrc).c_str());
9328 }
9329
9330 /* target isn't locked, but no changing data is accessed */
9331 if (task.midxSrcImageSame == UINT32_MAX)
9332 {
9333 vrc = VDCopy(hdd,
9334 VD_LAST_IMAGE,
9335 targetHdd,
9336 targetFormat.c_str(),
9337 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9338 false /* fMoveByRename */,
9339 0 /* cbSize */,
9340 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
9341 targetId.raw(),
9342 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9343 NULL /* pVDIfsOperation */,
9344 pTarget->m->vdImageIfaces,
9345 task.mVDOperationIfaces);
9346 }
9347 else
9348 {
9349 vrc = VDCopyEx(hdd,
9350 VD_LAST_IMAGE,
9351 targetHdd,
9352 targetFormat.c_str(),
9353 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9354 false /* fMoveByRename */,
9355 0 /* cbSize */,
9356 task.midxSrcImageSame,
9357 task.midxDstImageSame,
9358 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
9359 targetId.raw(),
9360 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9361 NULL /* pVDIfsOperation */,
9362 pTarget->m->vdImageIfaces,
9363 task.mVDOperationIfaces);
9364 }
9365 if (RT_FAILURE(vrc))
9366 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9367 tr("Could not create the clone medium '%s'%s"),
9368 targetLocation.c_str(), i_vdError(vrc).c_str());
9369
9370 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
9371 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
9372 unsigned uImageFlags;
9373 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
9374 if (RT_SUCCESS(vrc))
9375 variant = (MediumVariant_T)uImageFlags;
9376 }
9377 catch (HRESULT aRC) { rcTmp = aRC; }
9378
9379 VDDestroy(targetHdd);
9380 }
9381 catch (HRESULT aRC) { rcTmp = aRC; }
9382
9383 VDDestroy(hdd);
9384 }
9385 catch (HRESULT aRC) { rcTmp = aRC; }
9386
9387 ErrorInfoKeeper eik;
9388 MultiResult mrc(rcTmp);
9389
9390 /* Only do the parent changes for newly created media. */
9391 if (SUCCEEDED(mrc) && fCreatingTarget)
9392 {
9393 /* we set m->pParent & children() */
9394 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9395
9396 Assert(pTarget->m->pParent.isNull());
9397
9398 if (pParent)
9399 {
9400 /* Associate the clone with the parent and deassociate
9401 * from VirtualBox. Depth check above. */
9402 pTarget->i_setParent(pParent);
9403
9404 /* register with mVirtualBox as the last step and move to
9405 * Created state only on success (leaving an orphan file is
9406 * better than breaking media registry consistency) */
9407 eik.restore();
9408 ComObjPtr<Medium> pMedium;
9409 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9410 treeLock);
9411 Assert( FAILED(mrc)
9412 || pTarget == pMedium);
9413 eik.fetch();
9414
9415 if (FAILED(mrc))
9416 /* break parent association on failure to register */
9417 pTarget->i_deparent(); // removes target from parent
9418 }
9419 else
9420 {
9421 /* just register */
9422 eik.restore();
9423 ComObjPtr<Medium> pMedium;
9424 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9425 treeLock);
9426 Assert( FAILED(mrc)
9427 || pTarget == pMedium);
9428 eik.fetch();
9429 }
9430 }
9431
9432 if (fCreatingTarget)
9433 {
9434 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
9435
9436 if (SUCCEEDED(mrc))
9437 {
9438 pTarget->m->state = MediumState_Created;
9439
9440 pTarget->m->size = size;
9441 pTarget->m->logicalSize = logicalSize;
9442 pTarget->m->variant = variant;
9443 }
9444 else
9445 {
9446 /* back to NotCreated on failure */
9447 pTarget->m->state = MediumState_NotCreated;
9448
9449 /* reset UUID to prevent it from being reused next time */
9450 if (fGenerateUuid)
9451 unconst(pTarget->m->id).clear();
9452 }
9453 }
9454
9455 /* Copy any filter related settings over to the target. */
9456 if (SUCCEEDED(mrc))
9457 {
9458 /* Copy any filter related settings over. */
9459 ComObjPtr<Medium> pBase = i_getBase();
9460 ComObjPtr<Medium> pTargetBase = pTarget->i_getBase();
9461 std::vector<com::Utf8Str> aFilterPropNames;
9462 std::vector<com::Utf8Str> aFilterPropValues;
9463 mrc = pBase->i_getFilterProperties(aFilterPropNames, aFilterPropValues);
9464 if (SUCCEEDED(mrc))
9465 {
9466 /* Go through the properties and add them to the target medium. */
9467 for (unsigned idx = 0; idx < aFilterPropNames.size(); idx++)
9468 {
9469 mrc = pTargetBase->i_setPropertyDirect(aFilterPropNames[idx], aFilterPropValues[idx]);
9470 if (FAILED(mrc)) break;
9471 }
9472
9473 // now, at the end of this task (always asynchronous), save the settings
9474 if (SUCCEEDED(mrc))
9475 {
9476 // save the settings
9477 i_markRegistriesModified();
9478 /* collect multiple errors */
9479 eik.restore();
9480 m->pVirtualBox->i_saveModifiedRegistries();
9481 eik.fetch();
9482
9483 if (task.NotifyAboutChanges())
9484 {
9485 if (!fCreatingTarget)
9486 {
9487 if (!aFilterPropNames.empty())
9488 m->pVirtualBox->i_onMediumConfigChanged(pTargetBase);
9489 if (pParent)
9490 m->pVirtualBox->i_onMediumConfigChanged(pParent);
9491 }
9492 else
9493 {
9494 m->pVirtualBox->i_onMediumRegistered(pTarget->i_getId(), pTarget->i_getDeviceType(), TRUE);
9495 }
9496 }
9497 }
9498 }
9499 }
9500
9501 /* Everything is explicitly unlocked when the task exits,
9502 * as the task destruction also destroys the source chain. */
9503
9504 /* Make sure the source chain is released early. It could happen
9505 * that we get a deadlock in Appliance::Import when Medium::Close
9506 * is called & the source chain is released at the same time. */
9507 task.mpSourceMediumLockList->Clear();
9508
9509 return mrc;
9510}
9511
9512/**
9513 * Implementation code for the "move" task.
9514 *
9515 * This only gets started from Medium::MoveTo() and always
9516 * runs asynchronously.
9517 *
9518 * @param task
9519 * @return
9520 */
9521HRESULT Medium::i_taskMoveHandler(Medium::MoveTask &task)
9522{
9523 LogFlowFuncEnter();
9524 HRESULT rcOut = S_OK;
9525
9526 /* pTarget is equal "this" in our case */
9527 const ComObjPtr<Medium> &pTarget = task.mMedium;
9528
9529 uint64_t size = 0; NOREF(size);
9530 uint64_t logicalSize = 0; NOREF(logicalSize);
9531 MediumVariant_T variant = MediumVariant_Standard; NOREF(variant);
9532
9533 /*
9534 * it's exactly moving, not cloning
9535 */
9536 if (!i_isMoveOperation(pTarget))
9537 {
9538 HRESULT rc = setError(VBOX_E_FILE_ERROR,
9539 tr("Wrong preconditions for moving the medium %s"),
9540 pTarget->m->strLocationFull.c_str());
9541 LogFlowFunc(("LEAVE: rc=%Rhrc (early)\n", rc));
9542 return rc;
9543 }
9544
9545 try
9546 {
9547 /* Lock all in {parent,child} order. The lock is also used as a
9548 * signal from the task initiator (which releases it only after
9549 * RTThreadCreate()) that we can start the job. */
9550
9551 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9552
9553 PVDISK hdd;
9554 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9555 ComAssertRCThrow(vrc, E_FAIL);
9556
9557 try
9558 {
9559 /* Open all media in the source chain. */
9560 MediumLockList::Base::const_iterator sourceListBegin =
9561 task.mpMediumLockList->GetBegin();
9562 MediumLockList::Base::const_iterator sourceListEnd =
9563 task.mpMediumLockList->GetEnd();
9564 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9565 it != sourceListEnd;
9566 ++it)
9567 {
9568 const MediumLock &mediumLock = *it;
9569 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9570 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9571
9572 /* sanity check */
9573 Assert(pMedium->m->state == MediumState_LockedWrite);
9574
9575 vrc = VDOpen(hdd,
9576 pMedium->m->strFormat.c_str(),
9577 pMedium->m->strLocationFull.c_str(),
9578 VD_OPEN_FLAGS_NORMAL,
9579 pMedium->m->vdImageIfaces);
9580 if (RT_FAILURE(vrc))
9581 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9582 tr("Could not open the medium storage unit '%s'%s"),
9583 pMedium->m->strLocationFull.c_str(),
9584 i_vdError(vrc).c_str());
9585 }
9586
9587 /* we can directly use pTarget->m->"variables" but for better reading we use local copies */
9588 Guid targetId = pTarget->m->id;
9589 Utf8Str targetFormat(pTarget->m->strFormat);
9590 uint64_t targetCapabilities = pTarget->m->formatObj->i_getCapabilities();
9591
9592 /*
9593 * change target location
9594 * m->strNewLocationFull has been set already together with m->fMoveThisMedium in
9595 * i_preparationForMoving()
9596 */
9597 Utf8Str targetLocation = i_getNewLocationForMoving();
9598
9599 /* unlock before the potentially lengthy operation */
9600 thisLock.release();
9601
9602 /* ensure the target directory exists */
9603 if (targetCapabilities & MediumFormatCapabilities_File)
9604 {
9605 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9606 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9607 if (FAILED(rc))
9608 throw rc;
9609 }
9610
9611 try
9612 {
9613 vrc = VDCopy(hdd,
9614 VD_LAST_IMAGE,
9615 hdd,
9616 targetFormat.c_str(),
9617 targetLocation.c_str(),
9618 true /* fMoveByRename */,
9619 0 /* cbSize */,
9620 VD_IMAGE_FLAGS_NONE,
9621 targetId.raw(),
9622 VD_OPEN_FLAGS_NORMAL,
9623 NULL /* pVDIfsOperation */,
9624 NULL,
9625 NULL);
9626 if (RT_FAILURE(vrc))
9627 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9628 tr("Could not move medium '%s'%s"),
9629 targetLocation.c_str(), i_vdError(vrc).c_str());
9630 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9631 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9632 unsigned uImageFlags;
9633 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9634 if (RT_SUCCESS(vrc))
9635 variant = (MediumVariant_T)uImageFlags;
9636
9637 /*
9638 * set current location, because VDCopy\VDCopyEx doesn't do it.
9639 * also reset moving flag
9640 */
9641 i_resetMoveOperationData();
9642 m->strLocationFull = targetLocation;
9643
9644 }
9645 catch (HRESULT aRC) { rcOut = aRC; }
9646
9647 }
9648 catch (HRESULT aRC) { rcOut = aRC; }
9649
9650 VDDestroy(hdd);
9651 }
9652 catch (HRESULT aRC) { rcOut = aRC; }
9653
9654 ErrorInfoKeeper eik;
9655 MultiResult mrc(rcOut);
9656
9657 // now, at the end of this task (always asynchronous), save the settings
9658 if (SUCCEEDED(mrc))
9659 {
9660 // save the settings
9661 i_markRegistriesModified();
9662 /* collect multiple errors */
9663 eik.restore();
9664 m->pVirtualBox->i_saveModifiedRegistries();
9665 eik.fetch();
9666 }
9667
9668 /* Everything is explicitly unlocked when the task exits,
9669 * as the task destruction also destroys the source chain. */
9670
9671 task.mpMediumLockList->Clear();
9672
9673 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
9674 m->pVirtualBox->i_onMediumConfigChanged(this);
9675
9676 LogFlowFunc(("LEAVE: mrc=%Rhrc\n", (HRESULT)mrc));
9677 return mrc;
9678}
9679
9680/**
9681 * Implementation code for the "delete" task.
9682 *
9683 * This task always gets started from Medium::deleteStorage() and can run
9684 * synchronously or asynchronously depending on the "wait" parameter passed to
9685 * that function.
9686 *
9687 * @param task
9688 * @return
9689 */
9690HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
9691{
9692 NOREF(task);
9693 HRESULT rc = S_OK;
9694
9695 try
9696 {
9697 /* The lock is also used as a signal from the task initiator (which
9698 * releases it only after RTThreadCreate()) that we can start the job */
9699 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9700
9701 PVDISK hdd;
9702 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9703 ComAssertRCThrow(vrc, E_FAIL);
9704
9705 Utf8Str format(m->strFormat);
9706 Utf8Str location(m->strLocationFull);
9707
9708 /* unlock before the potentially lengthy operation */
9709 Assert(m->state == MediumState_Deleting);
9710 thisLock.release();
9711
9712 try
9713 {
9714 vrc = VDOpen(hdd,
9715 format.c_str(),
9716 location.c_str(),
9717 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9718 m->vdImageIfaces);
9719 if (RT_SUCCESS(vrc))
9720 vrc = VDClose(hdd, true /* fDelete */);
9721
9722 if (RT_FAILURE(vrc) && vrc != VERR_FILE_NOT_FOUND)
9723 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9724 tr("Could not delete the medium storage unit '%s'%s"),
9725 location.c_str(), i_vdError(vrc).c_str());
9726
9727 }
9728 catch (HRESULT aRC) { rc = aRC; }
9729
9730 VDDestroy(hdd);
9731 }
9732 catch (HRESULT aRC) { rc = aRC; }
9733
9734 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9735
9736 /* go to the NotCreated state even on failure since the storage
9737 * may have been already partially deleted and cannot be used any
9738 * more. One will be able to manually re-open the storage if really
9739 * needed to re-register it. */
9740 m->state = MediumState_NotCreated;
9741
9742 /* Reset UUID to prevent Create* from reusing it again */
9743 com::Guid uOldId = m->id;
9744 unconst(m->id).clear();
9745
9746 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
9747 {
9748 if (m->pParent.isNotNull())
9749 m->pVirtualBox->i_onMediumConfigChanged(m->pParent);
9750 m->pVirtualBox->i_onMediumRegistered(uOldId, m->devType, FALSE);
9751 }
9752
9753 return rc;
9754}
9755
9756/**
9757 * Implementation code for the "reset" task.
9758 *
9759 * This always gets started asynchronously from Medium::Reset().
9760 *
9761 * @param task
9762 * @return
9763 */
9764HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
9765{
9766 HRESULT rc = S_OK;
9767
9768 uint64_t size = 0, logicalSize = 0;
9769 MediumVariant_T variant = MediumVariant_Standard;
9770
9771 try
9772 {
9773 /* The lock is also used as a signal from the task initiator (which
9774 * releases it only after RTThreadCreate()) that we can start the job */
9775 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9776
9777 /// @todo Below we use a pair of delete/create operations to reset
9778 /// the diff contents but the most efficient way will of course be
9779 /// to add a VDResetDiff() API call
9780
9781 PVDISK hdd;
9782 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9783 ComAssertRCThrow(vrc, E_FAIL);
9784
9785 Guid id = m->id;
9786 Utf8Str format(m->strFormat);
9787 Utf8Str location(m->strLocationFull);
9788
9789 Medium *pParent = m->pParent;
9790 Guid parentId = pParent->m->id;
9791 Utf8Str parentFormat(pParent->m->strFormat);
9792 Utf8Str parentLocation(pParent->m->strLocationFull);
9793
9794 Assert(m->state == MediumState_LockedWrite);
9795
9796 /* unlock before the potentially lengthy operation */
9797 thisLock.release();
9798
9799 try
9800 {
9801 /* Open all media in the target chain but the last. */
9802 MediumLockList::Base::const_iterator targetListBegin =
9803 task.mpMediumLockList->GetBegin();
9804 MediumLockList::Base::const_iterator targetListEnd =
9805 task.mpMediumLockList->GetEnd();
9806 for (MediumLockList::Base::const_iterator it = targetListBegin;
9807 it != targetListEnd;
9808 ++it)
9809 {
9810 const MediumLock &mediumLock = *it;
9811 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9812
9813 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9814
9815 /* sanity check, "this" is checked above */
9816 Assert( pMedium == this
9817 || pMedium->m->state == MediumState_LockedRead);
9818
9819 /* Open all media in appropriate mode. */
9820 vrc = VDOpen(hdd,
9821 pMedium->m->strFormat.c_str(),
9822 pMedium->m->strLocationFull.c_str(),
9823 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9824 pMedium->m->vdImageIfaces);
9825 if (RT_FAILURE(vrc))
9826 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9827 tr("Could not open the medium storage unit '%s'%s"),
9828 pMedium->m->strLocationFull.c_str(),
9829 i_vdError(vrc).c_str());
9830
9831 /* Done when we hit the media which should be reset */
9832 if (pMedium == this)
9833 break;
9834 }
9835
9836 /* first, delete the storage unit */
9837 vrc = VDClose(hdd, true /* fDelete */);
9838 if (RT_FAILURE(vrc))
9839 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9840 tr("Could not delete the medium storage unit '%s'%s"),
9841 location.c_str(), i_vdError(vrc).c_str());
9842
9843 /* next, create it again */
9844 vrc = VDOpen(hdd,
9845 parentFormat.c_str(),
9846 parentLocation.c_str(),
9847 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9848 m->vdImageIfaces);
9849 if (RT_FAILURE(vrc))
9850 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9851 tr("Could not open the medium storage unit '%s'%s"),
9852 parentLocation.c_str(), i_vdError(vrc).c_str());
9853
9854 vrc = VDCreateDiff(hdd,
9855 format.c_str(),
9856 location.c_str(),
9857 /// @todo use the same medium variant as before
9858 VD_IMAGE_FLAGS_NONE,
9859 NULL,
9860 id.raw(),
9861 parentId.raw(),
9862 VD_OPEN_FLAGS_NORMAL,
9863 m->vdImageIfaces,
9864 task.mVDOperationIfaces);
9865 if (RT_FAILURE(vrc))
9866 {
9867 if (vrc == VERR_VD_INVALID_TYPE)
9868 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9869 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
9870 location.c_str(), i_vdError(vrc).c_str());
9871 else
9872 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9873 tr("Could not create the differencing medium storage unit '%s'%s"),
9874 location.c_str(), i_vdError(vrc).c_str());
9875 }
9876
9877 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9878 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9879 unsigned uImageFlags;
9880 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9881 if (RT_SUCCESS(vrc))
9882 variant = (MediumVariant_T)uImageFlags;
9883 }
9884 catch (HRESULT aRC) { rc = aRC; }
9885
9886 VDDestroy(hdd);
9887 }
9888 catch (HRESULT aRC) { rc = aRC; }
9889
9890 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9891
9892 m->size = size;
9893 m->logicalSize = logicalSize;
9894 m->variant = variant;
9895
9896 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
9897 m->pVirtualBox->i_onMediumConfigChanged(this);
9898
9899 /* Everything is explicitly unlocked when the task exits,
9900 * as the task destruction also destroys the media chain. */
9901
9902 return rc;
9903}
9904
9905/**
9906 * Implementation code for the "compact" task.
9907 *
9908 * @param task
9909 * @return
9910 */
9911HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
9912{
9913 HRESULT rc = S_OK;
9914
9915 /* Lock all in {parent,child} order. The lock is also used as a
9916 * signal from the task initiator (which releases it only after
9917 * RTThreadCreate()) that we can start the job. */
9918 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9919
9920 try
9921 {
9922 PVDISK hdd;
9923 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9924 ComAssertRCThrow(vrc, E_FAIL);
9925
9926 try
9927 {
9928 /* Open all media in the chain. */
9929 MediumLockList::Base::const_iterator mediumListBegin =
9930 task.mpMediumLockList->GetBegin();
9931 MediumLockList::Base::const_iterator mediumListEnd =
9932 task.mpMediumLockList->GetEnd();
9933 MediumLockList::Base::const_iterator mediumListLast =
9934 mediumListEnd;
9935 --mediumListLast;
9936 for (MediumLockList::Base::const_iterator it = mediumListBegin;
9937 it != mediumListEnd;
9938 ++it)
9939 {
9940 const MediumLock &mediumLock = *it;
9941 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9942 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9943
9944 /* sanity check */
9945 if (it == mediumListLast)
9946 Assert(pMedium->m->state == MediumState_LockedWrite);
9947 else
9948 Assert(pMedium->m->state == MediumState_LockedRead);
9949
9950 /* Open all media but last in read-only mode. Do not handle
9951 * shareable media, as compaction and sharing are mutually
9952 * exclusive. */
9953 vrc = VDOpen(hdd,
9954 pMedium->m->strFormat.c_str(),
9955 pMedium->m->strLocationFull.c_str(),
9956 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
9957 pMedium->m->vdImageIfaces);
9958 if (RT_FAILURE(vrc))
9959 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9960 tr("Could not open the medium storage unit '%s'%s"),
9961 pMedium->m->strLocationFull.c_str(),
9962 i_vdError(vrc).c_str());
9963 }
9964
9965 Assert(m->state == MediumState_LockedWrite);
9966
9967 Utf8Str location(m->strLocationFull);
9968
9969 /* unlock before the potentially lengthy operation */
9970 thisLock.release();
9971
9972 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
9973 if (RT_FAILURE(vrc))
9974 {
9975 if (vrc == VERR_NOT_SUPPORTED)
9976 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
9977 tr("Compacting is not yet supported for medium '%s'"),
9978 location.c_str());
9979 else if (vrc == VERR_NOT_IMPLEMENTED)
9980 throw setErrorBoth(E_NOTIMPL, vrc,
9981 tr("Compacting is not implemented, medium '%s'"),
9982 location.c_str());
9983 else
9984 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9985 tr("Could not compact medium '%s'%s"),
9986 location.c_str(),
9987 i_vdError(vrc).c_str());
9988 }
9989 }
9990 catch (HRESULT aRC) { rc = aRC; }
9991
9992 VDDestroy(hdd);
9993 }
9994 catch (HRESULT aRC) { rc = aRC; }
9995
9996 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
9997 m->pVirtualBox->i_onMediumConfigChanged(this);
9998
9999 /* Everything is explicitly unlocked when the task exits,
10000 * as the task destruction also destroys the media chain. */
10001
10002 return rc;
10003}
10004
10005/**
10006 * Implementation code for the "resize" task.
10007 *
10008 * @param task
10009 * @return
10010 */
10011HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
10012{
10013 HRESULT rc = S_OK;
10014
10015 uint64_t size = 0, logicalSize = 0;
10016
10017 try
10018 {
10019 /* The lock is also used as a signal from the task initiator (which
10020 * releases it only after RTThreadCreate()) that we can start the job */
10021 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10022
10023 PVDISK hdd;
10024 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10025 ComAssertRCThrow(vrc, E_FAIL);
10026
10027 try
10028 {
10029 /* Open all media in the chain. */
10030 MediumLockList::Base::const_iterator mediumListBegin =
10031 task.mpMediumLockList->GetBegin();
10032 MediumLockList::Base::const_iterator mediumListEnd =
10033 task.mpMediumLockList->GetEnd();
10034 MediumLockList::Base::const_iterator mediumListLast =
10035 mediumListEnd;
10036 --mediumListLast;
10037 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10038 it != mediumListEnd;
10039 ++it)
10040 {
10041 const MediumLock &mediumLock = *it;
10042 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10043 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10044
10045 /* sanity check */
10046 if (it == mediumListLast)
10047 Assert(pMedium->m->state == MediumState_LockedWrite);
10048 else
10049 Assert(pMedium->m->state == MediumState_LockedRead ||
10050 // Allow resize the target image during mergeTo in case
10051 // of direction from parent to child because all intermediate
10052 // images are marked to MediumState_Deleting and will be
10053 // destroyed after successful merge
10054 pMedium->m->state == MediumState_Deleting);
10055
10056 /* Open all media but last in read-only mode. Do not handle
10057 * shareable media, as compaction and sharing are mutually
10058 * exclusive. */
10059 vrc = VDOpen(hdd,
10060 pMedium->m->strFormat.c_str(),
10061 pMedium->m->strLocationFull.c_str(),
10062 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10063 pMedium->m->vdImageIfaces);
10064 if (RT_FAILURE(vrc))
10065 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10066 tr("Could not open the medium storage unit '%s'%s"),
10067 pMedium->m->strLocationFull.c_str(),
10068 i_vdError(vrc).c_str());
10069 }
10070
10071 Assert(m->state == MediumState_LockedWrite);
10072
10073 Utf8Str location(m->strLocationFull);
10074
10075 /* unlock before the potentially lengthy operation */
10076 thisLock.release();
10077
10078 VDGEOMETRY geo = {0, 0, 0}; /* auto */
10079 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
10080 if (RT_FAILURE(vrc))
10081 {
10082 if (vrc == VERR_VD_SHRINK_NOT_SUPPORTED)
10083 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10084 tr("Shrinking is not yet supported for medium '%s'"),
10085 location.c_str());
10086 if (vrc == VERR_NOT_SUPPORTED)
10087 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10088 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
10089 task.mSize, location.c_str());
10090 else if (vrc == VERR_NOT_IMPLEMENTED)
10091 throw setErrorBoth(E_NOTIMPL, vrc,
10092 tr("Resiting is not implemented, medium '%s'"),
10093 location.c_str());
10094 else
10095 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10096 tr("Could not resize medium '%s'%s"),
10097 location.c_str(),
10098 i_vdError(vrc).c_str());
10099 }
10100 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10101 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10102 }
10103 catch (HRESULT aRC) { rc = aRC; }
10104
10105 VDDestroy(hdd);
10106 }
10107 catch (HRESULT aRC) { rc = aRC; }
10108
10109 if (SUCCEEDED(rc))
10110 {
10111 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10112 m->size = size;
10113 m->logicalSize = logicalSize;
10114
10115 if (task.NotifyAboutChanges())
10116 m->pVirtualBox->i_onMediumConfigChanged(this);
10117 }
10118
10119 /* Everything is explicitly unlocked when the task exits,
10120 * as the task destruction also destroys the media chain. */
10121
10122 return rc;
10123}
10124
10125/**
10126 * Implementation code for the "import" task.
10127 *
10128 * This only gets started from Medium::importFile() and always runs
10129 * asynchronously. It potentially touches the media registry, so we
10130 * always save the VirtualBox.xml file when we're done here.
10131 *
10132 * @param task
10133 * @return
10134 */
10135HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
10136{
10137 /** @todo r=klaus The code below needs to be double checked with regard
10138 * to lock order violations, it probably causes lock order issues related
10139 * to the AutoCaller usage. */
10140 HRESULT rcTmp = S_OK;
10141
10142 const ComObjPtr<Medium> &pParent = task.mParent;
10143
10144 bool fCreatingTarget = false;
10145
10146 uint64_t size = 0, logicalSize = 0;
10147 MediumVariant_T variant = MediumVariant_Standard;
10148 bool fGenerateUuid = false;
10149
10150 try
10151 {
10152 if (!pParent.isNull())
10153 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
10154 {
10155 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
10156 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10157 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
10158 pParent->m->strLocationFull.c_str());
10159 }
10160
10161 /* Lock all in {parent,child} order. The lock is also used as a
10162 * signal from the task initiator (which releases it only after
10163 * RTThreadCreate()) that we can start the job. */
10164 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
10165
10166 fCreatingTarget = m->state == MediumState_Creating;
10167
10168 /* The object may request a specific UUID (through a special form of
10169 * the moveTo() argument). Otherwise we have to generate it */
10170 Guid targetId = m->id;
10171
10172 fGenerateUuid = targetId.isZero();
10173 if (fGenerateUuid)
10174 {
10175 targetId.create();
10176 /* VirtualBox::i_registerMedium() will need UUID */
10177 unconst(m->id) = targetId;
10178 }
10179
10180
10181 PVDISK hdd;
10182 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10183 ComAssertRCThrow(vrc, E_FAIL);
10184
10185 try
10186 {
10187 /* Open source medium. */
10188 vrc = VDOpen(hdd,
10189 task.mFormat->i_getId().c_str(),
10190 task.mFilename.c_str(),
10191 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
10192 task.mVDImageIfaces);
10193 if (RT_FAILURE(vrc))
10194 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10195 tr("Could not open the medium storage unit '%s'%s"),
10196 task.mFilename.c_str(),
10197 i_vdError(vrc).c_str());
10198
10199 Utf8Str targetFormat(m->strFormat);
10200 Utf8Str targetLocation(m->strLocationFull);
10201 uint64_t capabilities = task.mFormat->i_getCapabilities();
10202
10203 Assert( m->state == MediumState_Creating
10204 || m->state == MediumState_LockedWrite);
10205 Assert( pParent.isNull()
10206 || pParent->m->state == MediumState_LockedRead);
10207
10208 /* unlock before the potentially lengthy operation */
10209 thisLock.release();
10210
10211 /* ensure the target directory exists */
10212 if (capabilities & MediumFormatCapabilities_File)
10213 {
10214 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
10215 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
10216 if (FAILED(rc))
10217 throw rc;
10218 }
10219
10220 PVDISK targetHdd;
10221 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
10222 ComAssertRCThrow(vrc, E_FAIL);
10223
10224 try
10225 {
10226 /* Open all media in the target chain. */
10227 MediumLockList::Base::const_iterator targetListBegin =
10228 task.mpTargetMediumLockList->GetBegin();
10229 MediumLockList::Base::const_iterator targetListEnd =
10230 task.mpTargetMediumLockList->GetEnd();
10231 for (MediumLockList::Base::const_iterator it = targetListBegin;
10232 it != targetListEnd;
10233 ++it)
10234 {
10235 const MediumLock &mediumLock = *it;
10236 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10237
10238 /* If the target medium is not created yet there's no
10239 * reason to open it. */
10240 if (pMedium == this && fCreatingTarget)
10241 continue;
10242
10243 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10244
10245 /* sanity check */
10246 Assert( pMedium->m->state == MediumState_LockedRead
10247 || pMedium->m->state == MediumState_LockedWrite);
10248
10249 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
10250 if (pMedium->m->state != MediumState_LockedWrite)
10251 uOpenFlags = VD_OPEN_FLAGS_READONLY;
10252 if (pMedium->m->type == MediumType_Shareable)
10253 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
10254
10255 /* Open all media in appropriate mode. */
10256 vrc = VDOpen(targetHdd,
10257 pMedium->m->strFormat.c_str(),
10258 pMedium->m->strLocationFull.c_str(),
10259 uOpenFlags | m->uOpenFlagsDef,
10260 pMedium->m->vdImageIfaces);
10261 if (RT_FAILURE(vrc))
10262 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10263 tr("Could not open the medium storage unit '%s'%s"),
10264 pMedium->m->strLocationFull.c_str(),
10265 i_vdError(vrc).c_str());
10266 }
10267
10268 vrc = VDCopy(hdd,
10269 VD_LAST_IMAGE,
10270 targetHdd,
10271 targetFormat.c_str(),
10272 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
10273 false /* fMoveByRename */,
10274 0 /* cbSize */,
10275 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
10276 targetId.raw(),
10277 VD_OPEN_FLAGS_NORMAL,
10278 NULL /* pVDIfsOperation */,
10279 m->vdImageIfaces,
10280 task.mVDOperationIfaces);
10281 if (RT_FAILURE(vrc))
10282 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10283 tr("Could not create the imported medium '%s'%s"),
10284 targetLocation.c_str(), i_vdError(vrc).c_str());
10285
10286 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
10287 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
10288 unsigned uImageFlags;
10289 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
10290 if (RT_SUCCESS(vrc))
10291 variant = (MediumVariant_T)uImageFlags;
10292 }
10293 catch (HRESULT aRC) { rcTmp = aRC; }
10294
10295 VDDestroy(targetHdd);
10296 }
10297 catch (HRESULT aRC) { rcTmp = aRC; }
10298
10299 VDDestroy(hdd);
10300 }
10301 catch (HRESULT aRC) { rcTmp = aRC; }
10302
10303 ErrorInfoKeeper eik;
10304 MultiResult mrc(rcTmp);
10305
10306 /* Only do the parent changes for newly created media. */
10307 if (SUCCEEDED(mrc) && fCreatingTarget)
10308 {
10309 /* we set m->pParent & children() */
10310 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10311
10312 Assert(m->pParent.isNull());
10313
10314 if (pParent)
10315 {
10316 /* Associate the imported medium with the parent and deassociate
10317 * from VirtualBox. Depth check above. */
10318 i_setParent(pParent);
10319
10320 /* register with mVirtualBox as the last step and move to
10321 * Created state only on success (leaving an orphan file is
10322 * better than breaking media registry consistency) */
10323 eik.restore();
10324 ComObjPtr<Medium> pMedium;
10325 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
10326 treeLock);
10327 Assert(this == pMedium);
10328 eik.fetch();
10329
10330 if (FAILED(mrc))
10331 /* break parent association on failure to register */
10332 this->i_deparent(); // removes target from parent
10333 }
10334 else
10335 {
10336 /* just register */
10337 eik.restore();
10338 ComObjPtr<Medium> pMedium;
10339 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
10340 Assert(this == pMedium);
10341 eik.fetch();
10342 }
10343 }
10344
10345 if (fCreatingTarget)
10346 {
10347 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
10348
10349 if (SUCCEEDED(mrc))
10350 {
10351 m->state = MediumState_Created;
10352
10353 m->size = size;
10354 m->logicalSize = logicalSize;
10355 m->variant = variant;
10356 }
10357 else
10358 {
10359 /* back to NotCreated on failure */
10360 m->state = MediumState_NotCreated;
10361
10362 /* reset UUID to prevent it from being reused next time */
10363 if (fGenerateUuid)
10364 unconst(m->id).clear();
10365 }
10366 }
10367
10368 // now, at the end of this task (always asynchronous), save the settings
10369 {
10370 // save the settings
10371 i_markRegistriesModified();
10372 /* collect multiple errors */
10373 eik.restore();
10374 m->pVirtualBox->i_saveModifiedRegistries();
10375 eik.fetch();
10376 }
10377
10378 /* Everything is explicitly unlocked when the task exits,
10379 * as the task destruction also destroys the target chain. */
10380
10381 /* Make sure the target chain is released early, otherwise it can
10382 * lead to deadlocks with concurrent IAppliance activities. */
10383 task.mpTargetMediumLockList->Clear();
10384
10385 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
10386 {
10387 if (pParent)
10388 m->pVirtualBox->i_onMediumConfigChanged(pParent);
10389 if (fCreatingTarget)
10390 m->pVirtualBox->i_onMediumConfigChanged(this);
10391 else
10392 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
10393 }
10394
10395 return mrc;
10396}
10397
10398/**
10399 * Sets up the encryption settings for a filter.
10400 */
10401void Medium::i_taskEncryptSettingsSetup(MediumCryptoFilterSettings *pSettings, const char *pszCipher,
10402 const char *pszKeyStore, const char *pszPassword,
10403 bool fCreateKeyStore)
10404{
10405 pSettings->pszCipher = pszCipher;
10406 pSettings->pszPassword = pszPassword;
10407 pSettings->pszKeyStoreLoad = pszKeyStore;
10408 pSettings->fCreateKeyStore = fCreateKeyStore;
10409 pSettings->pbDek = NULL;
10410 pSettings->cbDek = 0;
10411 pSettings->vdFilterIfaces = NULL;
10412
10413 pSettings->vdIfCfg.pfnAreKeysValid = i_vdCryptoConfigAreKeysValid;
10414 pSettings->vdIfCfg.pfnQuerySize = i_vdCryptoConfigQuerySize;
10415 pSettings->vdIfCfg.pfnQuery = i_vdCryptoConfigQuery;
10416 pSettings->vdIfCfg.pfnQueryBytes = NULL;
10417
10418 pSettings->vdIfCrypto.pfnKeyRetain = i_vdCryptoKeyRetain;
10419 pSettings->vdIfCrypto.pfnKeyRelease = i_vdCryptoKeyRelease;
10420 pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = i_vdCryptoKeyStorePasswordRetain;
10421 pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = i_vdCryptoKeyStorePasswordRelease;
10422 pSettings->vdIfCrypto.pfnKeyStoreSave = i_vdCryptoKeyStoreSave;
10423 pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = i_vdCryptoKeyStoreReturnParameters;
10424
10425 int vrc = VDInterfaceAdd(&pSettings->vdIfCfg.Core,
10426 "Medium::vdInterfaceCfgCrypto",
10427 VDINTERFACETYPE_CONFIG, pSettings,
10428 sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces);
10429 AssertRC(vrc);
10430
10431 vrc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core,
10432 "Medium::vdInterfaceCrypto",
10433 VDINTERFACETYPE_CRYPTO, pSettings,
10434 sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces);
10435 AssertRC(vrc);
10436}
10437
10438/**
10439 * Implementation code for the "encrypt" task.
10440 *
10441 * @param task
10442 * @return
10443 */
10444HRESULT Medium::i_taskEncryptHandler(Medium::EncryptTask &task)
10445{
10446# ifndef VBOX_WITH_EXTPACK
10447 RT_NOREF(task);
10448# endif
10449 HRESULT rc = S_OK;
10450
10451 /* Lock all in {parent,child} order. The lock is also used as a
10452 * signal from the task initiator (which releases it only after
10453 * RTThreadCreate()) that we can start the job. */
10454 ComObjPtr<Medium> pBase = i_getBase();
10455 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10456
10457 try
10458 {
10459# ifdef VBOX_WITH_EXTPACK
10460 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
10461 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
10462 {
10463 /* Load the plugin */
10464 Utf8Str strPlugin;
10465 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
10466 if (SUCCEEDED(rc))
10467 {
10468 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
10469 if (RT_FAILURE(vrc))
10470 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10471 tr("Encrypting the image failed because the encryption plugin could not be loaded (%s)"),
10472 i_vdError(vrc).c_str());
10473 }
10474 else
10475 throw setError(VBOX_E_NOT_SUPPORTED,
10476 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
10477 ORACLE_PUEL_EXTPACK_NAME);
10478 }
10479 else
10480 throw setError(VBOX_E_NOT_SUPPORTED,
10481 tr("Encryption is not supported because the extension pack '%s' is missing"),
10482 ORACLE_PUEL_EXTPACK_NAME);
10483
10484 PVDISK pDisk = NULL;
10485 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
10486 ComAssertRCThrow(vrc, E_FAIL);
10487
10488 MediumCryptoFilterSettings CryptoSettingsRead;
10489 MediumCryptoFilterSettings CryptoSettingsWrite;
10490
10491 void *pvBuf = NULL;
10492 const char *pszPasswordNew = NULL;
10493 try
10494 {
10495 /* Set up disk encryption filters. */
10496 if (task.mstrCurrentPassword.isEmpty())
10497 {
10498 /*
10499 * Query whether the medium property indicating that encryption is
10500 * configured is existing.
10501 */
10502 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10503 if (it != pBase->m->mapProperties.end())
10504 throw setError(VBOX_E_PASSWORD_INCORRECT,
10505 tr("The password given for the encrypted image is incorrect"));
10506 }
10507 else
10508 {
10509 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10510 if (it == pBase->m->mapProperties.end())
10511 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10512 tr("The image is not configured for encryption"));
10513
10514 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, it->second.c_str(), task.mstrCurrentPassword.c_str(),
10515 false /* fCreateKeyStore */);
10516 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
10517 if (vrc == VERR_VD_PASSWORD_INCORRECT)
10518 throw setError(VBOX_E_PASSWORD_INCORRECT,
10519 tr("The password to decrypt the image is incorrect"));
10520 else if (RT_FAILURE(vrc))
10521 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10522 tr("Failed to load the decryption filter: %s"),
10523 i_vdError(vrc).c_str());
10524 }
10525
10526 if (task.mstrCipher.isNotEmpty())
10527 {
10528 if ( task.mstrNewPassword.isEmpty()
10529 && task.mstrNewPasswordId.isEmpty()
10530 && task.mstrCurrentPassword.isNotEmpty())
10531 {
10532 /* An empty password and password ID will default to the current password. */
10533 pszPasswordNew = task.mstrCurrentPassword.c_str();
10534 }
10535 else if (task.mstrNewPassword.isEmpty())
10536 throw setError(VBOX_E_OBJECT_NOT_FOUND,
10537 tr("A password must be given for the image encryption"));
10538 else if (task.mstrNewPasswordId.isEmpty())
10539 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10540 tr("A valid identifier for the password must be given"));
10541 else
10542 pszPasswordNew = task.mstrNewPassword.c_str();
10543
10544 i_taskEncryptSettingsSetup(&CryptoSettingsWrite, task.mstrCipher.c_str(), NULL,
10545 pszPasswordNew, true /* fCreateKeyStore */);
10546 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_WRITE, CryptoSettingsWrite.vdFilterIfaces);
10547 if (RT_FAILURE(vrc))
10548 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
10549 tr("Failed to load the encryption filter: %s"),
10550 i_vdError(vrc).c_str());
10551 }
10552 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
10553 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10554 tr("The password and password identifier must be empty if the output should be unencrypted"));
10555
10556 /* Open all media in the chain. */
10557 MediumLockList::Base::const_iterator mediumListBegin =
10558 task.mpMediumLockList->GetBegin();
10559 MediumLockList::Base::const_iterator mediumListEnd =
10560 task.mpMediumLockList->GetEnd();
10561 MediumLockList::Base::const_iterator mediumListLast =
10562 mediumListEnd;
10563 --mediumListLast;
10564 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10565 it != mediumListEnd;
10566 ++it)
10567 {
10568 const MediumLock &mediumLock = *it;
10569 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10570 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10571
10572 Assert(pMedium->m->state == MediumState_LockedWrite);
10573
10574 /* Open all media but last in read-only mode. Do not handle
10575 * shareable media, as compaction and sharing are mutually
10576 * exclusive. */
10577 vrc = VDOpen(pDisk,
10578 pMedium->m->strFormat.c_str(),
10579 pMedium->m->strLocationFull.c_str(),
10580 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10581 pMedium->m->vdImageIfaces);
10582 if (RT_FAILURE(vrc))
10583 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10584 tr("Could not open the medium storage unit '%s'%s"),
10585 pMedium->m->strLocationFull.c_str(),
10586 i_vdError(vrc).c_str());
10587 }
10588
10589 Assert(m->state == MediumState_LockedWrite);
10590
10591 Utf8Str location(m->strLocationFull);
10592
10593 /* unlock before the potentially lengthy operation */
10594 thisLock.release();
10595
10596 vrc = VDPrepareWithFilters(pDisk, task.mVDOperationIfaces);
10597 if (RT_FAILURE(vrc))
10598 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10599 tr("Could not prepare disk images for encryption (%Rrc): %s"),
10600 vrc, i_vdError(vrc).c_str());
10601
10602 thisLock.acquire();
10603 /* If everything went well set the new key store. */
10604 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10605 if (it != pBase->m->mapProperties.end())
10606 pBase->m->mapProperties.erase(it);
10607
10608 /* Delete KeyId if encryption is removed or the password did change. */
10609 if ( task.mstrNewPasswordId.isNotEmpty()
10610 || task.mstrCipher.isEmpty())
10611 {
10612 it = pBase->m->mapProperties.find("CRYPT/KeyId");
10613 if (it != pBase->m->mapProperties.end())
10614 pBase->m->mapProperties.erase(it);
10615 }
10616
10617 if (CryptoSettingsWrite.pszKeyStore)
10618 {
10619 pBase->m->mapProperties["CRYPT/KeyStore"] = Utf8Str(CryptoSettingsWrite.pszKeyStore);
10620 if (task.mstrNewPasswordId.isNotEmpty())
10621 pBase->m->mapProperties["CRYPT/KeyId"] = task.mstrNewPasswordId;
10622 }
10623
10624 if (CryptoSettingsRead.pszCipherReturned)
10625 RTStrFree(CryptoSettingsRead.pszCipherReturned);
10626
10627 if (CryptoSettingsWrite.pszCipherReturned)
10628 RTStrFree(CryptoSettingsWrite.pszCipherReturned);
10629
10630 thisLock.release();
10631 pBase->i_markRegistriesModified();
10632 m->pVirtualBox->i_saveModifiedRegistries();
10633 }
10634 catch (HRESULT aRC) { rc = aRC; }
10635
10636 if (pvBuf)
10637 RTMemFree(pvBuf);
10638
10639 VDDestroy(pDisk);
10640# else
10641 throw setError(VBOX_E_NOT_SUPPORTED,
10642 tr("Encryption is not supported because extension pack support is not built in"));
10643# endif
10644 }
10645 catch (HRESULT aRC) { rc = aRC; }
10646
10647 /* Everything is explicitly unlocked when the task exits,
10648 * as the task destruction also destroys the media chain. */
10649
10650 return rc;
10651}
10652
10653/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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