VirtualBox

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

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

Main/Medium: Fix a number of potential deadlocks due to lock order violation. Several are still present and need to be fixed later.

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

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