VirtualBox

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

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

Main: bugref:9623: Fixed issue causes the assertion during VBoxSVC unload. Fixed the issues during snapshots creating/deleting

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