VirtualBox

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

Last change on this file since 71053 was 70991, checked in by vboxsync, 7 years ago

bugref:8345. Fixed the condition for moving floppy disk. Creation of floppy disk on Windows had been implemented already \(See #8527\) so try it to run on Windows. Tests are needed.

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

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