VirtualBox

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

Last change on this file since 97360 was 97360, checked in by vboxsync, 2 years ago

bugref:10180 - fixed issue where some files are not cleaned up after vm deletion or vm execution interruption, ensured enum value ordering stayed consistent

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

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