VirtualBox

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

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

Backed out r154357,r154358,r154360,r154361,r154364 because it still doesn't build, old files weren't renamed or removed, and the changes for bugref:10180 and bugref:4787 got mixed up, making it difficult to selectively revert the latter one only.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 386.8 KB
Line 
1/* $Id: MediumImpl.cpp 97367 2022-11-01 10:38:05Z 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 throw setError(VBOX_E_NOT_SUPPORTED,
5532 tr("Medium format '%s' does not support storage deletion"),
5533 m->strFormat.c_str());
5534
5535 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5536 /** @todo r=klaus would be great if this could be moved to the async
5537 * part of the operation as it can take quite a while */
5538 while (m->queryInfoRunning)
5539 {
5540 alock.release();
5541 autoCaller.release();
5542 treelock.release();
5543 /* Must not hold the media tree lock or the object lock, as
5544 * Medium::i_queryInfo needs this lock and thus we would run
5545 * into a deadlock here. */
5546 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5547 Assert(!isWriteLockOnCurrentThread());
5548 {
5549 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5550 }
5551 treelock.acquire();
5552 autoCaller.add();
5553 AssertComRCThrowRC(autoCaller.rc());
5554 alock.acquire();
5555 }
5556
5557 /* Note that we are fine with Inaccessible state too: a) for symmetry
5558 * with create calls and b) because it doesn't really harm to try, if
5559 * it is really inaccessible, the delete operation will fail anyway.
5560 * Accepting Inaccessible state is especially important because all
5561 * registered media are initially Inaccessible upon VBoxSVC startup
5562 * until COMGETTER(RefreshState) is called. Accept Deleting state
5563 * because some callers need to put the medium in this state early
5564 * to prevent races. */
5565 switch (m->state)
5566 {
5567 case MediumState_Created:
5568 case MediumState_Deleting:
5569 case MediumState_Inaccessible:
5570 break;
5571 default:
5572 throw i_setStateError();
5573 }
5574
5575 if (m->backRefs.size() != 0)
5576 {
5577 Utf8Str strMachines;
5578 for (BackRefList::const_iterator it = m->backRefs.begin();
5579 it != m->backRefs.end();
5580 ++it)
5581 {
5582 const BackRef &b = *it;
5583 if (strMachines.length())
5584 strMachines.append(", ");
5585 strMachines.append(b.machineId.toString().c_str());
5586 }
5587#ifdef DEBUG
5588 i_dumpBackRefs();
5589#endif
5590 throw setError(VBOX_E_OBJECT_IN_USE,
5591 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s",
5592 "", m->backRefs.size()),
5593 m->strLocationFull.c_str(),
5594 m->backRefs.size(),
5595 strMachines.c_str());
5596 }
5597
5598 rc = i_canClose();
5599 if (FAILED(rc))
5600 throw rc;
5601
5602 /* go to Deleting state, so that the medium is not actually locked */
5603 if (m->state != MediumState_Deleting)
5604 {
5605 rc = i_markForDeletion();
5606 if (FAILED(rc))
5607 throw rc;
5608 }
5609
5610 /* Build the medium lock list. */
5611 MediumLockList *pMediumLockList(new MediumLockList());
5612 alock.release();
5613 autoCaller.release();
5614 treelock.release();
5615 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5616 this /* pToLockWrite */,
5617 false /* fMediumLockWriteAll */,
5618 NULL,
5619 *pMediumLockList);
5620 treelock.acquire();
5621 autoCaller.add();
5622 AssertComRCThrowRC(autoCaller.rc());
5623 alock.acquire();
5624 if (FAILED(rc))
5625 {
5626 delete pMediumLockList;
5627 throw rc;
5628 }
5629
5630 alock.release();
5631 autoCaller.release();
5632 treelock.release();
5633 rc = pMediumLockList->Lock();
5634 treelock.acquire();
5635 autoCaller.add();
5636 AssertComRCThrowRC(autoCaller.rc());
5637 alock.acquire();
5638 if (FAILED(rc))
5639 {
5640 delete pMediumLockList;
5641 throw setError(rc,
5642 tr("Failed to lock media when deleting '%s'"),
5643 i_getLocationFull().c_str());
5644 }
5645
5646 /* try to remove from the list of known media before performing
5647 * actual deletion (we favor the consistency of the media registry
5648 * which would have been broken if unregisterWithVirtualBox() failed
5649 * after we successfully deleted the storage) */
5650 rc = i_unregisterWithVirtualBox();
5651 if (FAILED(rc))
5652 throw rc;
5653 // no longer need lock
5654 alock.release();
5655 autoCaller.release();
5656 treelock.release();
5657 i_markRegistriesModified();
5658
5659 if (aProgress != NULL)
5660 {
5661 /* use the existing progress object... */
5662 pProgress = *aProgress;
5663
5664 /* ...but create a new one if it is null */
5665 if (pProgress.isNull())
5666 {
5667 pProgress.createObject();
5668 rc = pProgress->init(m->pVirtualBox,
5669 static_cast<IMedium*>(this),
5670 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
5671 FALSE /* aCancelable */);
5672 if (FAILED(rc))
5673 throw rc;
5674 }
5675 }
5676
5677 /* setup task object to carry out the operation sync/async */
5678 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList, false, aNotify);
5679 rc = pTask->rc();
5680 AssertComRC(rc);
5681 if (FAILED(rc))
5682 throw rc;
5683 }
5684 catch (HRESULT aRC) { rc = aRC; }
5685
5686 if (SUCCEEDED(rc))
5687 {
5688 if (aWait)
5689 {
5690 rc = pTask->runNow();
5691 delete pTask;
5692 }
5693 else
5694 rc = pTask->createThread();
5695 pTask = NULL;
5696 if (SUCCEEDED(rc) && aProgress != NULL)
5697 *aProgress = pProgress;
5698 }
5699 else
5700 {
5701 if (pTask)
5702 delete pTask;
5703
5704 /* Undo deleting state if necessary. */
5705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5706 /* Make sure that any error signalled by unmarkForDeletion() is not
5707 * ending up in the error list (if the caller uses MultiResult). It
5708 * usually is spurious, as in most cases the medium hasn't been marked
5709 * for deletion when the error was thrown above. */
5710 ErrorInfoKeeper eik;
5711 i_unmarkForDeletion();
5712 }
5713
5714 return rc;
5715}
5716
5717/**
5718 * Mark a medium for deletion.
5719 *
5720 * @note Caller must hold the write lock on this medium!
5721 */
5722HRESULT Medium::i_markForDeletion()
5723{
5724 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5725 switch (m->state)
5726 {
5727 case MediumState_Created:
5728 case MediumState_Inaccessible:
5729 m->preLockState = m->state;
5730 m->state = MediumState_Deleting;
5731 return S_OK;
5732 default:
5733 return i_setStateError();
5734 }
5735}
5736
5737/**
5738 * Removes the "mark for deletion".
5739 *
5740 * @note Caller must hold the write lock on this medium!
5741 */
5742HRESULT Medium::i_unmarkForDeletion()
5743{
5744 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5745 switch (m->state)
5746 {
5747 case MediumState_Deleting:
5748 m->state = m->preLockState;
5749 return S_OK;
5750 default:
5751 return i_setStateError();
5752 }
5753}
5754
5755/**
5756 * Mark a medium for deletion which is in locked state.
5757 *
5758 * @note Caller must hold the write lock on this medium!
5759 */
5760HRESULT Medium::i_markLockedForDeletion()
5761{
5762 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5763 if ( ( m->state == MediumState_LockedRead
5764 || m->state == MediumState_LockedWrite)
5765 && m->preLockState == MediumState_Created)
5766 {
5767 m->preLockState = MediumState_Deleting;
5768 return S_OK;
5769 }
5770 else
5771 return i_setStateError();
5772}
5773
5774/**
5775 * Removes the "mark for deletion" for a medium in locked state.
5776 *
5777 * @note Caller must hold the write lock on this medium!
5778 */
5779HRESULT Medium::i_unmarkLockedForDeletion()
5780{
5781 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5782 if ( ( m->state == MediumState_LockedRead
5783 || m->state == MediumState_LockedWrite)
5784 && m->preLockState == MediumState_Deleting)
5785 {
5786 m->preLockState = MediumState_Created;
5787 return S_OK;
5788 }
5789 else
5790 return i_setStateError();
5791}
5792
5793/**
5794 * Queries the preferred merge direction from this to the other medium, i.e.
5795 * the one which requires the least amount of I/O and therefore time and
5796 * disk consumption.
5797 *
5798 * @returns Status code.
5799 * @retval E_FAIL in case determining the merge direction fails for some reason,
5800 * for example if getting the size of the media fails. There is no
5801 * error set though and the caller is free to continue to find out
5802 * what was going wrong later. Leaves fMergeForward unset.
5803 * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
5804 * An error is set.
5805 * @param pOther The other medium to merge with.
5806 * @param fMergeForward Resulting preferred merge direction (out).
5807 */
5808HRESULT Medium::i_queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
5809 bool &fMergeForward)
5810{
5811 AssertReturn(pOther != NULL, E_FAIL);
5812 AssertReturn(pOther != this, E_FAIL);
5813
5814 HRESULT rc = S_OK;
5815 bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
5816
5817 try
5818 {
5819 // locking: we need the tree lock first because we access parent pointers
5820 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5821
5822 AutoCaller autoCaller(this);
5823 AssertComRCThrowRC(autoCaller.rc());
5824
5825 AutoCaller otherCaller(pOther);
5826 AssertComRCThrowRC(otherCaller.rc());
5827
5828 /* more sanity checking and figuring out the current merge direction */
5829 ComObjPtr<Medium> pMedium = i_getParent();
5830 while (!pMedium.isNull() && pMedium != pOther)
5831 pMedium = pMedium->i_getParent();
5832 if (pMedium == pOther)
5833 fThisParent = false;
5834 else
5835 {
5836 pMedium = pOther->i_getParent();
5837 while (!pMedium.isNull() && pMedium != this)
5838 pMedium = pMedium->i_getParent();
5839 if (pMedium == this)
5840 fThisParent = true;
5841 else
5842 {
5843 Utf8Str tgtLoc;
5844 {
5845 AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
5846 tgtLoc = pOther->i_getLocationFull();
5847 }
5848
5849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5850 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5851 tr("Media '%s' and '%s' are unrelated"),
5852 m->strLocationFull.c_str(), tgtLoc.c_str());
5853 }
5854 }
5855
5856 /*
5857 * Figure out the preferred merge direction. The current way is to
5858 * get the current sizes of file based images and select the merge
5859 * direction depending on the size.
5860 *
5861 * Can't use the VD API to get current size here as the media might
5862 * be write locked by a running VM. Resort to RTFileQuerySize().
5863 */
5864 int vrc = VINF_SUCCESS;
5865 uint64_t cbMediumThis = 0;
5866 uint64_t cbMediumOther = 0;
5867
5868 if (i_isMediumFormatFile() && pOther->i_isMediumFormatFile())
5869 {
5870 vrc = RTFileQuerySizeByPath(this->i_getLocationFull().c_str(), &cbMediumThis);
5871 if (RT_SUCCESS(vrc))
5872 {
5873 vrc = RTFileQuerySizeByPath(pOther->i_getLocationFull().c_str(),
5874 &cbMediumOther);
5875 }
5876
5877 if (RT_FAILURE(vrc))
5878 rc = E_FAIL;
5879 else
5880 {
5881 /*
5882 * Check which merge direction might be more optimal.
5883 * This method is not bullet proof of course as there might
5884 * be overlapping blocks in the images so the file size is
5885 * not the best indicator but it is good enough for our purpose
5886 * and everything else is too complicated, especially when the
5887 * media are used by a running VM.
5888 */
5889
5890 uint32_t mediumVariants = MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized;
5891 uint32_t mediumCaps = MediumFormatCapabilities_CreateDynamic | MediumFormatCapabilities_File;
5892
5893 bool fDynamicOther = pOther->i_getMediumFormat()->i_getCapabilities() & mediumCaps
5894 && pOther->i_getVariant() & ~mediumVariants;
5895 bool fDynamicThis = i_getMediumFormat()->i_getCapabilities() & mediumCaps
5896 && i_getVariant() & ~mediumVariants;
5897 bool fMergeIntoThis = (fDynamicThis && !fDynamicOther)
5898 || (fDynamicThis == fDynamicOther && cbMediumThis > cbMediumOther);
5899 fMergeForward = fMergeIntoThis != fThisParent;
5900 }
5901 }
5902 }
5903 catch (HRESULT aRC) { rc = aRC; }
5904
5905 return rc;
5906}
5907
5908/**
5909 * Prepares this (source) medium, target medium and all intermediate media
5910 * for the merge operation.
5911 *
5912 * This method is to be called prior to calling the #mergeTo() to perform
5913 * necessary consistency checks and place involved media to appropriate
5914 * states. If #mergeTo() is not called or fails, the state modifications
5915 * performed by this method must be undone by #i_cancelMergeTo().
5916 *
5917 * See #mergeTo() for more information about merging.
5918 *
5919 * @param pTarget Target medium.
5920 * @param aMachineId Allowed machine attachment. NULL means do not check.
5921 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
5922 * do not check.
5923 * @param fLockMedia Flag whether to lock the medium lock list or not.
5924 * If set to false and the medium lock list locking fails
5925 * later you must call #i_cancelMergeTo().
5926 * @param fMergeForward Resulting merge direction (out).
5927 * @param pParentForTarget New parent for target medium after merge (out).
5928 * @param aChildrenToReparent Medium lock list containing all children of the
5929 * source which will have to be reparented to the target
5930 * after merge (out).
5931 * @param aMediumLockList Medium locking information (out).
5932 *
5933 * @note Locks medium tree for reading. Locks this object, aTarget and all
5934 * intermediate media for writing.
5935 */
5936HRESULT Medium::i_prepareMergeTo(const ComObjPtr<Medium> &pTarget,
5937 const Guid *aMachineId,
5938 const Guid *aSnapshotId,
5939 bool fLockMedia,
5940 bool &fMergeForward,
5941 ComObjPtr<Medium> &pParentForTarget,
5942 MediumLockList * &aChildrenToReparent,
5943 MediumLockList * &aMediumLockList)
5944{
5945 AssertReturn(pTarget != NULL, E_FAIL);
5946 AssertReturn(pTarget != this, E_FAIL);
5947
5948 HRESULT rc = S_OK;
5949 fMergeForward = false;
5950 pParentForTarget.setNull();
5951 Assert(aChildrenToReparent == NULL);
5952 aChildrenToReparent = NULL;
5953 Assert(aMediumLockList == NULL);
5954 aMediumLockList = NULL;
5955
5956 try
5957 {
5958 // locking: we need the tree lock first because we access parent pointers
5959 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5960
5961 AutoCaller autoCaller(this);
5962 AssertComRCThrowRC(autoCaller.rc());
5963
5964 AutoCaller targetCaller(pTarget);
5965 AssertComRCThrowRC(targetCaller.rc());
5966
5967 /* more sanity checking and figuring out the merge direction */
5968 ComObjPtr<Medium> pMedium = i_getParent();
5969 while (!pMedium.isNull() && pMedium != pTarget)
5970 pMedium = pMedium->i_getParent();
5971 if (pMedium == pTarget)
5972 fMergeForward = false;
5973 else
5974 {
5975 pMedium = pTarget->i_getParent();
5976 while (!pMedium.isNull() && pMedium != this)
5977 pMedium = pMedium->i_getParent();
5978 if (pMedium == this)
5979 fMergeForward = true;
5980 else
5981 {
5982 Utf8Str tgtLoc;
5983 {
5984 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5985 tgtLoc = pTarget->i_getLocationFull();
5986 }
5987
5988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5989 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5990 tr("Media '%s' and '%s' are unrelated"),
5991 m->strLocationFull.c_str(), tgtLoc.c_str());
5992 }
5993 }
5994
5995 /* Build the lock list. */
5996 aMediumLockList = new MediumLockList();
5997 targetCaller.release();
5998 autoCaller.release();
5999 treeLock.release();
6000 if (fMergeForward)
6001 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
6002 pTarget /* pToLockWrite */,
6003 false /* fMediumLockWriteAll */,
6004 NULL,
6005 *aMediumLockList);
6006 else
6007 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6008 pTarget /* pToLockWrite */,
6009 false /* fMediumLockWriteAll */,
6010 NULL,
6011 *aMediumLockList);
6012 treeLock.acquire();
6013 autoCaller.add();
6014 AssertComRCThrowRC(autoCaller.rc());
6015 targetCaller.add();
6016 AssertComRCThrowRC(targetCaller.rc());
6017 if (FAILED(rc))
6018 throw rc;
6019
6020 /* Sanity checking, must be after lock list creation as it depends on
6021 * valid medium states. The medium objects must be accessible. Only
6022 * do this if immediate locking is requested, otherwise it fails when
6023 * we construct a medium lock list for an already running VM. Snapshot
6024 * deletion uses this to simplify its life. */
6025 if (fLockMedia)
6026 {
6027 {
6028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6029 if (m->state != MediumState_Created)
6030 throw i_setStateError();
6031 }
6032 {
6033 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6034 if (pTarget->m->state != MediumState_Created)
6035 throw pTarget->i_setStateError();
6036 }
6037 }
6038
6039 /* check medium attachment and other sanity conditions */
6040 if (fMergeForward)
6041 {
6042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6043 if (i_getChildren().size() > 1)
6044 {
6045 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6046 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6047 m->strLocationFull.c_str(), i_getChildren().size());
6048 }
6049 /* One backreference is only allowed if the machine ID is not empty
6050 * and it matches the machine the medium is attached to (including
6051 * the snapshot ID if not empty). */
6052 if ( m->backRefs.size() != 0
6053 && ( !aMachineId
6054 || m->backRefs.size() != 1
6055 || aMachineId->isZero()
6056 || *i_getFirstMachineBackrefId() != *aMachineId
6057 || ( (!aSnapshotId || !aSnapshotId->isZero())
6058 && *i_getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
6059 throw setError(VBOX_E_OBJECT_IN_USE,
6060 tr("Medium '%s' is attached to %d virtual machines", "", m->backRefs.size()),
6061 m->strLocationFull.c_str(), m->backRefs.size());
6062 if (m->type == MediumType_Immutable)
6063 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6064 tr("Medium '%s' is immutable"),
6065 m->strLocationFull.c_str());
6066 if (m->type == MediumType_MultiAttach)
6067 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6068 tr("Medium '%s' is multi-attach"),
6069 m->strLocationFull.c_str());
6070 }
6071 else
6072 {
6073 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6074 if (pTarget->i_getChildren().size() > 1)
6075 {
6076 throw setError(VBOX_E_OBJECT_IN_USE,
6077 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6078 pTarget->m->strLocationFull.c_str(),
6079 pTarget->i_getChildren().size());
6080 }
6081 if (pTarget->m->type == MediumType_Immutable)
6082 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6083 tr("Medium '%s' is immutable"),
6084 pTarget->m->strLocationFull.c_str());
6085 if (pTarget->m->type == MediumType_MultiAttach)
6086 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6087 tr("Medium '%s' is multi-attach"),
6088 pTarget->m->strLocationFull.c_str());
6089 }
6090 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
6091 ComObjPtr<Medium> pLastIntermediate = pLast->i_getParent();
6092 for (pLast = pLastIntermediate;
6093 !pLast.isNull() && pLast != pTarget && pLast != this;
6094 pLast = pLast->i_getParent())
6095 {
6096 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
6097 if (pLast->i_getChildren().size() > 1)
6098 {
6099 throw setError(VBOX_E_OBJECT_IN_USE,
6100 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6101 pLast->m->strLocationFull.c_str(),
6102 pLast->i_getChildren().size());
6103 }
6104 if (pLast->m->backRefs.size() != 0)
6105 throw setError(VBOX_E_OBJECT_IN_USE,
6106 tr("Medium '%s' is attached to %d virtual machines", "", pLast->m->backRefs.size()),
6107 pLast->m->strLocationFull.c_str(),
6108 pLast->m->backRefs.size());
6109
6110 }
6111
6112 /* Update medium states appropriately */
6113 {
6114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6115
6116 if (m->state == MediumState_Created)
6117 {
6118 rc = i_markForDeletion();
6119 if (FAILED(rc))
6120 throw rc;
6121 }
6122 else
6123 {
6124 if (fLockMedia)
6125 throw i_setStateError();
6126 else if ( m->state == MediumState_LockedWrite
6127 || m->state == MediumState_LockedRead)
6128 {
6129 /* Either mark it for deletion in locked state or allow
6130 * others to have done so. */
6131 if (m->preLockState == MediumState_Created)
6132 i_markLockedForDeletion();
6133 else if (m->preLockState != MediumState_Deleting)
6134 throw i_setStateError();
6135 }
6136 else
6137 throw i_setStateError();
6138 }
6139 }
6140
6141 if (fMergeForward)
6142 {
6143 /* we will need parent to reparent target */
6144 pParentForTarget = i_getParent();
6145 }
6146 else
6147 {
6148 /* we will need to reparent children of the source */
6149 aChildrenToReparent = new MediumLockList();
6150 for (MediaList::const_iterator it = i_getChildren().begin();
6151 it != i_getChildren().end();
6152 ++it)
6153 {
6154 pMedium = *it;
6155 aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
6156 }
6157 if (fLockMedia && aChildrenToReparent)
6158 {
6159 targetCaller.release();
6160 autoCaller.release();
6161 treeLock.release();
6162 rc = aChildrenToReparent->Lock();
6163 treeLock.acquire();
6164 autoCaller.add();
6165 AssertComRCThrowRC(autoCaller.rc());
6166 targetCaller.add();
6167 AssertComRCThrowRC(targetCaller.rc());
6168 if (FAILED(rc))
6169 throw rc;
6170 }
6171 }
6172 for (pLast = pLastIntermediate;
6173 !pLast.isNull() && pLast != pTarget && pLast != this;
6174 pLast = pLast->i_getParent())
6175 {
6176 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
6177 if (pLast->m->state == MediumState_Created)
6178 {
6179 rc = pLast->i_markForDeletion();
6180 if (FAILED(rc))
6181 throw rc;
6182 }
6183 else
6184 throw pLast->i_setStateError();
6185 }
6186
6187 /* Tweak the lock list in the backward merge case, as the target
6188 * isn't marked to be locked for writing yet. */
6189 if (!fMergeForward)
6190 {
6191 MediumLockList::Base::iterator lockListBegin =
6192 aMediumLockList->GetBegin();
6193 MediumLockList::Base::iterator lockListEnd =
6194 aMediumLockList->GetEnd();
6195 ++lockListEnd;
6196 for (MediumLockList::Base::iterator it = lockListBegin;
6197 it != lockListEnd;
6198 ++it)
6199 {
6200 MediumLock &mediumLock = *it;
6201 if (mediumLock.GetMedium() == pTarget)
6202 {
6203 HRESULT rc2 = mediumLock.UpdateLock(true);
6204 AssertComRC(rc2);
6205 break;
6206 }
6207 }
6208 }
6209
6210 if (fLockMedia)
6211 {
6212 targetCaller.release();
6213 autoCaller.release();
6214 treeLock.release();
6215 rc = aMediumLockList->Lock();
6216 treeLock.acquire();
6217 autoCaller.add();
6218 AssertComRCThrowRC(autoCaller.rc());
6219 targetCaller.add();
6220 AssertComRCThrowRC(targetCaller.rc());
6221 if (FAILED(rc))
6222 {
6223 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6224 throw setError(rc,
6225 tr("Failed to lock media when merging to '%s'"),
6226 pTarget->i_getLocationFull().c_str());
6227 }
6228 }
6229 }
6230 catch (HRESULT aRC) { rc = aRC; }
6231
6232 if (FAILED(rc))
6233 {
6234 if (aMediumLockList)
6235 {
6236 delete aMediumLockList;
6237 aMediumLockList = NULL;
6238 }
6239 if (aChildrenToReparent)
6240 {
6241 delete aChildrenToReparent;
6242 aChildrenToReparent = NULL;
6243 }
6244 }
6245
6246 return rc;
6247}
6248
6249/**
6250 * Merges this medium to the specified medium which must be either its
6251 * direct ancestor or descendant.
6252 *
6253 * Given this medium is SOURCE and the specified medium is TARGET, we will
6254 * get two variants of the merge operation:
6255 *
6256 * forward merge
6257 * ------------------------->
6258 * [Extra] <- SOURCE <- Intermediate <- TARGET
6259 * Any Del Del LockWr
6260 *
6261 *
6262 * backward merge
6263 * <-------------------------
6264 * TARGET <- Intermediate <- SOURCE <- [Extra]
6265 * LockWr Del Del LockWr
6266 *
6267 * Each diagram shows the involved media on the media chain where
6268 * SOURCE and TARGET belong. Under each medium there is a state value which
6269 * the medium must have at a time of the mergeTo() call.
6270 *
6271 * The media in the square braces may be absent (e.g. when the forward
6272 * operation takes place and SOURCE is the base medium, or when the backward
6273 * merge operation takes place and TARGET is the last child in the chain) but if
6274 * they present they are involved too as shown.
6275 *
6276 * Neither the source medium nor intermediate media may be attached to
6277 * any VM directly or in the snapshot, otherwise this method will assert.
6278 *
6279 * The #i_prepareMergeTo() method must be called prior to this method to place
6280 * all involved to necessary states and perform other consistency checks.
6281 *
6282 * If @a aWait is @c true then this method will perform the operation on the
6283 * calling thread and will not return to the caller until the operation is
6284 * completed. When this method succeeds, all intermediate medium objects in
6285 * the chain will be uninitialized, the state of the target medium (and all
6286 * involved extra media) will be restored. @a aMediumLockList will not be
6287 * deleted, whether the operation is successful or not. The caller has to do
6288 * this if appropriate. Note that this (source) medium is not uninitialized
6289 * because of possible AutoCaller instances held by the caller of this method
6290 * on the current thread. It's therefore the responsibility of the caller to
6291 * call Medium::uninit() after releasing all callers.
6292 *
6293 * If @a aWait is @c false then this method will create a thread to perform the
6294 * operation asynchronously and will return immediately. If the operation
6295 * succeeds, the thread will uninitialize the source medium object and all
6296 * intermediate medium objects in the chain, reset the state of the target
6297 * medium (and all involved extra media) and delete @a aMediumLockList.
6298 * If the operation fails, the thread will only reset the states of all
6299 * involved media and delete @a aMediumLockList.
6300 *
6301 * When this method fails (regardless of the @a aWait mode), it is a caller's
6302 * responsibility to undo state changes and delete @a aMediumLockList using
6303 * #i_cancelMergeTo().
6304 *
6305 * If @a aProgress is not NULL but the object it points to is @c null then a new
6306 * progress object will be created and assigned to @a *aProgress on success,
6307 * otherwise the existing progress object is used. If Progress is NULL, then no
6308 * progress object is created/used at all. Note that @a aProgress cannot be
6309 * NULL when @a aWait is @c false (this method will assert in this case).
6310 *
6311 * @param pTarget Target medium.
6312 * @param fMergeForward Merge direction.
6313 * @param pParentForTarget New parent for target medium after merge.
6314 * @param aChildrenToReparent List of children of the source which will have
6315 * to be reparented to the target after merge.
6316 * @param aMediumLockList Medium locking information.
6317 * @param aProgress Where to find/store a Progress object to track operation
6318 * completion.
6319 * @param aWait @c true if this method should block instead of creating
6320 * an asynchronous thread.
6321 * @param aNotify Notify about mediums which metadatа are changed
6322 * during execution of the function.
6323 *
6324 * @note Locks the tree lock for writing. Locks the media from the chain
6325 * for writing.
6326 */
6327HRESULT Medium::i_mergeTo(const ComObjPtr<Medium> &pTarget,
6328 bool fMergeForward,
6329 const ComObjPtr<Medium> &pParentForTarget,
6330 MediumLockList *aChildrenToReparent,
6331 MediumLockList *aMediumLockList,
6332 ComObjPtr<Progress> *aProgress,
6333 bool aWait, bool aNotify)
6334{
6335 AssertReturn(pTarget != NULL, E_FAIL);
6336 AssertReturn(pTarget != this, E_FAIL);
6337 AssertReturn(aMediumLockList != NULL, E_FAIL);
6338 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6339
6340 AutoCaller autoCaller(this);
6341 AssertComRCReturnRC(autoCaller.rc());
6342
6343 AutoCaller targetCaller(pTarget);
6344 AssertComRCReturnRC(targetCaller.rc());
6345
6346 HRESULT rc = S_OK;
6347 ComObjPtr<Progress> pProgress;
6348 Medium::Task *pTask = NULL;
6349
6350 try
6351 {
6352 if (aProgress != NULL)
6353 {
6354 /* use the existing progress object... */
6355 pProgress = *aProgress;
6356
6357 /* ...but create a new one if it is null */
6358 if (pProgress.isNull())
6359 {
6360 Utf8Str tgtName;
6361 {
6362 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6363 tgtName = pTarget->i_getName();
6364 }
6365
6366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6367
6368 pProgress.createObject();
6369 rc = pProgress->init(m->pVirtualBox,
6370 static_cast<IMedium*>(this),
6371 BstrFmt(tr("Merging medium '%s' to '%s'"),
6372 i_getName().c_str(),
6373 tgtName.c_str()).raw(),
6374 TRUE, /* aCancelable */
6375 2, /* Number of opearations */
6376 BstrFmt(tr("Resizing medium '%s' before merge"),
6377 tgtName.c_str()).raw()
6378 );
6379 if (FAILED(rc))
6380 throw rc;
6381 }
6382 }
6383
6384 /* setup task object to carry out the operation sync/async */
6385 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
6386 pParentForTarget, aChildrenToReparent,
6387 pProgress, aMediumLockList,
6388 aWait /* fKeepMediumLockList */,
6389 aNotify);
6390 rc = pTask->rc();
6391 AssertComRC(rc);
6392 if (FAILED(rc))
6393 throw rc;
6394 }
6395 catch (HRESULT aRC) { rc = aRC; }
6396
6397 if (SUCCEEDED(rc))
6398 {
6399 if (aWait)
6400 {
6401 rc = pTask->runNow();
6402 delete pTask;
6403 }
6404 else
6405 rc = pTask->createThread();
6406 pTask = NULL;
6407 if (SUCCEEDED(rc) && aProgress != NULL)
6408 *aProgress = pProgress;
6409 }
6410 else if (pTask != NULL)
6411 delete pTask;
6412
6413 return rc;
6414}
6415
6416/**
6417 * Undoes what #i_prepareMergeTo() did. Must be called if #mergeTo() is not
6418 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
6419 * the medium objects in @a aChildrenToReparent.
6420 *
6421 * @param aChildrenToReparent List of children of the source which will have
6422 * to be reparented to the target after merge.
6423 * @param aMediumLockList Medium locking information.
6424 *
6425 * @note Locks the tree lock for writing. Locks the media from the chain
6426 * for writing.
6427 */
6428void Medium::i_cancelMergeTo(MediumLockList *aChildrenToReparent,
6429 MediumLockList *aMediumLockList)
6430{
6431 AutoCaller autoCaller(this);
6432 AssertComRCReturnVoid(autoCaller.rc());
6433
6434 AssertReturnVoid(aMediumLockList != NULL);
6435
6436 /* Revert media marked for deletion to previous state. */
6437 HRESULT rc;
6438 MediumLockList::Base::const_iterator mediumListBegin =
6439 aMediumLockList->GetBegin();
6440 MediumLockList::Base::const_iterator mediumListEnd =
6441 aMediumLockList->GetEnd();
6442 for (MediumLockList::Base::const_iterator it = mediumListBegin;
6443 it != mediumListEnd;
6444 ++it)
6445 {
6446 const MediumLock &mediumLock = *it;
6447 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6448 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6449
6450 if (pMedium->m->state == MediumState_Deleting)
6451 {
6452 rc = pMedium->i_unmarkForDeletion();
6453 AssertComRC(rc);
6454 }
6455 else if ( ( pMedium->m->state == MediumState_LockedWrite
6456 || pMedium->m->state == MediumState_LockedRead)
6457 && pMedium->m->preLockState == MediumState_Deleting)
6458 {
6459 rc = pMedium->i_unmarkLockedForDeletion();
6460 AssertComRC(rc);
6461 }
6462 }
6463
6464 /* the destructor will do the work */
6465 delete aMediumLockList;
6466
6467 /* unlock the children which had to be reparented, the destructor will do
6468 * the work */
6469 if (aChildrenToReparent)
6470 delete aChildrenToReparent;
6471}
6472
6473/**
6474 * Resizes the media.
6475 *
6476 * If @a aWait is @c true then this method will perform the operation on the
6477 * calling thread and will not return to the caller until the operation is
6478 * completed. When this method succeeds, the state of the target medium (and all
6479 * involved extra media) will be restored. @a aMediumLockList will not be
6480 * deleted, whether the operation is successful or not. The caller has to do
6481 * this if appropriate.
6482 *
6483 * If @a aWait is @c false then this method will create a thread to perform the
6484 * operation asynchronously and will return immediately. The thread will reset
6485 * the state of the target medium (and all involved extra media) and delete
6486 * @a aMediumLockList.
6487 *
6488 * When this method fails (regardless of the @a aWait mode), it is a caller's
6489 * responsibility to undo state changes and delete @a aMediumLockList.
6490 *
6491 * If @a aProgress is not NULL but the object it points to is @c null then a new
6492 * progress object will be created and assigned to @a *aProgress on success,
6493 * otherwise the existing progress object is used. If Progress is NULL, then no
6494 * progress object is created/used at all. Note that @a aProgress cannot be
6495 * NULL when @a aWait is @c false (this method will assert in this case).
6496 *
6497 * @param aLogicalSize New nominal capacity of the medium in bytes.
6498 * @param aMediumLockList Medium locking information.
6499 * @param aProgress Where to find/store a Progress object to track operation
6500 * completion.
6501 * @param aWait @c true if this method should block instead of creating
6502 * an asynchronous thread.
6503 * @param aNotify Notify about mediums which metadatа are changed
6504 * during execution of the function.
6505 *
6506 * @note Locks the media from the chain for writing.
6507 */
6508
6509HRESULT Medium::i_resize(uint64_t aLogicalSize,
6510 MediumLockList *aMediumLockList,
6511 ComObjPtr<Progress> *aProgress,
6512 bool aWait,
6513 bool aNotify)
6514{
6515 AssertReturn(aMediumLockList != NULL, E_FAIL);
6516 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6517
6518 AutoCaller autoCaller(this);
6519 AssertComRCReturnRC(autoCaller.rc());
6520
6521 HRESULT rc = S_OK;
6522 ComObjPtr<Progress> pProgress;
6523 Medium::Task *pTask = NULL;
6524
6525 try
6526 {
6527 if (aProgress != NULL)
6528 {
6529 /* use the existing progress object... */
6530 pProgress = *aProgress;
6531
6532 /* ...but create a new one if it is null */
6533 if (pProgress.isNull())
6534 {
6535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6536
6537 pProgress.createObject();
6538 rc = pProgress->init(m->pVirtualBox,
6539 static_cast <IMedium *>(this),
6540 BstrFmt(tr("Resizing medium '%s'"), m->strLocationFull.c_str()).raw(),
6541 TRUE /* aCancelable */);
6542 if (FAILED(rc))
6543 throw rc;
6544 }
6545 }
6546
6547 /* setup task object to carry out the operation asynchronously */
6548 pTask = new Medium::ResizeTask(this,
6549 aLogicalSize,
6550 pProgress,
6551 aMediumLockList,
6552 aWait /* fKeepMediumLockList */,
6553 aNotify);
6554 rc = pTask->rc();
6555 AssertComRC(rc);
6556 if (FAILED(rc))
6557 throw rc;
6558 }
6559 catch (HRESULT aRC) { rc = aRC; }
6560
6561 if (SUCCEEDED(rc))
6562 {
6563 if (aWait)
6564 {
6565 rc = pTask->runNow();
6566 delete pTask;
6567 }
6568 else
6569 rc = pTask->createThread();
6570 pTask = NULL;
6571 if (SUCCEEDED(rc) && aProgress != NULL)
6572 *aProgress = pProgress;
6573 }
6574 else if (pTask != NULL)
6575 delete pTask;
6576
6577 return rc;
6578}
6579
6580/**
6581 * Fix the parent UUID of all children to point to this medium as their
6582 * parent.
6583 */
6584HRESULT Medium::i_fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
6585{
6586 /** @todo r=klaus The code below needs to be double checked with regard
6587 * to lock order violations, it probably causes lock order issues related
6588 * to the AutoCaller usage. Likewise the code using this method seems
6589 * problematic. */
6590 Assert(!isWriteLockOnCurrentThread());
6591 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6592 MediumLockList mediumLockList;
6593 HRESULT rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6594 NULL /* pToLockWrite */,
6595 false /* fMediumLockWriteAll */,
6596 this,
6597 mediumLockList);
6598 AssertComRCReturnRC(rc);
6599
6600 try
6601 {
6602 PVDISK hdd;
6603 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6604 ComAssertRCThrow(vrc, E_FAIL);
6605
6606 try
6607 {
6608 MediumLockList::Base::iterator lockListBegin =
6609 mediumLockList.GetBegin();
6610 MediumLockList::Base::iterator lockListEnd =
6611 mediumLockList.GetEnd();
6612 for (MediumLockList::Base::iterator it = lockListBegin;
6613 it != lockListEnd;
6614 ++it)
6615 {
6616 MediumLock &mediumLock = *it;
6617 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6618 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6619
6620 // open the medium
6621 vrc = VDOpen(hdd,
6622 pMedium->m->strFormat.c_str(),
6623 pMedium->m->strLocationFull.c_str(),
6624 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
6625 pMedium->m->vdImageIfaces);
6626 if (RT_FAILURE(vrc))
6627 throw vrc;
6628 }
6629
6630 MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
6631 MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
6632 for (MediumLockList::Base::iterator it = childrenBegin;
6633 it != childrenEnd;
6634 ++it)
6635 {
6636 Medium *pMedium = it->GetMedium();
6637 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
6638 vrc = VDOpen(hdd,
6639 pMedium->m->strFormat.c_str(),
6640 pMedium->m->strLocationFull.c_str(),
6641 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
6642 pMedium->m->vdImageIfaces);
6643 if (RT_FAILURE(vrc))
6644 throw vrc;
6645
6646 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
6647 if (RT_FAILURE(vrc))
6648 throw vrc;
6649
6650 vrc = VDClose(hdd, false /* fDelete */);
6651 if (RT_FAILURE(vrc))
6652 throw vrc;
6653 }
6654 }
6655 catch (HRESULT aRC) { rc = aRC; }
6656 catch (int aVRC)
6657 {
6658 rc = setErrorBoth(E_FAIL, aVRC,
6659 tr("Could not update medium UUID references to parent '%s' (%s)"),
6660 m->strLocationFull.c_str(),
6661 i_vdError(aVRC).c_str());
6662 }
6663
6664 VDDestroy(hdd);
6665 }
6666 catch (HRESULT aRC) { rc = aRC; }
6667
6668 return rc;
6669}
6670
6671/**
6672 *
6673 * @note Similar code exists in i_taskExportHandler.
6674 */
6675HRESULT Medium::i_addRawToFss(const char *aFilename, SecretKeyStore *pKeyStore, RTVFSFSSTREAM hVfsFssDst,
6676 const ComObjPtr<Progress> &aProgress, bool fSparse)
6677{
6678 AutoCaller autoCaller(this);
6679 HRESULT hrc = autoCaller.rc();
6680 if (SUCCEEDED(hrc))
6681 {
6682 /*
6683 * Get a readonly hdd for this medium.
6684 */
6685 MediumCryptoFilterSettings CryptoSettingsRead;
6686 MediumLockList SourceMediumLockList;
6687 PVDISK pHdd;
6688 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pHdd, &SourceMediumLockList, &CryptoSettingsRead);
6689 if (SUCCEEDED(hrc))
6690 {
6691 /*
6692 * Create a VFS file interface to the HDD and attach a progress wrapper
6693 * that monitors the progress reading of the raw image. The image will
6694 * be read twice if hVfsFssDst does sparse processing.
6695 */
6696 RTVFSFILE hVfsFileDisk = NIL_RTVFSFILE;
6697 int vrc = VDCreateVfsFileFromDisk(pHdd, 0 /*fFlags*/, &hVfsFileDisk);
6698 if (RT_SUCCESS(vrc))
6699 {
6700 RTVFSFILE hVfsFileProgress = NIL_RTVFSFILE;
6701 vrc = RTVfsCreateProgressForFile(hVfsFileDisk, aProgress->i_iprtProgressCallback, &*aProgress,
6702 RTVFSPROGRESS_F_CANCELABLE | RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ,
6703 VDGetSize(pHdd, VD_LAST_IMAGE) * (fSparse ? 2 : 1) /*cbExpectedRead*/,
6704 0 /*cbExpectedWritten*/, &hVfsFileProgress);
6705 RTVfsFileRelease(hVfsFileDisk);
6706 if (RT_SUCCESS(vrc))
6707 {
6708 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFileProgress);
6709 RTVfsFileRelease(hVfsFileProgress);
6710
6711 vrc = RTVfsFsStrmAdd(hVfsFssDst, aFilename, hVfsObj, 0 /*fFlags*/);
6712 RTVfsObjRelease(hVfsObj);
6713 if (RT_FAILURE(vrc))
6714 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to add '%s' to output (%Rrc)"), aFilename, vrc);
6715 }
6716 else
6717 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
6718 tr("RTVfsCreateProgressForFile failed when processing '%s' (%Rrc)"), aFilename, vrc);
6719 }
6720 else
6721 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("VDCreateVfsFileFromDisk failed for '%s' (%Rrc)"), aFilename, vrc);
6722 VDDestroy(pHdd);
6723 }
6724 }
6725 return hrc;
6726}
6727
6728/**
6729 * Used by IAppliance to export disk images.
6730 *
6731 * @param aFilename Filename to create (UTF8).
6732 * @param aFormat Medium format for creating @a aFilename.
6733 * @param aVariant Which exact image format variant to use for the
6734 * destination image.
6735 * @param pKeyStore The optional key store for decrypting the data for
6736 * encrypted media during the export.
6737 * @param hVfsIosDst The destination I/O stream object.
6738 * @param aProgress Progress object to use.
6739 * @return
6740 *
6741 * @note The source format is defined by the Medium instance.
6742 */
6743HRESULT Medium::i_exportFile(const char *aFilename,
6744 const ComObjPtr<MediumFormat> &aFormat,
6745 MediumVariant_T aVariant,
6746 SecretKeyStore *pKeyStore,
6747 RTVFSIOSTREAM hVfsIosDst,
6748 const ComObjPtr<Progress> &aProgress)
6749{
6750 AssertPtrReturn(aFilename, E_INVALIDARG);
6751 AssertReturn(aFormat.isNotNull(), E_INVALIDARG);
6752 AssertReturn(aProgress.isNotNull(), E_INVALIDARG);
6753
6754 AutoCaller autoCaller(this);
6755 HRESULT hrc = autoCaller.rc();
6756 if (SUCCEEDED(hrc))
6757 {
6758 /*
6759 * Setup VD interfaces.
6760 */
6761 PVDINTERFACE pVDImageIfaces = m->vdImageIfaces;
6762 PVDINTERFACEIO pVfsIoIf;
6763 int vrc = VDIfCreateFromVfsStream(hVfsIosDst, RTFILE_O_WRITE, &pVfsIoIf);
6764 if (RT_SUCCESS(vrc))
6765 {
6766 vrc = VDInterfaceAdd(&pVfsIoIf->Core, "Medium::ExportTaskVfsIos", VDINTERFACETYPE_IO,
6767 pVfsIoIf, sizeof(VDINTERFACEIO), &pVDImageIfaces);
6768 if (RT_SUCCESS(vrc))
6769 {
6770 /*
6771 * Get a readonly hdd for this medium (source).
6772 */
6773 MediumCryptoFilterSettings CryptoSettingsRead;
6774 MediumLockList SourceMediumLockList;
6775 PVDISK pSrcHdd;
6776 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pSrcHdd, &SourceMediumLockList, &CryptoSettingsRead);
6777 if (SUCCEEDED(hrc))
6778 {
6779 /*
6780 * Create the target medium.
6781 */
6782 Utf8Str strDstFormat(aFormat->i_getId());
6783
6784 /* ensure the target directory exists */
6785 uint64_t fDstCapabilities = aFormat->i_getCapabilities();
6786 if (fDstCapabilities & MediumFormatCapabilities_File)
6787 {
6788 Utf8Str strDstLocation(aFilename);
6789 hrc = VirtualBox::i_ensureFilePathExists(strDstLocation.c_str(),
6790 !(aVariant & MediumVariant_NoCreateDir) /* fCreate */);
6791 }
6792 if (SUCCEEDED(hrc))
6793 {
6794 PVDISK pDstHdd;
6795 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDstHdd);
6796 if (RT_SUCCESS(vrc))
6797 {
6798 /*
6799 * Create an interface for getting progress callbacks.
6800 */
6801 VDINTERFACEPROGRESS ProgressIf = VDINTERFACEPROGRESS_INITALIZER(aProgress->i_vdProgressCallback);
6802 PVDINTERFACE pProgress = NULL;
6803 vrc = VDInterfaceAdd(&ProgressIf.Core, "export-progress", VDINTERFACETYPE_PROGRESS,
6804 &*aProgress, sizeof(ProgressIf), &pProgress);
6805 AssertRC(vrc);
6806
6807 /*
6808 * Do the exporting.
6809 */
6810 vrc = VDCopy(pSrcHdd,
6811 VD_LAST_IMAGE,
6812 pDstHdd,
6813 strDstFormat.c_str(),
6814 aFilename,
6815 false /* fMoveByRename */,
6816 0 /* cbSize */,
6817 aVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
6818 NULL /* pDstUuid */,
6819 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
6820 pProgress,
6821 pVDImageIfaces,
6822 NULL);
6823 if (RT_SUCCESS(vrc))
6824 hrc = S_OK;
6825 else
6826 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Could not create the exported medium '%s'%s"),
6827 aFilename, i_vdError(vrc).c_str());
6828 VDDestroy(pDstHdd);
6829 }
6830 else
6831 hrc = setErrorVrc(vrc);
6832 }
6833 }
6834 VDDestroy(pSrcHdd);
6835 }
6836 else
6837 hrc = setErrorVrc(vrc, "VDInterfaceAdd -> %Rrc", vrc);
6838 VDIfDestroyFromVfsStream(pVfsIoIf);
6839 }
6840 else
6841 hrc = setErrorVrc(vrc, "VDIfCreateFromVfsStream -> %Rrc", vrc);
6842 }
6843 return hrc;
6844}
6845
6846/**
6847 * Used by IAppliance to import disk images.
6848 *
6849 * @param aFilename Filename to read (UTF8).
6850 * @param aFormat Medium format for reading @a aFilename.
6851 * @param aVariant Which exact image format variant to use
6852 * for the destination image.
6853 * @param aVfsIosSrc Handle to the source I/O stream.
6854 * @param aParent Parent medium. May be NULL.
6855 * @param aProgress Progress object to use.
6856 * @param aNotify Notify about mediums which metadatа are changed
6857 * during execution of the function.
6858 * @return
6859 * @note The destination format is defined by the Medium instance.
6860 *
6861 * @todo The only consumer of this method (Appliance::i_importOneDiskImage) is
6862 * already on a worker thread, so perhaps consider bypassing the thread
6863 * here and run in the task synchronously? VBoxSVC has enough threads as
6864 * it is...
6865 */
6866HRESULT Medium::i_importFile(const char *aFilename,
6867 const ComObjPtr<MediumFormat> &aFormat,
6868 MediumVariant_T aVariant,
6869 RTVFSIOSTREAM aVfsIosSrc,
6870 const ComObjPtr<Medium> &aParent,
6871 const ComObjPtr<Progress> &aProgress,
6872 bool aNotify)
6873{
6874 /** @todo r=klaus The code below needs to be double checked with regard
6875 * to lock order violations, it probably causes lock order issues related
6876 * to the AutoCaller usage. */
6877 AssertPtrReturn(aFilename, E_INVALIDARG);
6878 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
6879 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
6880
6881 AutoCaller autoCaller(this);
6882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6883
6884 HRESULT rc = S_OK;
6885 Medium::Task *pTask = NULL;
6886
6887 try
6888 {
6889 // locking: we need the tree lock first because we access parent pointers
6890 // and we need to write-lock the media involved
6891 uint32_t cHandles = 2;
6892 LockHandle* pHandles[3] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
6893 this->lockHandle() };
6894 /* Only add parent to the lock if it is not null */
6895 if (!aParent.isNull())
6896 pHandles[cHandles++] = aParent->lockHandle();
6897 AutoWriteLock alock(cHandles,
6898 pHandles
6899 COMMA_LOCKVAL_SRC_POS);
6900
6901 if ( m->state != MediumState_NotCreated
6902 && m->state != MediumState_Created)
6903 throw i_setStateError();
6904
6905 /* Build the target lock list. */
6906 MediumLockList *pTargetMediumLockList(new MediumLockList());
6907 alock.release();
6908 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6909 this /* pToLockWrite */,
6910 false /* fMediumLockWriteAll */,
6911 aParent,
6912 *pTargetMediumLockList);
6913 alock.acquire();
6914 if (FAILED(rc))
6915 {
6916 delete pTargetMediumLockList;
6917 throw rc;
6918 }
6919
6920 alock.release();
6921 rc = pTargetMediumLockList->Lock();
6922 alock.acquire();
6923 if (FAILED(rc))
6924 {
6925 delete pTargetMediumLockList;
6926 throw setError(rc,
6927 tr("Failed to lock target media '%s'"),
6928 i_getLocationFull().c_str());
6929 }
6930
6931 /* setup task object to carry out the operation asynchronously */
6932 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat, aVariant,
6933 aVfsIosSrc, aParent, pTargetMediumLockList, false, aNotify);
6934 rc = pTask->rc();
6935 AssertComRC(rc);
6936 if (FAILED(rc))
6937 throw rc;
6938
6939 if (m->state == MediumState_NotCreated)
6940 m->state = MediumState_Creating;
6941 }
6942 catch (HRESULT aRC) { rc = aRC; }
6943
6944 if (SUCCEEDED(rc))
6945 {
6946 rc = pTask->createThread();
6947 pTask = NULL;
6948 }
6949 else if (pTask != NULL)
6950 delete pTask;
6951
6952 return rc;
6953}
6954
6955/**
6956 * Internal version of the public CloneTo API which allows to enable certain
6957 * optimizations to improve speed during VM cloning.
6958 *
6959 * @param aTarget Target medium
6960 * @param aVariant Which exact image format variant to use
6961 * for the destination image.
6962 * @param aParent Parent medium. May be NULL.
6963 * @param aProgress Progress object to use.
6964 * @param idxSrcImageSame The last image in the source chain which has the
6965 * same content as the given image in the destination
6966 * chain. Use UINT32_MAX to disable this optimization.
6967 * @param idxDstImageSame The last image in the destination chain which has the
6968 * same content as the given image in the source chain.
6969 * Use UINT32_MAX to disable this optimization.
6970 * @param aNotify Notify about mediums which metadatа are changed
6971 * during execution of the function.
6972 * @return
6973 */
6974HRESULT Medium::i_cloneToEx(const ComObjPtr<Medium> &aTarget, MediumVariant_T aVariant,
6975 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
6976 uint32_t idxSrcImageSame, uint32_t idxDstImageSame, bool aNotify)
6977{
6978 /** @todo r=klaus The code below needs to be double checked with regard
6979 * to lock order violations, it probably causes lock order issues related
6980 * to the AutoCaller usage. */
6981 CheckComArgNotNull(aTarget);
6982 CheckComArgOutPointerValid(aProgress);
6983 ComAssertRet(aTarget != this, E_INVALIDARG);
6984
6985 AutoCaller autoCaller(this);
6986 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6987
6988 HRESULT rc = S_OK;
6989 ComObjPtr<Progress> pProgress;
6990 Medium::Task *pTask = NULL;
6991
6992 try
6993 {
6994 // locking: we need the tree lock first because we access parent pointers
6995 // and we need to write-lock the media involved
6996 uint32_t cHandles = 3;
6997 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
6998 this->lockHandle(),
6999 aTarget->lockHandle() };
7000 /* Only add parent to the lock if it is not null */
7001 if (!aParent.isNull())
7002 pHandles[cHandles++] = aParent->lockHandle();
7003 AutoWriteLock alock(cHandles,
7004 pHandles
7005 COMMA_LOCKVAL_SRC_POS);
7006
7007 if ( aTarget->m->state != MediumState_NotCreated
7008 && aTarget->m->state != MediumState_Created)
7009 throw aTarget->i_setStateError();
7010
7011 /* Build the source lock list. */
7012 MediumLockList *pSourceMediumLockList(new MediumLockList());
7013 alock.release();
7014 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
7015 NULL /* pToLockWrite */,
7016 false /* fMediumLockWriteAll */,
7017 NULL,
7018 *pSourceMediumLockList);
7019 alock.acquire();
7020 if (FAILED(rc))
7021 {
7022 delete pSourceMediumLockList;
7023 throw rc;
7024 }
7025
7026 /* Build the target lock list (including the to-be parent chain). */
7027 MediumLockList *pTargetMediumLockList(new MediumLockList());
7028 alock.release();
7029 rc = aTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
7030 aTarget /* pToLockWrite */,
7031 false /* fMediumLockWriteAll */,
7032 aParent,
7033 *pTargetMediumLockList);
7034 alock.acquire();
7035 if (FAILED(rc))
7036 {
7037 delete pSourceMediumLockList;
7038 delete pTargetMediumLockList;
7039 throw rc;
7040 }
7041
7042 alock.release();
7043 rc = pSourceMediumLockList->Lock();
7044 alock.acquire();
7045 if (FAILED(rc))
7046 {
7047 delete pSourceMediumLockList;
7048 delete pTargetMediumLockList;
7049 throw setError(rc,
7050 tr("Failed to lock source media '%s'"),
7051 i_getLocationFull().c_str());
7052 }
7053 alock.release();
7054 rc = pTargetMediumLockList->Lock();
7055 alock.acquire();
7056 if (FAILED(rc))
7057 {
7058 delete pSourceMediumLockList;
7059 delete pTargetMediumLockList;
7060 throw setError(rc,
7061 tr("Failed to lock target media '%s'"),
7062 aTarget->i_getLocationFull().c_str());
7063 }
7064
7065 pProgress.createObject();
7066 rc = pProgress->init(m->pVirtualBox,
7067 static_cast <IMedium *>(this),
7068 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
7069 TRUE /* aCancelable */);
7070 if (FAILED(rc))
7071 {
7072 delete pSourceMediumLockList;
7073 delete pTargetMediumLockList;
7074 throw rc;
7075 }
7076
7077 /* setup task object to carry out the operation asynchronously */
7078 pTask = new Medium::CloneTask(this, pProgress, aTarget, aVariant,
7079 aParent, idxSrcImageSame,
7080 idxDstImageSame, pSourceMediumLockList,
7081 pTargetMediumLockList, false, false, aNotify);
7082 rc = pTask->rc();
7083 AssertComRC(rc);
7084 if (FAILED(rc))
7085 throw rc;
7086
7087 if (aTarget->m->state == MediumState_NotCreated)
7088 aTarget->m->state = MediumState_Creating;
7089 }
7090 catch (HRESULT aRC) { rc = aRC; }
7091
7092 if (SUCCEEDED(rc))
7093 {
7094 rc = pTask->createThread();
7095 pTask = NULL;
7096 if (SUCCEEDED(rc))
7097 pProgress.queryInterfaceTo(aProgress);
7098 }
7099 else if (pTask != NULL)
7100 delete pTask;
7101
7102 return rc;
7103}
7104
7105/**
7106 * Returns the key identifier for this medium if encryption is configured.
7107 *
7108 * @returns Key identifier or empty string if no encryption is configured.
7109 */
7110const Utf8Str& Medium::i_getKeyId()
7111{
7112 ComObjPtr<Medium> pBase = i_getBase();
7113
7114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7115
7116 settings::StringsMap::const_iterator it = pBase->m->mapProperties.find("CRYPT/KeyId");
7117 if (it == pBase->m->mapProperties.end())
7118 return Utf8Str::Empty;
7119
7120 return it->second;
7121}
7122
7123
7124/**
7125 * Returns all filter related properties.
7126 *
7127 * @returns COM status code.
7128 * @param aReturnNames Where to store the properties names on success.
7129 * @param aReturnValues Where to store the properties values on success.
7130 */
7131HRESULT Medium::i_getFilterProperties(std::vector<com::Utf8Str> &aReturnNames,
7132 std::vector<com::Utf8Str> &aReturnValues)
7133{
7134 std::vector<com::Utf8Str> aPropNames;
7135 std::vector<com::Utf8Str> aPropValues;
7136 HRESULT hrc = getProperties(Utf8Str(""), aPropNames, aPropValues);
7137
7138 if (SUCCEEDED(hrc))
7139 {
7140 unsigned cReturnSize = 0;
7141 aReturnNames.resize(0);
7142 aReturnValues.resize(0);
7143 for (unsigned idx = 0; idx < aPropNames.size(); idx++)
7144 {
7145 if (i_isPropertyForFilter(aPropNames[idx]))
7146 {
7147 aReturnNames.resize(cReturnSize + 1);
7148 aReturnValues.resize(cReturnSize + 1);
7149 aReturnNames[cReturnSize] = aPropNames[idx];
7150 aReturnValues[cReturnSize] = aPropValues[idx];
7151 cReturnSize++;
7152 }
7153 }
7154 }
7155
7156 return hrc;
7157}
7158
7159/**
7160 * Preparation to move this medium to a new location
7161 *
7162 * @param aLocation Location of the storage unit. If the location is a FS-path,
7163 * then it can be relative to the VirtualBox home directory.
7164 *
7165 * @note Must be called from under this object's write lock.
7166 */
7167HRESULT Medium::i_preparationForMoving(const Utf8Str &aLocation)
7168{
7169 HRESULT rc = E_FAIL;
7170
7171 if (i_getLocationFull() != aLocation)
7172 {
7173 m->strNewLocationFull = aLocation;
7174 m->fMoveThisMedium = true;
7175 rc = S_OK;
7176 }
7177
7178 return rc;
7179}
7180
7181/**
7182 * Checking whether current operation "moving" or not
7183 */
7184bool Medium::i_isMoveOperation(const ComObjPtr<Medium> &aTarget) const
7185{
7186 RT_NOREF(aTarget);
7187 return m->fMoveThisMedium;
7188}
7189
7190bool Medium::i_resetMoveOperationData()
7191{
7192 m->strNewLocationFull.setNull();
7193 m->fMoveThisMedium = false;
7194 return true;
7195}
7196
7197Utf8Str Medium::i_getNewLocationForMoving() const
7198{
7199 if (m->fMoveThisMedium == true)
7200 return m->strNewLocationFull;
7201 else
7202 return Utf8Str();
7203}
7204////////////////////////////////////////////////////////////////////////////////
7205//
7206// Private methods
7207//
7208////////////////////////////////////////////////////////////////////////////////
7209
7210/**
7211 * Queries information from the medium.
7212 *
7213 * As a result of this call, the accessibility state and data members such as
7214 * size and description will be updated with the current information.
7215 *
7216 * @note This method may block during a system I/O call that checks storage
7217 * accessibility.
7218 *
7219 * @note Caller MUST NOT hold the media tree or medium lock.
7220 *
7221 * @note Locks m->pParent for reading. Locks this object for writing.
7222 *
7223 * @param fSetImageId Whether to reset the UUID contained in the image file
7224 * to the UUID in the medium instance data (see SetIDs())
7225 * @param fSetParentId Whether to reset the parent UUID contained in the image
7226 * file to the parent UUID in the medium instance data (see
7227 * SetIDs())
7228 * @param autoCaller
7229 * @return
7230 */
7231HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
7232{
7233 Assert(!isWriteLockOnCurrentThread());
7234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7235
7236 if ( ( m->state != MediumState_Created
7237 && m->state != MediumState_Inaccessible
7238 && m->state != MediumState_LockedRead)
7239 || m->fClosing)
7240 return E_FAIL;
7241
7242 HRESULT rc = S_OK;
7243
7244 int vrc = VINF_SUCCESS;
7245
7246 /* check if a blocking i_queryInfo() call is in progress on some other thread,
7247 * and wait for it to finish if so instead of querying data ourselves */
7248 if (m->queryInfoRunning)
7249 {
7250 Assert( m->state == MediumState_LockedRead
7251 || m->state == MediumState_LockedWrite);
7252
7253 while (m->queryInfoRunning)
7254 {
7255 alock.release();
7256 /* must not hold the object lock now */
7257 Assert(!isWriteLockOnCurrentThread());
7258 {
7259 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
7260 }
7261 alock.acquire();
7262 }
7263
7264 return S_OK;
7265 }
7266
7267 bool success = false;
7268 Utf8Str lastAccessError;
7269
7270 /* are we dealing with a new medium constructed using the existing
7271 * location? */
7272 bool isImport = m->id.isZero();
7273 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
7274
7275 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
7276 * media because that would prevent necessary modifications
7277 * when opening media of some third-party formats for the first
7278 * time in VirtualBox (such as VMDK for which VDOpen() needs to
7279 * generate an UUID if it is missing) */
7280 if ( m->hddOpenMode == OpenReadOnly
7281 || m->type == MediumType_Readonly
7282 || (!isImport && !fSetImageId && !fSetParentId)
7283 )
7284 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7285
7286 /* Open shareable medium with the appropriate flags */
7287 if (m->type == MediumType_Shareable)
7288 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7289
7290 /* Lock the medium, which makes the behavior much more consistent, must be
7291 * done before dropping the object lock and setting queryInfoRunning. */
7292 ComPtr<IToken> pToken;
7293 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
7294 rc = LockRead(pToken.asOutParam());
7295 else
7296 rc = LockWrite(pToken.asOutParam());
7297 if (FAILED(rc)) return rc;
7298
7299 /* Copies of the input state fields which are not read-only,
7300 * as we're dropping the lock. CAUTION: be extremely careful what
7301 * you do with the contents of this medium object, as you will
7302 * create races if there are concurrent changes. */
7303 Utf8Str format(m->strFormat);
7304 Utf8Str location(m->strLocationFull);
7305 ComObjPtr<MediumFormat> formatObj = m->formatObj;
7306
7307 /* "Output" values which can't be set because the lock isn't held
7308 * at the time the values are determined. */
7309 Guid mediumId = m->id;
7310 uint64_t mediumSize = 0;
7311 uint64_t mediumLogicalSize = 0;
7312
7313 /* Flag whether a base image has a non-zero parent UUID and thus
7314 * need repairing after it was closed again. */
7315 bool fRepairImageZeroParentUuid = false;
7316
7317 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
7318
7319 /* must be set before leaving the object lock the first time */
7320 m->queryInfoRunning = true;
7321
7322 /* must leave object lock now, because a lock from a higher lock class
7323 * is needed and also a lengthy operation is coming */
7324 alock.release();
7325 autoCaller.release();
7326
7327 /* Note that taking the queryInfoSem after leaving the object lock above
7328 * can lead to short spinning of the loops waiting for i_queryInfo() to
7329 * complete. This is unavoidable since the other order causes a lock order
7330 * violation: here it would be requesting the object lock (at the beginning
7331 * of the method), then queryInfoSem, and below the other way round. */
7332 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
7333
7334 /* take the opportunity to have a media tree lock, released initially */
7335 Assert(!isWriteLockOnCurrentThread());
7336 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7337 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7338 treeLock.release();
7339
7340 /* re-take the caller, but not the object lock, to keep uninit away */
7341 autoCaller.add();
7342 if (FAILED(autoCaller.rc()))
7343 {
7344 m->queryInfoRunning = false;
7345 return autoCaller.rc();
7346 }
7347
7348 try
7349 {
7350 /* skip accessibility checks for host drives */
7351 if (m->hostDrive)
7352 {
7353 success = true;
7354 throw S_OK;
7355 }
7356
7357 PVDISK hdd;
7358 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7359 ComAssertRCThrow(vrc, E_FAIL);
7360
7361 try
7362 {
7363 /** @todo This kind of opening of media is assuming that diff
7364 * media can be opened as base media. Should be documented that
7365 * it must work for all medium format backends. */
7366 vrc = VDOpen(hdd,
7367 format.c_str(),
7368 location.c_str(),
7369 uOpenFlags | m->uOpenFlagsDef,
7370 m->vdImageIfaces);
7371 if (RT_FAILURE(vrc))
7372 {
7373 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
7374 location.c_str(), i_vdError(vrc).c_str());
7375 throw S_OK;
7376 }
7377
7378 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
7379 {
7380 /* Modify the UUIDs if necessary. The associated fields are
7381 * not modified by other code, so no need to copy. */
7382 if (fSetImageId)
7383 {
7384 alock.acquire();
7385 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
7386 alock.release();
7387 if (RT_FAILURE(vrc))
7388 {
7389 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
7390 location.c_str(), i_vdError(vrc).c_str());
7391 throw S_OK;
7392 }
7393 mediumId = m->uuidImage;
7394 }
7395 if (fSetParentId)
7396 {
7397 alock.acquire();
7398 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
7399 alock.release();
7400 if (RT_FAILURE(vrc))
7401 {
7402 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
7403 location.c_str(), i_vdError(vrc).c_str());
7404 throw S_OK;
7405 }
7406 }
7407 /* zap the information, these are no long-term members */
7408 alock.acquire();
7409 unconst(m->uuidImage).clear();
7410 unconst(m->uuidParentImage).clear();
7411 alock.release();
7412
7413 /* check the UUID */
7414 RTUUID uuid;
7415 vrc = VDGetUuid(hdd, 0, &uuid);
7416 ComAssertRCThrow(vrc, E_FAIL);
7417
7418 if (isImport)
7419 {
7420 mediumId = uuid;
7421
7422 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
7423 // only when importing a VDMK that has no UUID, create one in memory
7424 mediumId.create();
7425 }
7426 else
7427 {
7428 Assert(!mediumId.isZero());
7429
7430 if (mediumId != uuid)
7431 {
7432 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7433 lastAccessError = Utf8StrFmt(
7434 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
7435 &uuid,
7436 location.c_str(),
7437 mediumId.raw(),
7438 pVirtualBox->i_settingsFilePath().c_str());
7439 throw S_OK;
7440 }
7441 }
7442 }
7443 else
7444 {
7445 /* the backend does not support storing UUIDs within the
7446 * underlying storage so use what we store in XML */
7447
7448 if (fSetImageId)
7449 {
7450 /* set the UUID if an API client wants to change it */
7451 alock.acquire();
7452 mediumId = m->uuidImage;
7453 alock.release();
7454 }
7455 else if (isImport)
7456 {
7457 /* generate an UUID for an imported UUID-less medium */
7458 mediumId.create();
7459 }
7460 }
7461
7462 /* set the image uuid before the below parent uuid handling code
7463 * might place it somewhere in the media tree, so that the medium
7464 * UUID is valid at this point */
7465 alock.acquire();
7466 if (isImport || fSetImageId)
7467 unconst(m->id) = mediumId;
7468 alock.release();
7469
7470 /* get the medium variant */
7471 unsigned uImageFlags;
7472 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7473 ComAssertRCThrow(vrc, E_FAIL);
7474 alock.acquire();
7475 m->variant = (MediumVariant_T)uImageFlags;
7476 alock.release();
7477
7478 /* check/get the parent uuid and update corresponding state */
7479 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
7480 {
7481 RTUUID parentId;
7482 vrc = VDGetParentUuid(hdd, 0, &parentId);
7483 ComAssertRCThrow(vrc, E_FAIL);
7484
7485 /* streamOptimized VMDK images are only accepted as base
7486 * images, as this allows automatic repair of OVF appliances.
7487 * Since such images don't support random writes they will not
7488 * be created for diff images. Only an overly smart user might
7489 * manually create this case. Too bad for him. */
7490 if ( (isImport || fSetParentId)
7491 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
7492 {
7493 /* the parent must be known to us. Note that we freely
7494 * call locking methods of mVirtualBox and parent, as all
7495 * relevant locks must be already held. There may be no
7496 * concurrent access to the just opened medium on other
7497 * threads yet (and init() will fail if this method reports
7498 * MediumState_Inaccessible) */
7499
7500 ComObjPtr<Medium> pParent;
7501 if (RTUuidIsNull(&parentId))
7502 rc = VBOX_E_OBJECT_NOT_FOUND;
7503 else
7504 rc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
7505 if (FAILED(rc))
7506 {
7507 if (fSetImageId && !fSetParentId)
7508 {
7509 /* If the image UUID gets changed for an existing
7510 * image then the parent UUID can be stale. In such
7511 * cases clear the parent information. The parent
7512 * information may/will be re-set later if the
7513 * API client wants to adjust a complete medium
7514 * hierarchy one by one. */
7515 rc = S_OK;
7516 alock.acquire();
7517 RTUuidClear(&parentId);
7518 vrc = VDSetParentUuid(hdd, 0, &parentId);
7519 alock.release();
7520 ComAssertRCThrow(vrc, E_FAIL);
7521 }
7522 else
7523 {
7524 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
7525 &parentId, location.c_str(),
7526 pVirtualBox->i_settingsFilePath().c_str());
7527 throw S_OK;
7528 }
7529 }
7530
7531 /* must drop the caller before taking the tree lock */
7532 autoCaller.release();
7533 /* we set m->pParent & children() */
7534 treeLock.acquire();
7535 autoCaller.add();
7536 if (FAILED(autoCaller.rc()))
7537 throw autoCaller.rc();
7538
7539 if (m->pParent)
7540 i_deparent();
7541
7542 if (!pParent.isNull())
7543 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7544 {
7545 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
7546 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7547 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"),
7548 pParent->m->strLocationFull.c_str());
7549 }
7550 i_setParent(pParent);
7551
7552 treeLock.release();
7553 }
7554 else
7555 {
7556 /* must drop the caller before taking the tree lock */
7557 autoCaller.release();
7558 /* we access m->pParent */
7559 treeLock.acquire();
7560 autoCaller.add();
7561 if (FAILED(autoCaller.rc()))
7562 throw autoCaller.rc();
7563
7564 /* check that parent UUIDs match. Note that there's no need
7565 * for the parent's AutoCaller (our lifetime is bound to
7566 * it) */
7567
7568 if (m->pParent.isNull())
7569 {
7570 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
7571 * and 3.1.0-3.1.8 there are base images out there
7572 * which have a non-zero parent UUID. No point in
7573 * complaining about them, instead automatically
7574 * repair the problem. Later we can bring back the
7575 * error message, but we should wait until really
7576 * most users have repaired their images, either with
7577 * VBoxFixHdd or this way. */
7578#if 1
7579 fRepairImageZeroParentUuid = true;
7580#else /* 0 */
7581 lastAccessError = Utf8StrFmt(
7582 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
7583 location.c_str(),
7584 pVirtualBox->settingsFilePath().c_str());
7585 treeLock.release();
7586 throw S_OK;
7587#endif /* 0 */
7588 }
7589
7590 {
7591 autoCaller.release();
7592 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
7593 autoCaller.add();
7594 if (FAILED(autoCaller.rc()))
7595 throw autoCaller.rc();
7596
7597 if ( !fRepairImageZeroParentUuid
7598 && m->pParent->i_getState() != MediumState_Inaccessible
7599 && m->pParent->i_getId() != parentId)
7600 {
7601 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7602 lastAccessError = Utf8StrFmt(
7603 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
7604 &parentId, location.c_str(),
7605 m->pParent->i_getId().raw(),
7606 pVirtualBox->i_settingsFilePath().c_str());
7607 parentLock.release();
7608 treeLock.release();
7609 throw S_OK;
7610 }
7611 }
7612
7613 /// @todo NEWMEDIA what to do if the parent is not
7614 /// accessible while the diff is? Probably nothing. The
7615 /// real code will detect the mismatch anyway.
7616
7617 treeLock.release();
7618 }
7619 }
7620
7621 mediumSize = VDGetFileSize(hdd, 0);
7622 mediumLogicalSize = VDGetSize(hdd, 0);
7623
7624 success = true;
7625 }
7626 catch (HRESULT aRC)
7627 {
7628 rc = aRC;
7629 }
7630
7631 vrc = VDDestroy(hdd);
7632 if (RT_FAILURE(vrc))
7633 {
7634 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
7635 location.c_str(), i_vdError(vrc).c_str());
7636 success = false;
7637 throw S_OK;
7638 }
7639 }
7640 catch (HRESULT aRC)
7641 {
7642 rc = aRC;
7643 }
7644
7645 autoCaller.release();
7646 treeLock.acquire();
7647 autoCaller.add();
7648 if (FAILED(autoCaller.rc()))
7649 {
7650 m->queryInfoRunning = false;
7651 return autoCaller.rc();
7652 }
7653 alock.acquire();
7654
7655 if (success)
7656 {
7657 m->size = mediumSize;
7658 m->logicalSize = mediumLogicalSize;
7659 m->strLastAccessError.setNull();
7660 }
7661 else
7662 {
7663 m->strLastAccessError = lastAccessError;
7664 Log1WarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
7665 location.c_str(), m->strLastAccessError.c_str(), rc, vrc));
7666 }
7667
7668 /* Set the proper state according to the result of the check */
7669 if (success)
7670 m->preLockState = MediumState_Created;
7671 else
7672 m->preLockState = MediumState_Inaccessible;
7673
7674 /* unblock anyone waiting for the i_queryInfo results */
7675 qlock.release();
7676 m->queryInfoRunning = false;
7677
7678 pToken->Abandon();
7679 pToken.setNull();
7680
7681 if (FAILED(rc))
7682 return rc;
7683
7684 /* If this is a base image which incorrectly has a parent UUID set,
7685 * repair the image now by zeroing the parent UUID. This is only done
7686 * when we have structural information from a config file, on import
7687 * this is not possible. If someone would accidentally call openMedium
7688 * with a diff image before the base is registered this would destroy
7689 * the diff. Not acceptable. */
7690 do
7691 {
7692 if (fRepairImageZeroParentUuid)
7693 {
7694 rc = LockWrite(pToken.asOutParam());
7695 if (FAILED(rc))
7696 break;
7697
7698 alock.release();
7699
7700 try
7701 {
7702 PVDISK hdd;
7703 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7704 ComAssertRCThrow(vrc, E_FAIL);
7705
7706 try
7707 {
7708 vrc = VDOpen(hdd,
7709 format.c_str(),
7710 location.c_str(),
7711 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
7712 m->vdImageIfaces);
7713 if (RT_FAILURE(vrc))
7714 throw S_OK;
7715
7716 RTUUID zeroParentUuid;
7717 RTUuidClear(&zeroParentUuid);
7718 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
7719 ComAssertRCThrow(vrc, E_FAIL);
7720 }
7721 catch (HRESULT aRC)
7722 {
7723 rc = aRC;
7724 }
7725
7726 VDDestroy(hdd);
7727 }
7728 catch (HRESULT aRC)
7729 {
7730 rc = aRC;
7731 }
7732
7733 pToken->Abandon();
7734 pToken.setNull();
7735 if (FAILED(rc))
7736 break;
7737 }
7738 } while(0);
7739
7740 return rc;
7741}
7742
7743/**
7744 * Performs extra checks if the medium can be closed and returns S_OK in
7745 * this case. Otherwise, returns a respective error message. Called by
7746 * Close() under the medium tree lock and the medium lock.
7747 *
7748 * @note Also reused by Medium::Reset().
7749 *
7750 * @note Caller must hold the media tree write lock!
7751 */
7752HRESULT Medium::i_canClose()
7753{
7754 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7755
7756 if (i_getChildren().size() != 0)
7757 return setError(VBOX_E_OBJECT_IN_USE,
7758 tr("Cannot close medium '%s' because it has %d child media", "", i_getChildren().size()),
7759 m->strLocationFull.c_str(), i_getChildren().size());
7760
7761 return S_OK;
7762}
7763
7764/**
7765 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
7766 *
7767 * @note Caller must have locked the media tree lock for writing!
7768 */
7769HRESULT Medium::i_unregisterWithVirtualBox()
7770{
7771 /* Note that we need to de-associate ourselves from the parent to let
7772 * VirtualBox::i_unregisterMedium() properly save the registry */
7773
7774 /* we modify m->pParent and access children */
7775 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7776
7777 Medium *pParentBackup = m->pParent;
7778 AssertReturn(i_getChildren().size() == 0, E_FAIL);
7779 if (m->pParent)
7780 i_deparent();
7781
7782 HRESULT rc = m->pVirtualBox->i_unregisterMedium(this);
7783 if (FAILED(rc))
7784 {
7785 if (pParentBackup)
7786 {
7787 // re-associate with the parent as we are still relatives in the registry
7788 i_setParent(pParentBackup);
7789 }
7790 }
7791
7792 return rc;
7793}
7794
7795/**
7796 * Like SetProperty but do not trigger a settings store. Only for internal use!
7797 */
7798HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
7799{
7800 AutoCaller autoCaller(this);
7801 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7802
7803 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
7804
7805 switch (m->state)
7806 {
7807 case MediumState_Created:
7808 case MediumState_Inaccessible:
7809 break;
7810 default:
7811 return i_setStateError();
7812 }
7813
7814 m->mapProperties[aName] = aValue;
7815
7816 return S_OK;
7817}
7818
7819/**
7820 * Sets the extended error info according to the current media state.
7821 *
7822 * @note Must be called from under this object's write or read lock.
7823 */
7824HRESULT Medium::i_setStateError()
7825{
7826 HRESULT rc = E_FAIL;
7827
7828 switch (m->state)
7829 {
7830 case MediumState_NotCreated:
7831 {
7832 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7833 tr("Storage for the medium '%s' is not created"),
7834 m->strLocationFull.c_str());
7835 break;
7836 }
7837 case MediumState_Created:
7838 {
7839 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7840 tr("Storage for the medium '%s' is already created"),
7841 m->strLocationFull.c_str());
7842 break;
7843 }
7844 case MediumState_LockedRead:
7845 {
7846 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7847 tr("Medium '%s' is locked for reading by another task"),
7848 m->strLocationFull.c_str());
7849 break;
7850 }
7851 case MediumState_LockedWrite:
7852 {
7853 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7854 tr("Medium '%s' is locked for writing by another task"),
7855 m->strLocationFull.c_str());
7856 break;
7857 }
7858 case MediumState_Inaccessible:
7859 {
7860 /* be in sync with Console::powerUpThread() */
7861 if (!m->strLastAccessError.isEmpty())
7862 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7863 tr("Medium '%s' is not accessible. %s"),
7864 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
7865 else
7866 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7867 tr("Medium '%s' is not accessible"),
7868 m->strLocationFull.c_str());
7869 break;
7870 }
7871 case MediumState_Creating:
7872 {
7873 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7874 tr("Storage for the medium '%s' is being created"),
7875 m->strLocationFull.c_str());
7876 break;
7877 }
7878 case MediumState_Deleting:
7879 {
7880 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7881 tr("Storage for the medium '%s' is being deleted"),
7882 m->strLocationFull.c_str());
7883 break;
7884 }
7885 default:
7886 {
7887 AssertFailed();
7888 break;
7889 }
7890 }
7891
7892 return rc;
7893}
7894
7895/**
7896 * Sets the value of m->strLocationFull. The given location must be a fully
7897 * qualified path; relative paths are not supported here.
7898 *
7899 * As a special exception, if the specified location is a file path that ends with '/'
7900 * then the file name part will be generated by this method automatically in the format
7901 * '{\<uuid\>}.\<ext\>' where \<uuid\> is a fresh UUID that this method will generate
7902 * and assign to this medium, and \<ext\> is the default extension for this
7903 * medium's storage format. Note that this procedure requires the media state to
7904 * be NotCreated and will return a failure otherwise.
7905 *
7906 * @param aLocation Location of the storage unit. If the location is a FS-path,
7907 * then it can be relative to the VirtualBox home directory.
7908 * @param aFormat Optional fallback format if it is an import and the format
7909 * cannot be determined.
7910 *
7911 * @note Must be called from under this object's write lock.
7912 */
7913HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
7914 const Utf8Str &aFormat /* = Utf8Str::Empty */)
7915{
7916 AssertReturn(!aLocation.isEmpty(), E_FAIL);
7917
7918 AutoCaller autoCaller(this);
7919 AssertComRCReturnRC(autoCaller.rc());
7920
7921 /* formatObj may be null only when initializing from an existing path and
7922 * no format is known yet */
7923 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
7924 || ( getObjectState().getState() == ObjectState::InInit
7925 && m->state != MediumState_NotCreated
7926 && m->id.isZero()
7927 && m->strFormat.isEmpty()
7928 && m->formatObj.isNull()),
7929 E_FAIL);
7930
7931 /* are we dealing with a new medium constructed using the existing
7932 * location? */
7933 bool isImport = m->strFormat.isEmpty();
7934
7935 if ( isImport
7936 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7937 && !m->hostDrive))
7938 {
7939 Guid id;
7940
7941 Utf8Str locationFull(aLocation);
7942
7943 if (m->state == MediumState_NotCreated)
7944 {
7945 /* must be a file (formatObj must be already known) */
7946 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
7947
7948 if (RTPathFilename(aLocation.c_str()) == NULL)
7949 {
7950 /* no file name is given (either an empty string or ends with a
7951 * slash), generate a new UUID + file name if the state allows
7952 * this */
7953
7954 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
7955 (tr("Must be at least one extension if it is MediumFormatCapabilities_File\n")),
7956 E_FAIL);
7957
7958 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
7959 ComAssertMsgRet(!strExt.isEmpty(),
7960 (tr("Default extension must not be empty\n")),
7961 E_FAIL);
7962
7963 id.create();
7964
7965 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
7966 aLocation.c_str(), id.raw(), strExt.c_str());
7967 }
7968 }
7969
7970 // we must always have full paths now (if it refers to a file)
7971 if ( ( m->formatObj.isNull()
7972 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7973 && !RTPathStartsWithRoot(locationFull.c_str()))
7974 return setError(VBOX_E_FILE_ERROR,
7975 tr("The given path '%s' is not fully qualified"),
7976 locationFull.c_str());
7977
7978 /* detect the backend from the storage unit if importing */
7979 if (isImport)
7980 {
7981 VDTYPE const enmDesiredType = i_convertDeviceType();
7982 VDTYPE enmType = VDTYPE_INVALID;
7983 char *backendName = NULL;
7984
7985 /* is it a file? */
7986 RTFILE hFile;
7987 int vrc = RTFileOpen(&hFile, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
7988 if (RT_SUCCESS(vrc))
7989 {
7990 RTFileClose(hFile);
7991 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7992 locationFull.c_str(), enmDesiredType, &backendName, &enmType);
7993 }
7994 else if ( vrc != VERR_FILE_NOT_FOUND
7995 && vrc != VERR_PATH_NOT_FOUND
7996 && vrc != VERR_ACCESS_DENIED
7997 && locationFull != aLocation)
7998 {
7999 /* assume it's not a file, restore the original location */
8000 locationFull = aLocation;
8001 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
8002 locationFull.c_str(), enmDesiredType, &backendName, &enmType);
8003 }
8004
8005 if (RT_FAILURE(vrc))
8006 {
8007 if (vrc == VERR_ACCESS_DENIED)
8008 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8009 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
8010 locationFull.c_str(), vrc);
8011 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
8012 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8013 tr("Could not find file for the medium '%s' (%Rrc)"),
8014 locationFull.c_str(), vrc);
8015 if (aFormat.isEmpty())
8016 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8017 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
8018 locationFull.c_str(), vrc);
8019 HRESULT rc = i_setFormat(aFormat);
8020 /* setFormat() must not fail since we've just used the backend so
8021 * the format object must be there */
8022 AssertComRCReturnRC(rc);
8023 }
8024 else if ( enmType == VDTYPE_INVALID
8025 || m->devType != i_convertToDeviceType(enmType))
8026 {
8027 /*
8028 * The user tried to use a image as a device which is not supported
8029 * by the backend.
8030 */
8031 RTStrFree(backendName);
8032 return setError(E_FAIL,
8033 tr("The medium '%s' can't be used as the requested device type (%s, detected %s)"),
8034 locationFull.c_str(), getDeviceTypeName(m->devType), getVDTypeName(enmType));
8035 }
8036 else
8037 {
8038 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
8039
8040 HRESULT rc = i_setFormat(backendName);
8041 RTStrFree(backendName);
8042
8043 /* setFormat() must not fail since we've just used the backend so
8044 * the format object must be there */
8045 AssertComRCReturnRC(rc);
8046 }
8047 }
8048
8049 m->strLocationFull = locationFull;
8050
8051 /* is it still a file? */
8052 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
8053 && (m->state == MediumState_NotCreated)
8054 )
8055 /* assign a new UUID (this UUID will be used when calling
8056 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
8057 * also do that if we didn't generate it to make sure it is
8058 * either generated by us or reset to null */
8059 unconst(m->id) = id;
8060 }
8061 else
8062 m->strLocationFull = aLocation;
8063
8064 return S_OK;
8065}
8066
8067/**
8068 * Checks that the format ID is valid and sets it on success.
8069 *
8070 * Note that this method will caller-reference the format object on success!
8071 * This reference must be released somewhere to let the MediumFormat object be
8072 * uninitialized.
8073 *
8074 * @note Must be called from under this object's write lock.
8075 */
8076HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
8077{
8078 /* get the format object first */
8079 {
8080 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
8081 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
8082
8083 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
8084 if (m->formatObj.isNull())
8085 return setError(E_INVALIDARG,
8086 tr("Invalid medium storage format '%s'"),
8087 aFormat.c_str());
8088
8089 /* get properties (preinsert them as keys in the map). Note that the
8090 * map doesn't grow over the object life time since the set of
8091 * properties is meant to be constant. */
8092
8093 Assert(m->mapProperties.empty());
8094
8095 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
8096 it != m->formatObj->i_getProperties().end();
8097 ++it)
8098 {
8099 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
8100 }
8101 }
8102
8103 unconst(m->strFormat) = aFormat;
8104
8105 return S_OK;
8106}
8107
8108/**
8109 * Converts the Medium device type to the VD type.
8110 */
8111VDTYPE Medium::i_convertDeviceType()
8112{
8113 VDTYPE enmType;
8114
8115 switch (m->devType)
8116 {
8117 case DeviceType_HardDisk:
8118 enmType = VDTYPE_HDD;
8119 break;
8120 case DeviceType_DVD:
8121 enmType = VDTYPE_OPTICAL_DISC;
8122 break;
8123 case DeviceType_Floppy:
8124 enmType = VDTYPE_FLOPPY;
8125 break;
8126 default:
8127 ComAssertFailedRet(VDTYPE_INVALID);
8128 }
8129
8130 return enmType;
8131}
8132
8133/**
8134 * Converts from the VD type to the medium type.
8135 */
8136DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
8137{
8138 DeviceType_T devType;
8139
8140 switch (enmType)
8141 {
8142 case VDTYPE_HDD:
8143 devType = DeviceType_HardDisk;
8144 break;
8145 case VDTYPE_OPTICAL_DISC:
8146 devType = DeviceType_DVD;
8147 break;
8148 case VDTYPE_FLOPPY:
8149 devType = DeviceType_Floppy;
8150 break;
8151 default:
8152 ComAssertFailedRet(DeviceType_Null);
8153 }
8154
8155 return devType;
8156}
8157
8158/**
8159 * Internal method which checks whether a property name is for a filter plugin.
8160 */
8161bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
8162{
8163 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
8164 size_t offSlash;
8165 if ((offSlash = aName.find("/", 0)) != aName.npos)
8166 {
8167 com::Utf8Str strFilter;
8168 com::Utf8Str strKey;
8169
8170 HRESULT rc = strFilter.assignEx(aName, 0, offSlash);
8171 if (FAILED(rc))
8172 return false;
8173
8174 rc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
8175 if (FAILED(rc))
8176 return false;
8177
8178 VDFILTERINFO FilterInfo;
8179 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
8180 if (RT_SUCCESS(vrc))
8181 {
8182 /* Check that the property exists. */
8183 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
8184 while (paConfig->pszKey)
8185 {
8186 if (strKey.equals(paConfig->pszKey))
8187 return true;
8188 paConfig++;
8189 }
8190 }
8191 }
8192
8193 return false;
8194}
8195
8196/**
8197 * Returns the last error message collected by the i_vdErrorCall callback and
8198 * resets it.
8199 *
8200 * The error message is returned prepended with a dot and a space, like this:
8201 * <code>
8202 * ". <error_text> (%Rrc)"
8203 * </code>
8204 * to make it easily appendable to a more general error message. The @c %Rrc
8205 * format string is given @a aVRC as an argument.
8206 *
8207 * If there is no last error message collected by i_vdErrorCall or if it is a
8208 * null or empty string, then this function returns the following text:
8209 * <code>
8210 * " (%Rrc)"
8211 * </code>
8212 *
8213 * @note Doesn't do any object locking; it is assumed that the caller makes sure
8214 * the callback isn't called by more than one thread at a time.
8215 *
8216 * @param aVRC VBox error code to use when no error message is provided.
8217 */
8218Utf8Str Medium::i_vdError(int aVRC)
8219{
8220 Utf8Str error;
8221
8222 if (m->vdError.isEmpty())
8223 error = Utf8StrFmt(" (%Rrc)", aVRC);
8224 else
8225 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
8226
8227 m->vdError.setNull();
8228
8229 return error;
8230}
8231
8232/**
8233 * Error message callback.
8234 *
8235 * Puts the reported error message to the m->vdError field.
8236 *
8237 * @note Doesn't do any object locking; it is assumed that the caller makes sure
8238 * the callback isn't called by more than one thread at a time.
8239 *
8240 * @param pvUser The opaque data passed on container creation.
8241 * @param rc The VBox error code.
8242 * @param SRC_POS Use RT_SRC_POS.
8243 * @param pszFormat Error message format string.
8244 * @param va Error message arguments.
8245 */
8246/*static*/
8247DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
8248 const char *pszFormat, va_list va)
8249{
8250 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
8251
8252 Medium *that = static_cast<Medium*>(pvUser);
8253 AssertReturnVoid(that != NULL);
8254
8255 if (that->m->vdError.isEmpty())
8256 that->m->vdError =
8257 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
8258 else
8259 that->m->vdError =
8260 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
8261 Utf8Str(pszFormat, va).c_str(), rc);
8262}
8263
8264/* static */
8265DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
8266 const char * /* pszzValid */)
8267{
8268 Medium *that = static_cast<Medium*>(pvUser);
8269 AssertReturn(that != NULL, false);
8270
8271 /* we always return true since the only keys we have are those found in
8272 * VDBACKENDINFO */
8273 return true;
8274}
8275
8276/* static */
8277DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
8278 const char *pszName,
8279 size_t *pcbValue)
8280{
8281 AssertPtrReturn(pcbValue, VERR_INVALID_POINTER);
8282
8283 Medium *that = static_cast<Medium*>(pvUser);
8284 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
8285
8286 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
8287 if (it == that->m->mapProperties.end())
8288 return VERR_CFGM_VALUE_NOT_FOUND;
8289
8290 /* we interpret null values as "no value" in Medium */
8291 if (it->second.isEmpty())
8292 return VERR_CFGM_VALUE_NOT_FOUND;
8293
8294 *pcbValue = it->second.length() + 1 /* include terminator */;
8295
8296 return VINF_SUCCESS;
8297}
8298
8299/* static */
8300DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
8301 const char *pszName,
8302 char *pszValue,
8303 size_t cchValue)
8304{
8305 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
8306
8307 Medium *that = static_cast<Medium*>(pvUser);
8308 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
8309
8310 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
8311 if (it == that->m->mapProperties.end())
8312 return VERR_CFGM_VALUE_NOT_FOUND;
8313
8314 /* we interpret null values as "no value" in Medium */
8315 if (it->second.isEmpty())
8316 return VERR_CFGM_VALUE_NOT_FOUND;
8317
8318 const Utf8Str &value = it->second;
8319 if (value.length() >= cchValue)
8320 return VERR_CFGM_NOT_ENOUGH_SPACE;
8321
8322 memcpy(pszValue, value.c_str(), value.length() + 1);
8323
8324 return VINF_SUCCESS;
8325}
8326
8327DECLCALLBACK(bool) Medium::i_vdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid)
8328{
8329 /* Just return always true here. */
8330 NOREF(pvUser);
8331 NOREF(pszzValid);
8332 return true;
8333}
8334
8335DECLCALLBACK(int) Medium::i_vdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
8336{
8337 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8338 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8339 AssertPtrReturn(pcbValue, VERR_INVALID_POINTER);
8340
8341 size_t cbValue = 0;
8342 if (!strcmp(pszName, "Algorithm"))
8343 cbValue = strlen(pSettings->pszCipher) + 1;
8344 else if (!strcmp(pszName, "KeyId"))
8345 cbValue = sizeof("irrelevant");
8346 else if (!strcmp(pszName, "KeyStore"))
8347 {
8348 if (!pSettings->pszKeyStoreLoad)
8349 return VERR_CFGM_VALUE_NOT_FOUND;
8350 cbValue = strlen(pSettings->pszKeyStoreLoad) + 1;
8351 }
8352 else if (!strcmp(pszName, "CreateKeyStore"))
8353 cbValue = 2; /* Single digit + terminator. */
8354 else
8355 return VERR_CFGM_VALUE_NOT_FOUND;
8356
8357 *pcbValue = cbValue + 1 /* include terminator */;
8358
8359 return VINF_SUCCESS;
8360}
8361
8362DECLCALLBACK(int) Medium::i_vdCryptoConfigQuery(void *pvUser, const char *pszName,
8363 char *pszValue, size_t cchValue)
8364{
8365 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8366 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8367 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
8368
8369 const char *psz = NULL;
8370 if (!strcmp(pszName, "Algorithm"))
8371 psz = pSettings->pszCipher;
8372 else if (!strcmp(pszName, "KeyId"))
8373 psz = "irrelevant";
8374 else if (!strcmp(pszName, "KeyStore"))
8375 psz = pSettings->pszKeyStoreLoad;
8376 else if (!strcmp(pszName, "CreateKeyStore"))
8377 {
8378 if (pSettings->fCreateKeyStore)
8379 psz = "1";
8380 else
8381 psz = "0";
8382 }
8383 else
8384 return VERR_CFGM_VALUE_NOT_FOUND;
8385
8386 size_t cch = strlen(psz);
8387 if (cch >= cchValue)
8388 return VERR_CFGM_NOT_ENOUGH_SPACE;
8389
8390 memcpy(pszValue, psz, cch + 1);
8391 return VINF_SUCCESS;
8392}
8393
8394DECLCALLBACK(int) Medium::i_vdConfigUpdate(void *pvUser,
8395 bool fCreate,
8396 const char *pszName,
8397 const char *pszValue)
8398{
8399 Medium *that = (Medium *)pvUser;
8400
8401 // Detect if this runs inside i_queryInfo() on the current thread.
8402 // Skip if not. Check does not need synchronization.
8403 if (!that->m || !that->m->queryInfoRunning || !that->m->queryInfoSem.isWriteLockOnCurrentThread())
8404 return VINF_SUCCESS;
8405
8406 // It's guaranteed that this code is executing inside Medium::i_queryInfo,
8407 // can assume it took care of synchronization.
8408 int rv = VINF_SUCCESS;
8409 Utf8Str strName(pszName);
8410 settings::StringsMap::const_iterator it = that->m->mapProperties.find(strName);
8411 if (it == that->m->mapProperties.end() && !fCreate)
8412 rv = VERR_CFGM_VALUE_NOT_FOUND;
8413 else
8414 that->m->mapProperties[strName] = Utf8Str(pszValue);
8415 return rv;
8416}
8417
8418DECLCALLBACK(int) Medium::i_vdCryptoKeyRetain(void *pvUser, const char *pszId,
8419 const uint8_t **ppbKey, size_t *pcbKey)
8420{
8421 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8422 NOREF(pszId);
8423 NOREF(ppbKey);
8424 NOREF(pcbKey);
8425 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8426 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8427}
8428
8429DECLCALLBACK(int) Medium::i_vdCryptoKeyRelease(void *pvUser, const char *pszId)
8430{
8431 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8432 NOREF(pszId);
8433 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8434 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8435}
8436
8437DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
8438{
8439 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8440 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8441
8442 NOREF(pszId);
8443 *ppszPassword = pSettings->pszPassword;
8444 return VINF_SUCCESS;
8445}
8446
8447DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
8448{
8449 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8450 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8451 NOREF(pszId);
8452 return VINF_SUCCESS;
8453}
8454
8455DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore)
8456{
8457 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8458 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8459
8460 pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore);
8461 if (!pSettings->pszKeyStore)
8462 return VERR_NO_MEMORY;
8463
8464 memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore);
8465 return VINF_SUCCESS;
8466}
8467
8468DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher,
8469 const uint8_t *pbDek, size_t cbDek)
8470{
8471 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8472 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8473
8474 pSettings->pszCipherReturned = RTStrDup(pszCipher);
8475 pSettings->pbDek = pbDek;
8476 pSettings->cbDek = cbDek;
8477
8478 return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY;
8479}
8480
8481/**
8482 * Creates a VDISK instance for this medium.
8483 *
8484 * @note Caller should not hold any medium related locks as this method will
8485 * acquire the medium lock for writing and others (VirtualBox).
8486 *
8487 * @returns COM status code.
8488 * @param fWritable Whether to return a writable VDISK instance
8489 * (true) or a read-only one (false).
8490 * @param pKeyStore The key store.
8491 * @param ppHdd Where to return the pointer to the VDISK on
8492 * success.
8493 * @param pMediumLockList The lock list to populate and lock. Caller
8494 * is responsible for calling the destructor or
8495 * MediumLockList::Clear() after destroying
8496 * @a *ppHdd
8497 * @param pCryptoSettings The crypto settings to use for setting up
8498 * decryption/encryption of the VDISK. This object
8499 * must be alive until the VDISK is destroyed!
8500 */
8501HRESULT Medium::i_openForIO(bool fWritable, SecretKeyStore *pKeyStore, PVDISK *ppHdd, MediumLockList *pMediumLockList,
8502 MediumCryptoFilterSettings *pCryptoSettings)
8503{
8504 /*
8505 * Create the media lock list and lock the media.
8506 */
8507 HRESULT hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
8508 fWritable ? this : NULL /* pToLockWrite */,
8509 false /* fMediumLockWriteAll */,
8510 NULL,
8511 *pMediumLockList);
8512 if (SUCCEEDED(hrc))
8513 hrc = pMediumLockList->Lock();
8514 if (FAILED(hrc))
8515 return hrc;
8516
8517 /*
8518 * Get the base medium before write locking this medium.
8519 */
8520 ComObjPtr<Medium> pBase = i_getBase();
8521 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8522
8523 /*
8524 * Create the VDISK instance.
8525 */
8526 PVDISK pHdd;
8527 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pHdd);
8528 AssertRCReturn(vrc, E_FAIL);
8529
8530 /*
8531 * Goto avoidance using try/catch/throw(HRESULT).
8532 */
8533 try
8534 {
8535 settings::StringsMap::iterator itKeyStore = pBase->m->mapProperties.find("CRYPT/KeyStore");
8536 if (itKeyStore != pBase->m->mapProperties.end())
8537 {
8538#ifdef VBOX_WITH_EXTPACK
8539 settings::StringsMap::iterator itKeyId = pBase->m->mapProperties.find("CRYPT/KeyId");
8540
8541 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
8542 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
8543 {
8544 /* Load the plugin */
8545 Utf8Str strPlugin;
8546 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
8547 if (SUCCEEDED(hrc))
8548 {
8549 vrc = VDPluginLoadFromFilename(strPlugin.c_str());
8550 if (RT_FAILURE(vrc))
8551 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
8552 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
8553 i_vdError(vrc).c_str());
8554 }
8555 else
8556 throw setError(VBOX_E_NOT_SUPPORTED,
8557 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
8558 ORACLE_PUEL_EXTPACK_NAME);
8559 }
8560 else
8561 throw setError(VBOX_E_NOT_SUPPORTED,
8562 tr("Encryption is not supported because the extension pack '%s' is missing"),
8563 ORACLE_PUEL_EXTPACK_NAME);
8564
8565 if (itKeyId == pBase->m->mapProperties.end())
8566 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8567 tr("Image '%s' is configured for encryption but doesn't has a key identifier set"),
8568 pBase->m->strLocationFull.c_str());
8569
8570 /* Find the proper secret key in the key store. */
8571 if (!pKeyStore)
8572 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8573 tr("Image '%s' is configured for encryption but there is no key store to retrieve the password from"),
8574 pBase->m->strLocationFull.c_str());
8575
8576 SecretKey *pKey = NULL;
8577 vrc = pKeyStore->retainSecretKey(itKeyId->second, &pKey);
8578 if (RT_FAILURE(vrc))
8579 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
8580 tr("Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)"),
8581 itKeyId->second.c_str(), vrc);
8582
8583 i_taskEncryptSettingsSetup(pCryptoSettings, NULL, itKeyStore->second.c_str(), (const char *)pKey->getKeyBuffer(),
8584 false /* fCreateKeyStore */);
8585 vrc = VDFilterAdd(pHdd, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pCryptoSettings->vdFilterIfaces);
8586 pKeyStore->releaseSecretKey(itKeyId->second);
8587 if (vrc == VERR_VD_PASSWORD_INCORRECT)
8588 throw setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc, tr("The password to decrypt the image is incorrect"));
8589 if (RT_FAILURE(vrc))
8590 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc, tr("Failed to load the decryption filter: %s"),
8591 i_vdError(vrc).c_str());
8592#else
8593 RT_NOREF(pKeyStore, pCryptoSettings);
8594 throw setError(VBOX_E_NOT_SUPPORTED,
8595 tr("Encryption is not supported because extension pack support is not built in"));
8596#endif /* VBOX_WITH_EXTPACK */
8597 }
8598
8599 /*
8600 * Open all media in the source chain.
8601 */
8602 MediumLockList::Base::const_iterator sourceListBegin = pMediumLockList->GetBegin();
8603 MediumLockList::Base::const_iterator sourceListEnd = pMediumLockList->GetEnd();
8604 MediumLockList::Base::const_iterator mediumListLast = sourceListEnd;
8605 --mediumListLast;
8606
8607 for (MediumLockList::Base::const_iterator it = sourceListBegin; it != sourceListEnd; ++it)
8608 {
8609 const MediumLock &mediumLock = *it;
8610 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8611 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8612
8613 /* sanity check */
8614 Assert(pMedium->m->state == (fWritable && it == mediumListLast ? MediumState_LockedWrite : MediumState_LockedRead));
8615
8616 /* Open all media in read-only mode. */
8617 vrc = VDOpen(pHdd,
8618 pMedium->m->strFormat.c_str(),
8619 pMedium->m->strLocationFull.c_str(),
8620 m->uOpenFlagsDef | (fWritable && it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
8621 pMedium->m->vdImageIfaces);
8622 if (RT_FAILURE(vrc))
8623 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8624 tr("Could not open the medium storage unit '%s'%s"),
8625 pMedium->m->strLocationFull.c_str(),
8626 i_vdError(vrc).c_str());
8627 }
8628
8629 Assert(m->state == (fWritable ? MediumState_LockedWrite : MediumState_LockedRead));
8630
8631 /*
8632 * Done!
8633 */
8634 *ppHdd = pHdd;
8635 return S_OK;
8636 }
8637 catch (HRESULT hrc2)
8638 {
8639 hrc = hrc2;
8640 }
8641
8642 VDDestroy(pHdd);
8643 return hrc;
8644
8645}
8646
8647/**
8648 * Implementation code for the "create base" task.
8649 *
8650 * This only gets started from Medium::CreateBaseStorage() and always runs
8651 * asynchronously. As a result, we always save the VirtualBox.xml file when
8652 * we're done here.
8653 *
8654 * @param task
8655 * @return
8656 */
8657HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
8658{
8659 /** @todo r=klaus The code below needs to be double checked with regard
8660 * to lock order violations, it probably causes lock order issues related
8661 * to the AutoCaller usage. */
8662 HRESULT rc = S_OK;
8663
8664 /* these parameters we need after creation */
8665 uint64_t size = 0, logicalSize = 0;
8666 MediumVariant_T variant = MediumVariant_Standard;
8667 bool fGenerateUuid = false;
8668
8669 try
8670 {
8671 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8672
8673 /* The object may request a specific UUID (through a special form of
8674 * the moveTo() argument). Otherwise we have to generate it */
8675 Guid id = m->id;
8676
8677 fGenerateUuid = id.isZero();
8678 if (fGenerateUuid)
8679 {
8680 id.create();
8681 /* VirtualBox::i_registerMedium() will need UUID */
8682 unconst(m->id) = id;
8683 }
8684
8685 Utf8Str format(m->strFormat);
8686 Utf8Str location(m->strLocationFull);
8687 uint64_t capabilities = m->formatObj->i_getCapabilities();
8688 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
8689 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
8690 Assert(m->state == MediumState_Creating);
8691
8692 PVDISK hdd;
8693 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8694 ComAssertRCThrow(vrc, E_FAIL);
8695
8696 /* unlock before the potentially lengthy operation */
8697 thisLock.release();
8698
8699 try
8700 {
8701 /* ensure the directory exists */
8702 if (capabilities & MediumFormatCapabilities_File)
8703 {
8704 rc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8705 if (FAILED(rc))
8706 throw rc;
8707 }
8708
8709 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
8710
8711 vrc = VDCreateBase(hdd,
8712 format.c_str(),
8713 location.c_str(),
8714 task.mSize,
8715 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
8716 NULL,
8717 &geo,
8718 &geo,
8719 id.raw(),
8720 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8721 m->vdImageIfaces,
8722 task.mVDOperationIfaces);
8723 if (RT_FAILURE(vrc))
8724 {
8725 if (vrc == VERR_VD_INVALID_TYPE)
8726 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8727 tr("Parameters for creating the medium storage unit '%s' are invalid%s"),
8728 location.c_str(), i_vdError(vrc).c_str());
8729 else
8730 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8731 tr("Could not create the medium storage unit '%s'%s"),
8732 location.c_str(), i_vdError(vrc).c_str());
8733 }
8734
8735 if (task.mVariant & MediumVariant_Formatted)
8736 {
8737 RTVFSFILE hVfsFile;
8738 vrc = VDCreateVfsFileFromDisk(hdd, 0 /*fFlags*/, &hVfsFile);
8739 if (RT_FAILURE(vrc))
8740 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Opening medium storage unit '%s' failed%s"),
8741 location.c_str(), i_vdError(vrc).c_str());
8742 RTERRINFOSTATIC ErrInfo;
8743 vrc = RTFsFatVolFormat(hVfsFile, 0 /* offVol */, 0 /* cbVol */, RTFSFATVOL_FMT_F_FULL,
8744 0 /* cbSector */, 0 /* cbSectorPerCluster */, RTFSFATTYPE_INVALID,
8745 0 /* cHeads */, 0 /* cSectorsPerTrack*/, 0 /* bMedia */,
8746 0 /* cRootDirEntries */, 0 /* cHiddenSectors */,
8747 RTErrInfoInitStatic(&ErrInfo));
8748 RTVfsFileRelease(hVfsFile);
8749 if (RT_FAILURE(vrc) && RTErrInfoIsSet(&ErrInfo.Core))
8750 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed: %s"),
8751 location.c_str(), ErrInfo.Core.pszMsg);
8752 if (RT_FAILURE(vrc))
8753 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed%s"),
8754 location.c_str(), i_vdError(vrc).c_str());
8755 }
8756
8757 size = VDGetFileSize(hdd, 0);
8758 logicalSize = VDGetSize(hdd, 0);
8759 unsigned uImageFlags;
8760 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8761 if (RT_SUCCESS(vrc))
8762 variant = (MediumVariant_T)uImageFlags;
8763 }
8764 catch (HRESULT aRC) { rc = aRC; }
8765
8766 VDDestroy(hdd);
8767 }
8768 catch (HRESULT aRC) { rc = aRC; }
8769
8770 if (SUCCEEDED(rc))
8771 {
8772 /* register with mVirtualBox as the last step and move to
8773 * Created state only on success (leaving an orphan file is
8774 * better than breaking media registry consistency) */
8775 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8776 ComObjPtr<Medium> pMedium;
8777 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
8778 Assert(pMedium == NULL || this == pMedium);
8779 }
8780
8781 // re-acquire the lock before changing state
8782 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8783
8784 if (SUCCEEDED(rc))
8785 {
8786 m->state = MediumState_Created;
8787
8788 m->size = size;
8789 m->logicalSize = logicalSize;
8790 m->variant = variant;
8791
8792 thisLock.release();
8793 i_markRegistriesModified();
8794 if (task.isAsync())
8795 {
8796 // in asynchronous mode, save settings now
8797 m->pVirtualBox->i_saveModifiedRegistries();
8798 }
8799 }
8800 else
8801 {
8802 /* back to NotCreated on failure */
8803 m->state = MediumState_NotCreated;
8804
8805 /* reset UUID to prevent it from being reused next time */
8806 if (fGenerateUuid)
8807 unconst(m->id).clear();
8808 }
8809
8810 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
8811 {
8812 m->pVirtualBox->i_onMediumConfigChanged(this);
8813 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
8814 }
8815
8816 return rc;
8817}
8818
8819/**
8820 * Implementation code for the "create diff" task.
8821 *
8822 * This task always gets started from Medium::createDiffStorage() and can run
8823 * synchronously or asynchronously depending on the "wait" parameter passed to
8824 * that function. If we run synchronously, the caller expects the medium
8825 * registry modification to be set before returning; otherwise (in asynchronous
8826 * mode), we save the settings ourselves.
8827 *
8828 * @param task
8829 * @return
8830 */
8831HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
8832{
8833 /** @todo r=klaus The code below needs to be double checked with regard
8834 * to lock order violations, it probably causes lock order issues related
8835 * to the AutoCaller usage. */
8836 HRESULT rcTmp = S_OK;
8837
8838 const ComObjPtr<Medium> &pTarget = task.mTarget;
8839
8840 uint64_t size = 0, logicalSize = 0;
8841 MediumVariant_T variant = MediumVariant_Standard;
8842 bool fGenerateUuid = false;
8843
8844 try
8845 {
8846 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8847 {
8848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8849 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8850 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"),
8851 m->strLocationFull.c_str());
8852 }
8853
8854 /* Lock both in {parent,child} order. */
8855 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8856
8857 /* The object may request a specific UUID (through a special form of
8858 * the moveTo() argument). Otherwise we have to generate it */
8859 Guid targetId = pTarget->m->id;
8860
8861 fGenerateUuid = targetId.isZero();
8862 if (fGenerateUuid)
8863 {
8864 targetId.create();
8865 /* VirtualBox::i_registerMedium() will need UUID */
8866 unconst(pTarget->m->id) = targetId;
8867 }
8868
8869 Guid id = m->id;
8870
8871 Utf8Str targetFormat(pTarget->m->strFormat);
8872 Utf8Str targetLocation(pTarget->m->strLocationFull);
8873 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
8874 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
8875
8876 Assert(pTarget->m->state == MediumState_Creating);
8877 Assert(m->state == MediumState_LockedRead);
8878
8879 PVDISK hdd;
8880 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8881 ComAssertRCThrow(vrc, E_FAIL);
8882
8883 /* the two media are now protected by their non-default states;
8884 * unlock the media before the potentially lengthy operation */
8885 mediaLock.release();
8886
8887 try
8888 {
8889 /* Open all media in the target chain but the last. */
8890 MediumLockList::Base::const_iterator targetListBegin =
8891 task.mpMediumLockList->GetBegin();
8892 MediumLockList::Base::const_iterator targetListEnd =
8893 task.mpMediumLockList->GetEnd();
8894 for (MediumLockList::Base::const_iterator it = targetListBegin;
8895 it != targetListEnd;
8896 ++it)
8897 {
8898 const MediumLock &mediumLock = *it;
8899 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8900
8901 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8902
8903 /* Skip over the target diff medium */
8904 if (pMedium->m->state == MediumState_Creating)
8905 continue;
8906
8907 /* sanity check */
8908 Assert(pMedium->m->state == MediumState_LockedRead);
8909
8910 /* Open all media in appropriate mode. */
8911 vrc = VDOpen(hdd,
8912 pMedium->m->strFormat.c_str(),
8913 pMedium->m->strLocationFull.c_str(),
8914 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8915 pMedium->m->vdImageIfaces);
8916 if (RT_FAILURE(vrc))
8917 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8918 tr("Could not open the medium storage unit '%s'%s"),
8919 pMedium->m->strLocationFull.c_str(),
8920 i_vdError(vrc).c_str());
8921 }
8922
8923 /* ensure the target directory exists */
8924 if (capabilities & MediumFormatCapabilities_File)
8925 {
8926 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8927 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8928 if (FAILED(rc))
8929 throw rc;
8930 }
8931
8932 vrc = VDCreateDiff(hdd,
8933 targetFormat.c_str(),
8934 targetLocation.c_str(),
8935 (task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX))
8936 | VD_IMAGE_FLAGS_DIFF,
8937 NULL,
8938 targetId.raw(),
8939 id.raw(),
8940 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8941 pTarget->m->vdImageIfaces,
8942 task.mVDOperationIfaces);
8943 if (RT_FAILURE(vrc))
8944 {
8945 if (vrc == VERR_VD_INVALID_TYPE)
8946 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8947 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
8948 targetLocation.c_str(), i_vdError(vrc).c_str());
8949 else
8950 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8951 tr("Could not create the differencing medium storage unit '%s'%s"),
8952 targetLocation.c_str(), i_vdError(vrc).c_str());
8953 }
8954
8955 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
8956 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
8957 unsigned uImageFlags;
8958 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8959 if (RT_SUCCESS(vrc))
8960 variant = (MediumVariant_T)uImageFlags;
8961 }
8962 catch (HRESULT aRC) { rcTmp = aRC; }
8963
8964 VDDestroy(hdd);
8965 }
8966 catch (HRESULT aRC) { rcTmp = aRC; }
8967
8968 MultiResult mrc(rcTmp);
8969
8970 if (SUCCEEDED(mrc))
8971 {
8972 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8973
8974 Assert(pTarget->m->pParent.isNull());
8975
8976 /* associate child with the parent, maximum depth was checked above */
8977 pTarget->i_setParent(this);
8978
8979 /* diffs for immutable media are auto-reset by default */
8980 bool fAutoReset;
8981 {
8982 ComObjPtr<Medium> pBase = i_getBase();
8983 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
8984 fAutoReset = (pBase->m->type == MediumType_Immutable);
8985 }
8986 {
8987 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
8988 pTarget->m->autoReset = fAutoReset;
8989 }
8990
8991 /* register with mVirtualBox as the last step and move to
8992 * Created state only on success (leaving an orphan file is
8993 * better than breaking media registry consistency) */
8994 ComObjPtr<Medium> pMedium;
8995 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
8996 Assert(pTarget == pMedium);
8997
8998 if (FAILED(mrc))
8999 /* break the parent association on failure to register */
9000 i_deparent();
9001 }
9002
9003 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
9004
9005 if (SUCCEEDED(mrc))
9006 {
9007 pTarget->m->state = MediumState_Created;
9008
9009 pTarget->m->size = size;
9010 pTarget->m->logicalSize = logicalSize;
9011 pTarget->m->variant = variant;
9012 }
9013 else
9014 {
9015 /* back to NotCreated on failure */
9016 pTarget->m->state = MediumState_NotCreated;
9017
9018 pTarget->m->autoReset = false;
9019
9020 /* reset UUID to prevent it from being reused next time */
9021 if (fGenerateUuid)
9022 unconst(pTarget->m->id).clear();
9023 }
9024
9025 // deregister the task registered in createDiffStorage()
9026 Assert(m->numCreateDiffTasks != 0);
9027 --m->numCreateDiffTasks;
9028
9029 mediaLock.release();
9030 i_markRegistriesModified();
9031 if (task.isAsync())
9032 {
9033 // in asynchronous mode, save settings now
9034 m->pVirtualBox->i_saveModifiedRegistries();
9035 }
9036
9037 /* Note that in sync mode, it's the caller's responsibility to
9038 * unlock the medium. */
9039
9040 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
9041 {
9042 m->pVirtualBox->i_onMediumConfigChanged(this);
9043 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
9044 }
9045
9046 return mrc;
9047}
9048
9049/**
9050 * Implementation code for the "merge" task.
9051 *
9052 * This task always gets started from Medium::mergeTo() and can run
9053 * synchronously or asynchronously depending on the "wait" parameter passed to
9054 * that function. If we run synchronously, the caller expects the medium
9055 * registry modification to be set before returning; otherwise (in asynchronous
9056 * mode), we save the settings ourselves.
9057 *
9058 * @param task
9059 * @return
9060 */
9061HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
9062{
9063 /** @todo r=klaus The code below needs to be double checked with regard
9064 * to lock order violations, it probably causes lock order issues related
9065 * to the AutoCaller usage. */
9066 HRESULT rcTmp = S_OK;
9067
9068 const ComObjPtr<Medium> &pTarget = task.mTarget;
9069
9070 try
9071 {
9072 if (!task.mParentForTarget.isNull())
9073 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9074 {
9075 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
9076 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9077 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9078 task.mParentForTarget->m->strLocationFull.c_str());
9079 }
9080
9081 // Resize target to source size, if possible. Otherwise throw an error.
9082 // It's offline resizing. Online resizing will be called in the
9083 // SessionMachine::onlineMergeMedium.
9084
9085 uint64_t sourceSize = 0;
9086 Utf8Str sourceName;
9087 {
9088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9089 sourceSize = i_getLogicalSize();
9090 sourceName = i_getName();
9091 }
9092 uint64_t targetSize = 0;
9093 Utf8Str targetName;
9094 {
9095 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
9096 targetSize = pTarget->i_getLogicalSize();
9097 targetName = pTarget->i_getName();
9098 }
9099
9100 //reducing vm disks are not implemented yet
9101 if (sourceSize > targetSize)
9102 {
9103 if (i_isMediumFormatFile())
9104 {
9105 // Have to make own lock list, because "resize" method resizes only last image
9106 // in the lock chain. The lock chain already in the task.mpMediumLockList, so
9107 // just make new lock list based on it. In fact the own lock list neither makes
9108 // double locking of mediums nor unlocks them during delete, because medium
9109 // already locked by task.mpMediumLockList and own list is used just to specify
9110 // what "resize" method should resize.
9111
9112 MediumLockList* pMediumLockListForResize = new MediumLockList();
9113
9114 for (MediumLockList::Base::iterator it = task.mpMediumLockList->GetBegin();
9115 it != task.mpMediumLockList->GetEnd();
9116 ++it)
9117 {
9118 ComObjPtr<Medium> pMedium = it->GetMedium();
9119 pMediumLockListForResize->Append(pMedium, pMedium->m->state == MediumState_LockedWrite);
9120 if (pMedium == pTarget)
9121 break;
9122 }
9123
9124 // just to switch internal state of the lock list to avoid errors during list deletion,
9125 // because all meduims in the list already locked by task.mpMediumLockList
9126 HRESULT rc = pMediumLockListForResize->Lock(true /* fSkipOverLockedMedia */);
9127 if (FAILED(rc))
9128 {
9129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9130 rc = setError(rc,
9131 tr("Failed to lock the medium '%s' to resize before merge"),
9132 targetName.c_str());
9133 delete pMediumLockListForResize;
9134 throw rc;
9135 }
9136
9137 ComObjPtr<Progress> pProgress(task.GetProgressObject());
9138 rc = pTarget->i_resize(sourceSize, pMediumLockListForResize, &pProgress, true, false);
9139 if (FAILED(rc))
9140 {
9141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9142 rc = setError(rc,
9143 tr("Failed to set size of '%s' to size of '%s'"),
9144 targetName.c_str(), sourceName.c_str());
9145 delete pMediumLockListForResize;
9146 throw rc;
9147 }
9148 delete pMediumLockListForResize;
9149 }
9150 else
9151 {
9152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9153 HRESULT rc = setError(VBOX_E_NOT_SUPPORTED,
9154 tr("Sizes of '%s' and '%s' are different and medium format does not support resing"),
9155 sourceName.c_str(), targetName.c_str());
9156 throw rc;
9157 }
9158 }
9159
9160 task.GetProgressObject()->SetNextOperation(BstrFmt(tr("Merging medium '%s' to '%s'"),
9161 i_getName().c_str(),
9162 targetName.c_str()).raw(),
9163 1);
9164
9165 PVDISK hdd;
9166 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9167 ComAssertRCThrow(vrc, E_FAIL);
9168
9169 try
9170 {
9171 // Similar code appears in SessionMachine::onlineMergeMedium, so
9172 // if you make any changes below check whether they are applicable
9173 // in that context as well.
9174
9175 unsigned uTargetIdx = VD_LAST_IMAGE;
9176 unsigned uSourceIdx = VD_LAST_IMAGE;
9177 /* Open all media in the chain. */
9178 MediumLockList::Base::iterator lockListBegin =
9179 task.mpMediumLockList->GetBegin();
9180 MediumLockList::Base::iterator lockListEnd =
9181 task.mpMediumLockList->GetEnd();
9182 unsigned i = 0;
9183 for (MediumLockList::Base::iterator it = lockListBegin;
9184 it != lockListEnd;
9185 ++it)
9186 {
9187 MediumLock &mediumLock = *it;
9188 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9189
9190 if (pMedium == this)
9191 uSourceIdx = i;
9192 else if (pMedium == pTarget)
9193 uTargetIdx = i;
9194
9195 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9196
9197 /*
9198 * complex sanity (sane complexity)
9199 *
9200 * The current medium must be in the Deleting (medium is merged)
9201 * or LockedRead (parent medium) state if it is not the target.
9202 * If it is the target it must be in the LockedWrite state.
9203 */
9204 Assert( ( pMedium != pTarget
9205 && ( pMedium->m->state == MediumState_Deleting
9206 || pMedium->m->state == MediumState_LockedRead))
9207 || ( pMedium == pTarget
9208 && pMedium->m->state == MediumState_LockedWrite));
9209 /*
9210 * Medium must be the target, in the LockedRead state
9211 * or Deleting state where it is not allowed to be attached
9212 * to a virtual machine.
9213 */
9214 Assert( pMedium == pTarget
9215 || pMedium->m->state == MediumState_LockedRead
9216 || ( pMedium->m->backRefs.size() == 0
9217 && pMedium->m->state == MediumState_Deleting));
9218 /* The source medium must be in Deleting state. */
9219 Assert( pMedium != this
9220 || pMedium->m->state == MediumState_Deleting);
9221
9222 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9223
9224 if ( pMedium->m->state == MediumState_LockedRead
9225 || pMedium->m->state == MediumState_Deleting)
9226 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9227 if (pMedium->m->type == MediumType_Shareable)
9228 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9229
9230 /* Open the medium */
9231 vrc = VDOpen(hdd,
9232 pMedium->m->strFormat.c_str(),
9233 pMedium->m->strLocationFull.c_str(),
9234 uOpenFlags | m->uOpenFlagsDef,
9235 pMedium->m->vdImageIfaces);
9236 if (RT_FAILURE(vrc))
9237 throw vrc;
9238
9239 i++;
9240 }
9241
9242 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
9243 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
9244
9245 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
9246 task.mVDOperationIfaces);
9247 if (RT_FAILURE(vrc))
9248 throw vrc;
9249
9250 /* update parent UUIDs */
9251 if (!task.mfMergeForward)
9252 {
9253 /* we need to update UUIDs of all source's children
9254 * which cannot be part of the container at once so
9255 * add each one in there individually */
9256 if (task.mpChildrenToReparent)
9257 {
9258 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9259 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9260 for (MediumLockList::Base::iterator it = childrenBegin;
9261 it != childrenEnd;
9262 ++it)
9263 {
9264 Medium *pMedium = it->GetMedium();
9265 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
9266 vrc = VDOpen(hdd,
9267 pMedium->m->strFormat.c_str(),
9268 pMedium->m->strLocationFull.c_str(),
9269 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9270 pMedium->m->vdImageIfaces);
9271 if (RT_FAILURE(vrc))
9272 throw vrc;
9273
9274 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
9275 pTarget->m->id.raw());
9276 if (RT_FAILURE(vrc))
9277 throw vrc;
9278
9279 vrc = VDClose(hdd, false /* fDelete */);
9280 if (RT_FAILURE(vrc))
9281 throw vrc;
9282 }
9283 }
9284 }
9285 }
9286 catch (HRESULT aRC) { rcTmp = aRC; }
9287 catch (int aVRC)
9288 {
9289 rcTmp = setErrorBoth(VBOX_E_FILE_ERROR, aVRC,
9290 tr("Could not merge the medium '%s' to '%s'%s"),
9291 m->strLocationFull.c_str(),
9292 pTarget->m->strLocationFull.c_str(),
9293 i_vdError(aVRC).c_str());
9294 }
9295
9296 VDDestroy(hdd);
9297 }
9298 catch (HRESULT aRC) { rcTmp = aRC; }
9299
9300 ErrorInfoKeeper eik;
9301 MultiResult mrc(rcTmp);
9302 HRESULT rc2;
9303
9304 std::set<ComObjPtr<Medium> > pMediumsForNotify;
9305 std::map<Guid, DeviceType_T> uIdsForNotify;
9306
9307 if (SUCCEEDED(mrc))
9308 {
9309 /* all media but the target were successfully deleted by
9310 * VDMerge; reparent the last one and uninitialize deleted media. */
9311
9312 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9313
9314 if (task.mfMergeForward)
9315 {
9316 /* first, unregister the target since it may become a base
9317 * medium which needs re-registration */
9318 rc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
9319 AssertComRC(rc2);
9320
9321 /* then, reparent it and disconnect the deleted branch at both ends
9322 * (chain->parent() is source's parent). Depth check above. */
9323 pTarget->i_deparent();
9324 pTarget->i_setParent(task.mParentForTarget);
9325 if (task.mParentForTarget)
9326 {
9327 i_deparent();
9328 if (task.NotifyAboutChanges())
9329 pMediumsForNotify.insert(task.mParentForTarget);
9330 }
9331
9332 /* then, register again */
9333 ComObjPtr<Medium> pMedium;
9334 rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9335 treeLock);
9336 AssertComRC(rc2);
9337 }
9338 else
9339 {
9340 Assert(pTarget->i_getChildren().size() == 1);
9341 Medium *targetChild = pTarget->i_getChildren().front();
9342
9343 /* disconnect the deleted branch at the elder end */
9344 targetChild->i_deparent();
9345
9346 /* reparent source's children and disconnect the deleted
9347 * branch at the younger end */
9348 if (task.mpChildrenToReparent)
9349 {
9350 /* obey {parent,child} lock order */
9351 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
9352
9353 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9354 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9355 for (MediumLockList::Base::iterator it = childrenBegin;
9356 it != childrenEnd;
9357 ++it)
9358 {
9359 Medium *pMedium = it->GetMedium();
9360 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
9361
9362 pMedium->i_deparent(); // removes pMedium from source
9363 // no depth check, reduces depth
9364 pMedium->i_setParent(pTarget);
9365
9366 if (task.NotifyAboutChanges())
9367 pMediumsForNotify.insert(pMedium);
9368 }
9369 }
9370 pMediumsForNotify.insert(pTarget);
9371 }
9372
9373 /* unregister and uninitialize all media removed by the merge */
9374 MediumLockList::Base::iterator lockListBegin =
9375 task.mpMediumLockList->GetBegin();
9376 MediumLockList::Base::iterator lockListEnd =
9377 task.mpMediumLockList->GetEnd();
9378 for (MediumLockList::Base::iterator it = lockListBegin;
9379 it != lockListEnd;
9380 )
9381 {
9382 MediumLock &mediumLock = *it;
9383 /* Create a real copy of the medium pointer, as the medium
9384 * lock deletion below would invalidate the referenced object. */
9385 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
9386
9387 /* The target and all media not merged (readonly) are skipped */
9388 if ( pMedium == pTarget
9389 || pMedium->m->state == MediumState_LockedRead)
9390 {
9391 ++it;
9392 continue;
9393 }
9394
9395 uIdsForNotify[pMedium->i_getId()] = pMedium->i_getDeviceType();
9396 rc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
9397 AssertComRC(rc2);
9398
9399 /* now, uninitialize the deleted medium (note that
9400 * due to the Deleting state, uninit() will not touch
9401 * the parent-child relationship so we need to
9402 * uninitialize each disk individually) */
9403
9404 /* note that the operation initiator medium (which is
9405 * normally also the source medium) is a special case
9406 * -- there is one more caller added by Task to it which
9407 * we must release. Also, if we are in sync mode, the
9408 * caller may still hold an AutoCaller instance for it
9409 * and therefore we cannot uninit() it (it's therefore
9410 * the caller's responsibility) */
9411 if (pMedium == this)
9412 {
9413 Assert(i_getChildren().size() == 0);
9414 Assert(m->backRefs.size() == 0);
9415 task.mMediumCaller.release();
9416 }
9417
9418 /* Delete the medium lock list entry, which also releases the
9419 * caller added by MergeChain before uninit() and updates the
9420 * iterator to point to the right place. */
9421 rc2 = task.mpMediumLockList->RemoveByIterator(it);
9422 AssertComRC(rc2);
9423
9424 if (task.isAsync() || pMedium != this)
9425 {
9426 treeLock.release();
9427 pMedium->uninit();
9428 treeLock.acquire();
9429 }
9430 }
9431 }
9432
9433 i_markRegistriesModified();
9434 if (task.isAsync())
9435 {
9436 // in asynchronous mode, save settings now
9437 eik.restore();
9438 m->pVirtualBox->i_saveModifiedRegistries();
9439 eik.fetch();
9440 }
9441
9442 if (FAILED(mrc))
9443 {
9444 /* Here we come if either VDMerge() failed (in which case we
9445 * assume that it tried to do everything to make a further
9446 * retry possible -- e.g. not deleted intermediate media
9447 * and so on) or VirtualBox::saveRegistries() failed (where we
9448 * should have the original tree but with intermediate storage
9449 * units deleted by VDMerge()). We have to only restore states
9450 * (through the MergeChain dtor) unless we are run synchronously
9451 * in which case it's the responsibility of the caller as stated
9452 * in the mergeTo() docs. The latter also implies that we
9453 * don't own the merge chain, so release it in this case. */
9454 if (task.isAsync())
9455 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
9456 }
9457 else if (task.NotifyAboutChanges())
9458 {
9459 for (std::set<ComObjPtr<Medium> >::const_iterator it = pMediumsForNotify.begin();
9460 it != pMediumsForNotify.end();
9461 ++it)
9462 {
9463 if (it->isNotNull())
9464 m->pVirtualBox->i_onMediumConfigChanged(*it);
9465 }
9466 for (std::map<Guid, DeviceType_T>::const_iterator it = uIdsForNotify.begin();
9467 it != uIdsForNotify.end();
9468 ++it)
9469 {
9470 m->pVirtualBox->i_onMediumRegistered(it->first, it->second, FALSE);
9471 }
9472 }
9473
9474 return mrc;
9475}
9476
9477/**
9478 * Implementation code for the "clone" task.
9479 *
9480 * This only gets started from Medium::CloneTo() and always runs asynchronously.
9481 * As a result, we always save the VirtualBox.xml file when we're done here.
9482 *
9483 * @param task
9484 * @return
9485 */
9486HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
9487{
9488 /** @todo r=klaus The code below needs to be double checked with regard
9489 * to lock order violations, it probably causes lock order issues related
9490 * to the AutoCaller usage. */
9491 HRESULT rcTmp = S_OK;
9492
9493 const ComObjPtr<Medium> &pTarget = task.mTarget;
9494 const ComObjPtr<Medium> &pParent = task.mParent;
9495
9496 bool fCreatingTarget = false;
9497
9498 uint64_t size = 0, logicalSize = 0;
9499 MediumVariant_T variant = MediumVariant_Standard;
9500 bool fGenerateUuid = false;
9501
9502 try
9503 {
9504 if (!pParent.isNull())
9505 {
9506
9507 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9508 {
9509 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
9510 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9511 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9512 pParent->m->strLocationFull.c_str());
9513 }
9514 }
9515
9516 /* Lock all in {parent,child} order. The lock is also used as a
9517 * signal from the task initiator (which releases it only after
9518 * RTThreadCreate()) that we can start the job. */
9519 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
9520
9521 fCreatingTarget = pTarget->m->state == MediumState_Creating;
9522
9523 /* The object may request a specific UUID (through a special form of
9524 * the moveTo() argument). Otherwise we have to generate it */
9525 Guid targetId = pTarget->m->id;
9526
9527 fGenerateUuid = targetId.isZero();
9528 if (fGenerateUuid)
9529 {
9530 targetId.create();
9531 /* VirtualBox::registerMedium() will need UUID */
9532 unconst(pTarget->m->id) = targetId;
9533 }
9534
9535 PVDISK hdd;
9536 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9537 ComAssertRCThrow(vrc, E_FAIL);
9538
9539 try
9540 {
9541 /* Open all media in the source chain. */
9542 MediumLockList::Base::const_iterator sourceListBegin =
9543 task.mpSourceMediumLockList->GetBegin();
9544 MediumLockList::Base::const_iterator sourceListEnd =
9545 task.mpSourceMediumLockList->GetEnd();
9546 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9547 it != sourceListEnd;
9548 ++it)
9549 {
9550 const MediumLock &mediumLock = *it;
9551 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9552 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9553
9554 /* sanity check */
9555 Assert(pMedium->m->state == MediumState_LockedRead);
9556
9557 /** Open all media in read-only mode. */
9558 vrc = VDOpen(hdd,
9559 pMedium->m->strFormat.c_str(),
9560 pMedium->m->strLocationFull.c_str(),
9561 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9562 pMedium->m->vdImageIfaces);
9563 if (RT_FAILURE(vrc))
9564 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9565 tr("Could not open the medium storage unit '%s'%s"),
9566 pMedium->m->strLocationFull.c_str(),
9567 i_vdError(vrc).c_str());
9568 }
9569
9570 Utf8Str targetFormat(pTarget->m->strFormat);
9571 Utf8Str targetLocation(pTarget->m->strLocationFull);
9572 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
9573
9574 Assert( pTarget->m->state == MediumState_Creating
9575 || pTarget->m->state == MediumState_LockedWrite);
9576 Assert(m->state == MediumState_LockedRead);
9577 Assert( pParent.isNull()
9578 || pParent->m->state == MediumState_LockedRead);
9579
9580 /* unlock before the potentially lengthy operation */
9581 thisLock.release();
9582
9583 /* ensure the target directory exists */
9584 if (capabilities & MediumFormatCapabilities_File)
9585 {
9586 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9587 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9588 if (FAILED(rc))
9589 throw rc;
9590 }
9591
9592 PVDISK targetHdd;
9593 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9594 ComAssertRCThrow(vrc, E_FAIL);
9595
9596 try
9597 {
9598 /* Open all media in the target chain. */
9599 MediumLockList::Base::const_iterator targetListBegin =
9600 task.mpTargetMediumLockList->GetBegin();
9601 MediumLockList::Base::const_iterator targetListEnd =
9602 task.mpTargetMediumLockList->GetEnd();
9603 for (MediumLockList::Base::const_iterator it = targetListBegin;
9604 it != targetListEnd;
9605 ++it)
9606 {
9607 const MediumLock &mediumLock = *it;
9608 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9609
9610 /* If the target medium is not created yet there's no
9611 * reason to open it. */
9612 if (pMedium == pTarget && fCreatingTarget)
9613 continue;
9614
9615 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9616
9617 /* sanity check */
9618 Assert( pMedium->m->state == MediumState_LockedRead
9619 || pMedium->m->state == MediumState_LockedWrite);
9620
9621 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9622 if (pMedium->m->state != MediumState_LockedWrite)
9623 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9624 if (pMedium->m->type == MediumType_Shareable)
9625 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9626
9627 /* Open all media in appropriate mode. */
9628 vrc = VDOpen(targetHdd,
9629 pMedium->m->strFormat.c_str(),
9630 pMedium->m->strLocationFull.c_str(),
9631 uOpenFlags | m->uOpenFlagsDef,
9632 pMedium->m->vdImageIfaces);
9633 if (RT_FAILURE(vrc))
9634 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9635 tr("Could not open the medium storage unit '%s'%s"),
9636 pMedium->m->strLocationFull.c_str(),
9637 i_vdError(vrc).c_str());
9638 }
9639
9640 /* target isn't locked, but no changing data is accessed */
9641 if (task.midxSrcImageSame == UINT32_MAX)
9642 {
9643 vrc = VDCopy(hdd,
9644 VD_LAST_IMAGE,
9645 targetHdd,
9646 targetFormat.c_str(),
9647 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9648 false /* fMoveByRename */,
9649 0 /* cbSize */,
9650 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
9651 targetId.raw(),
9652 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9653 NULL /* pVDIfsOperation */,
9654 pTarget->m->vdImageIfaces,
9655 task.mVDOperationIfaces);
9656 }
9657 else
9658 {
9659 vrc = VDCopyEx(hdd,
9660 VD_LAST_IMAGE,
9661 targetHdd,
9662 targetFormat.c_str(),
9663 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9664 false /* fMoveByRename */,
9665 0 /* cbSize */,
9666 task.midxSrcImageSame,
9667 task.midxDstImageSame,
9668 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
9669 targetId.raw(),
9670 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9671 NULL /* pVDIfsOperation */,
9672 pTarget->m->vdImageIfaces,
9673 task.mVDOperationIfaces);
9674 }
9675 if (RT_FAILURE(vrc))
9676 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9677 tr("Could not create the clone medium '%s'%s"),
9678 targetLocation.c_str(), i_vdError(vrc).c_str());
9679
9680 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
9681 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
9682 unsigned uImageFlags;
9683 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
9684 if (RT_SUCCESS(vrc))
9685 variant = (MediumVariant_T)uImageFlags;
9686 }
9687 catch (HRESULT aRC) { rcTmp = aRC; }
9688
9689 VDDestroy(targetHdd);
9690 }
9691 catch (HRESULT aRC) { rcTmp = aRC; }
9692
9693 VDDestroy(hdd);
9694 }
9695 catch (HRESULT aRC) { rcTmp = aRC; }
9696
9697 ErrorInfoKeeper eik;
9698 MultiResult mrc(rcTmp);
9699
9700 /* Only do the parent changes for newly created media. */
9701 if (SUCCEEDED(mrc) && fCreatingTarget)
9702 {
9703 /* we set m->pParent & children() */
9704 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9705
9706 Assert(pTarget->m->pParent.isNull());
9707
9708 if (pParent)
9709 {
9710 /* Associate the clone with the parent and deassociate
9711 * from VirtualBox. Depth check above. */
9712 pTarget->i_setParent(pParent);
9713
9714 /* register with mVirtualBox as the last step and move to
9715 * Created state only on success (leaving an orphan file is
9716 * better than breaking media registry consistency) */
9717 eik.restore();
9718 ComObjPtr<Medium> pMedium;
9719 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9720 treeLock);
9721 Assert( FAILED(mrc)
9722 || pTarget == pMedium);
9723 eik.fetch();
9724
9725 if (FAILED(mrc))
9726 /* break parent association on failure to register */
9727 pTarget->i_deparent(); // removes target from parent
9728 }
9729 else
9730 {
9731 /* just register */
9732 eik.restore();
9733 ComObjPtr<Medium> pMedium;
9734 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9735 treeLock);
9736 Assert( FAILED(mrc)
9737 || pTarget == pMedium);
9738 eik.fetch();
9739 }
9740 }
9741
9742 if (fCreatingTarget)
9743 {
9744 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
9745
9746 if (SUCCEEDED(mrc))
9747 {
9748 pTarget->m->state = MediumState_Created;
9749
9750 pTarget->m->size = size;
9751 pTarget->m->logicalSize = logicalSize;
9752 pTarget->m->variant = variant;
9753 }
9754 else
9755 {
9756 /* back to NotCreated on failure */
9757 pTarget->m->state = MediumState_NotCreated;
9758
9759 /* reset UUID to prevent it from being reused next time */
9760 if (fGenerateUuid)
9761 unconst(pTarget->m->id).clear();
9762 }
9763 }
9764
9765 /* Copy any filter related settings over to the target. */
9766 if (SUCCEEDED(mrc))
9767 {
9768 /* Copy any filter related settings over. */
9769 ComObjPtr<Medium> pBase = i_getBase();
9770 ComObjPtr<Medium> pTargetBase = pTarget->i_getBase();
9771 std::vector<com::Utf8Str> aFilterPropNames;
9772 std::vector<com::Utf8Str> aFilterPropValues;
9773 mrc = pBase->i_getFilterProperties(aFilterPropNames, aFilterPropValues);
9774 if (SUCCEEDED(mrc))
9775 {
9776 /* Go through the properties and add them to the target medium. */
9777 for (unsigned idx = 0; idx < aFilterPropNames.size(); idx++)
9778 {
9779 mrc = pTargetBase->i_setPropertyDirect(aFilterPropNames[idx], aFilterPropValues[idx]);
9780 if (FAILED(mrc)) break;
9781 }
9782
9783 // now, at the end of this task (always asynchronous), save the settings
9784 if (SUCCEEDED(mrc))
9785 {
9786 // save the settings
9787 i_markRegistriesModified();
9788 /* collect multiple errors */
9789 eik.restore();
9790 m->pVirtualBox->i_saveModifiedRegistries();
9791 eik.fetch();
9792
9793 if (task.NotifyAboutChanges())
9794 {
9795 if (!fCreatingTarget)
9796 {
9797 if (!aFilterPropNames.empty())
9798 m->pVirtualBox->i_onMediumConfigChanged(pTargetBase);
9799 if (pParent)
9800 m->pVirtualBox->i_onMediumConfigChanged(pParent);
9801 }
9802 else
9803 {
9804 m->pVirtualBox->i_onMediumRegistered(pTarget->i_getId(), pTarget->i_getDeviceType(), TRUE);
9805 }
9806 }
9807 }
9808 }
9809 }
9810
9811 /* Everything is explicitly unlocked when the task exits,
9812 * as the task destruction also destroys the source chain. */
9813
9814 /* Make sure the source chain is released early. It could happen
9815 * that we get a deadlock in Appliance::Import when Medium::Close
9816 * is called & the source chain is released at the same time. */
9817 task.mpSourceMediumLockList->Clear();
9818
9819 return mrc;
9820}
9821
9822/**
9823 * Implementation code for the "move" task.
9824 *
9825 * This only gets started from Medium::MoveTo() and always
9826 * runs asynchronously.
9827 *
9828 * @param task
9829 * @return
9830 */
9831HRESULT Medium::i_taskMoveHandler(Medium::MoveTask &task)
9832{
9833 LogFlowFuncEnter();
9834 HRESULT rcOut = S_OK;
9835
9836 /* pTarget is equal "this" in our case */
9837 const ComObjPtr<Medium> &pTarget = task.mMedium;
9838
9839 uint64_t size = 0; NOREF(size);
9840 uint64_t logicalSize = 0; NOREF(logicalSize);
9841 MediumVariant_T variant = MediumVariant_Standard; NOREF(variant);
9842
9843 /*
9844 * it's exactly moving, not cloning
9845 */
9846 if (!i_isMoveOperation(pTarget))
9847 {
9848 HRESULT rc = setError(VBOX_E_FILE_ERROR,
9849 tr("Wrong preconditions for moving the medium %s"),
9850 pTarget->m->strLocationFull.c_str());
9851 LogFlowFunc(("LEAVE: rc=%Rhrc (early)\n", rc));
9852 return rc;
9853 }
9854
9855 try
9856 {
9857 /* Lock all in {parent,child} order. The lock is also used as a
9858 * signal from the task initiator (which releases it only after
9859 * RTThreadCreate()) that we can start the job. */
9860
9861 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9862
9863 PVDISK hdd;
9864 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9865 ComAssertRCThrow(vrc, E_FAIL);
9866
9867 try
9868 {
9869 /* Open all media in the source chain. */
9870 MediumLockList::Base::const_iterator sourceListBegin =
9871 task.mpMediumLockList->GetBegin();
9872 MediumLockList::Base::const_iterator sourceListEnd =
9873 task.mpMediumLockList->GetEnd();
9874 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9875 it != sourceListEnd;
9876 ++it)
9877 {
9878 const MediumLock &mediumLock = *it;
9879 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9880 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9881
9882 /* sanity check */
9883 Assert(pMedium->m->state == MediumState_LockedWrite);
9884
9885 vrc = VDOpen(hdd,
9886 pMedium->m->strFormat.c_str(),
9887 pMedium->m->strLocationFull.c_str(),
9888 VD_OPEN_FLAGS_NORMAL,
9889 pMedium->m->vdImageIfaces);
9890 if (RT_FAILURE(vrc))
9891 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9892 tr("Could not open the medium storage unit '%s'%s"),
9893 pMedium->m->strLocationFull.c_str(),
9894 i_vdError(vrc).c_str());
9895 }
9896
9897 /* we can directly use pTarget->m->"variables" but for better reading we use local copies */
9898 Guid targetId = pTarget->m->id;
9899 Utf8Str targetFormat(pTarget->m->strFormat);
9900 uint64_t targetCapabilities = pTarget->m->formatObj->i_getCapabilities();
9901
9902 /*
9903 * change target location
9904 * m->strNewLocationFull has been set already together with m->fMoveThisMedium in
9905 * i_preparationForMoving()
9906 */
9907 Utf8Str targetLocation = i_getNewLocationForMoving();
9908
9909 /* unlock before the potentially lengthy operation */
9910 thisLock.release();
9911
9912 /* ensure the target directory exists */
9913 if (targetCapabilities & MediumFormatCapabilities_File)
9914 {
9915 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9916 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9917 if (FAILED(rc))
9918 throw rc;
9919 }
9920
9921 try
9922 {
9923 vrc = VDCopy(hdd,
9924 VD_LAST_IMAGE,
9925 hdd,
9926 targetFormat.c_str(),
9927 targetLocation.c_str(),
9928 true /* fMoveByRename */,
9929 0 /* cbSize */,
9930 VD_IMAGE_FLAGS_NONE,
9931 targetId.raw(),
9932 VD_OPEN_FLAGS_NORMAL,
9933 NULL /* pVDIfsOperation */,
9934 pTarget->m->vdImageIfaces,
9935 task.mVDOperationIfaces);
9936 if (RT_FAILURE(vrc))
9937 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9938 tr("Could not move medium '%s'%s"),
9939 targetLocation.c_str(), i_vdError(vrc).c_str());
9940 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9941 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9942 unsigned uImageFlags;
9943 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9944 if (RT_SUCCESS(vrc))
9945 variant = (MediumVariant_T)uImageFlags;
9946
9947 /*
9948 * set current location, because VDCopy\VDCopyEx doesn't do it.
9949 * also reset moving flag
9950 */
9951 i_resetMoveOperationData();
9952 m->strLocationFull = targetLocation;
9953
9954 }
9955 catch (HRESULT aRC) { rcOut = aRC; }
9956
9957 }
9958 catch (HRESULT aRC) { rcOut = aRC; }
9959
9960 VDDestroy(hdd);
9961 }
9962 catch (HRESULT aRC) { rcOut = aRC; }
9963
9964 ErrorInfoKeeper eik;
9965 MultiResult mrc(rcOut);
9966
9967 // now, at the end of this task (always asynchronous), save the settings
9968 if (SUCCEEDED(mrc))
9969 {
9970 // save the settings
9971 i_markRegistriesModified();
9972 /* collect multiple errors */
9973 eik.restore();
9974 m->pVirtualBox->i_saveModifiedRegistries();
9975 eik.fetch();
9976 }
9977
9978 /* Everything is explicitly unlocked when the task exits,
9979 * as the task destruction also destroys the source chain. */
9980
9981 task.mpMediumLockList->Clear();
9982
9983 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
9984 m->pVirtualBox->i_onMediumConfigChanged(this);
9985
9986 LogFlowFunc(("LEAVE: mrc=%Rhrc\n", (HRESULT)mrc));
9987 return mrc;
9988}
9989
9990/**
9991 * Implementation code for the "delete" task.
9992 *
9993 * This task always gets started from Medium::deleteStorage() and can run
9994 * synchronously or asynchronously depending on the "wait" parameter passed to
9995 * that function.
9996 *
9997 * @param task
9998 * @return
9999 */
10000HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
10001{
10002 NOREF(task);
10003 HRESULT rc = S_OK;
10004
10005 try
10006 {
10007 /* The lock is also used as a signal from the task initiator (which
10008 * releases it only after RTThreadCreate()) that we can start the job */
10009 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10010
10011 PVDISK hdd;
10012 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10013 ComAssertRCThrow(vrc, E_FAIL);
10014
10015 Utf8Str format(m->strFormat);
10016 Utf8Str location(m->strLocationFull);
10017
10018 /* unlock before the potentially lengthy operation */
10019 Assert(m->state == MediumState_Deleting);
10020 thisLock.release();
10021
10022 try
10023 {
10024 vrc = VDOpen(hdd,
10025 format.c_str(),
10026 location.c_str(),
10027 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
10028 m->vdImageIfaces);
10029 if (RT_SUCCESS(vrc))
10030 vrc = VDClose(hdd, true /* fDelete */);
10031
10032 if (RT_FAILURE(vrc) && vrc != VERR_FILE_NOT_FOUND)
10033 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10034 tr("Could not delete the medium storage unit '%s'%s"),
10035 location.c_str(), i_vdError(vrc).c_str());
10036
10037 }
10038 catch (HRESULT aRC) { rc = aRC; }
10039
10040 VDDestroy(hdd);
10041 }
10042 catch (HRESULT aRC) { rc = aRC; }
10043
10044 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10045
10046 /* go to the NotCreated state even on failure since the storage
10047 * may have been already partially deleted and cannot be used any
10048 * more. One will be able to manually re-open the storage if really
10049 * needed to re-register it. */
10050 m->state = MediumState_NotCreated;
10051
10052 /* Reset UUID to prevent Create* from reusing it again */
10053 com::Guid uOldId = m->id;
10054 unconst(m->id).clear();
10055
10056 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
10057 {
10058 if (m->pParent.isNotNull())
10059 m->pVirtualBox->i_onMediumConfigChanged(m->pParent);
10060 m->pVirtualBox->i_onMediumRegistered(uOldId, m->devType, FALSE);
10061 }
10062
10063 return rc;
10064}
10065
10066/**
10067 * Implementation code for the "reset" task.
10068 *
10069 * This always gets started asynchronously from Medium::Reset().
10070 *
10071 * @param task
10072 * @return
10073 */
10074HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
10075{
10076 HRESULT rc = S_OK;
10077
10078 uint64_t size = 0, logicalSize = 0;
10079 MediumVariant_T variant = MediumVariant_Standard;
10080
10081 try
10082 {
10083 /* The lock is also used as a signal from the task initiator (which
10084 * releases it only after RTThreadCreate()) that we can start the job */
10085 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10086
10087 /// @todo Below we use a pair of delete/create operations to reset
10088 /// the diff contents but the most efficient way will of course be
10089 /// to add a VDResetDiff() API call
10090
10091 PVDISK hdd;
10092 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10093 ComAssertRCThrow(vrc, E_FAIL);
10094
10095 Guid id = m->id;
10096 Utf8Str format(m->strFormat);
10097 Utf8Str location(m->strLocationFull);
10098
10099 Medium *pParent = m->pParent;
10100 Guid parentId = pParent->m->id;
10101 Utf8Str parentFormat(pParent->m->strFormat);
10102 Utf8Str parentLocation(pParent->m->strLocationFull);
10103
10104 Assert(m->state == MediumState_LockedWrite);
10105
10106 /* unlock before the potentially lengthy operation */
10107 thisLock.release();
10108
10109 try
10110 {
10111 /* Open all media in the target chain but the last. */
10112 MediumLockList::Base::const_iterator targetListBegin =
10113 task.mpMediumLockList->GetBegin();
10114 MediumLockList::Base::const_iterator targetListEnd =
10115 task.mpMediumLockList->GetEnd();
10116 for (MediumLockList::Base::const_iterator it = targetListBegin;
10117 it != targetListEnd;
10118 ++it)
10119 {
10120 const MediumLock &mediumLock = *it;
10121 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10122
10123 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10124
10125 /* sanity check, "this" is checked above */
10126 Assert( pMedium == this
10127 || pMedium->m->state == MediumState_LockedRead);
10128
10129 /* Open all media in appropriate mode. */
10130 vrc = VDOpen(hdd,
10131 pMedium->m->strFormat.c_str(),
10132 pMedium->m->strLocationFull.c_str(),
10133 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
10134 pMedium->m->vdImageIfaces);
10135 if (RT_FAILURE(vrc))
10136 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10137 tr("Could not open the medium storage unit '%s'%s"),
10138 pMedium->m->strLocationFull.c_str(),
10139 i_vdError(vrc).c_str());
10140
10141 /* Done when we hit the media which should be reset */
10142 if (pMedium == this)
10143 break;
10144 }
10145
10146 /* first, delete the storage unit */
10147 vrc = VDClose(hdd, true /* fDelete */);
10148 if (RT_FAILURE(vrc))
10149 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10150 tr("Could not delete the medium storage unit '%s'%s"),
10151 location.c_str(), i_vdError(vrc).c_str());
10152
10153 /* next, create it again */
10154 vrc = VDOpen(hdd,
10155 parentFormat.c_str(),
10156 parentLocation.c_str(),
10157 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
10158 m->vdImageIfaces);
10159 if (RT_FAILURE(vrc))
10160 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10161 tr("Could not open the medium storage unit '%s'%s"),
10162 parentLocation.c_str(), i_vdError(vrc).c_str());
10163
10164 vrc = VDCreateDiff(hdd,
10165 format.c_str(),
10166 location.c_str(),
10167 /// @todo use the same medium variant as before
10168 VD_IMAGE_FLAGS_NONE,
10169 NULL,
10170 id.raw(),
10171 parentId.raw(),
10172 VD_OPEN_FLAGS_NORMAL,
10173 m->vdImageIfaces,
10174 task.mVDOperationIfaces);
10175 if (RT_FAILURE(vrc))
10176 {
10177 if (vrc == VERR_VD_INVALID_TYPE)
10178 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10179 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
10180 location.c_str(), i_vdError(vrc).c_str());
10181 else
10182 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10183 tr("Could not create the differencing medium storage unit '%s'%s"),
10184 location.c_str(), i_vdError(vrc).c_str());
10185 }
10186
10187 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10188 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10189 unsigned uImageFlags;
10190 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
10191 if (RT_SUCCESS(vrc))
10192 variant = (MediumVariant_T)uImageFlags;
10193 }
10194 catch (HRESULT aRC) { rc = aRC; }
10195
10196 VDDestroy(hdd);
10197 }
10198 catch (HRESULT aRC) { rc = aRC; }
10199
10200 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10201
10202 m->size = size;
10203 m->logicalSize = logicalSize;
10204 m->variant = variant;
10205
10206 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
10207 m->pVirtualBox->i_onMediumConfigChanged(this);
10208
10209 /* Everything is explicitly unlocked when the task exits,
10210 * as the task destruction also destroys the media chain. */
10211
10212 return rc;
10213}
10214
10215/**
10216 * Implementation code for the "compact" task.
10217 *
10218 * @param task
10219 * @return
10220 */
10221HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
10222{
10223 HRESULT rc = S_OK;
10224
10225 /* Lock all in {parent,child} order. The lock is also used as a
10226 * signal from the task initiator (which releases it only after
10227 * RTThreadCreate()) that we can start the job. */
10228 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10229
10230 try
10231 {
10232 PVDISK hdd;
10233 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10234 ComAssertRCThrow(vrc, E_FAIL);
10235
10236 try
10237 {
10238 /* Open all media in the chain. */
10239 MediumLockList::Base::const_iterator mediumListBegin =
10240 task.mpMediumLockList->GetBegin();
10241 MediumLockList::Base::const_iterator mediumListEnd =
10242 task.mpMediumLockList->GetEnd();
10243 MediumLockList::Base::const_iterator mediumListLast =
10244 mediumListEnd;
10245 --mediumListLast;
10246 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10247 it != mediumListEnd;
10248 ++it)
10249 {
10250 const MediumLock &mediumLock = *it;
10251 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10252 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10253
10254 /* sanity check */
10255 if (it == mediumListLast)
10256 Assert(pMedium->m->state == MediumState_LockedWrite);
10257 else
10258 Assert(pMedium->m->state == MediumState_LockedRead);
10259
10260 /* Open all media but last in read-only mode. Do not handle
10261 * shareable media, as compaction and sharing are mutually
10262 * exclusive. */
10263 vrc = VDOpen(hdd,
10264 pMedium->m->strFormat.c_str(),
10265 pMedium->m->strLocationFull.c_str(),
10266 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10267 pMedium->m->vdImageIfaces);
10268 if (RT_FAILURE(vrc))
10269 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10270 tr("Could not open the medium storage unit '%s'%s"),
10271 pMedium->m->strLocationFull.c_str(),
10272 i_vdError(vrc).c_str());
10273 }
10274
10275 Assert(m->state == MediumState_LockedWrite);
10276
10277 Utf8Str location(m->strLocationFull);
10278
10279 /* unlock before the potentially lengthy operation */
10280 thisLock.release();
10281
10282 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
10283 if (RT_FAILURE(vrc))
10284 {
10285 if (vrc == VERR_NOT_SUPPORTED)
10286 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10287 tr("Compacting is not yet supported for medium '%s'"),
10288 location.c_str());
10289 else if (vrc == VERR_NOT_IMPLEMENTED)
10290 throw setErrorBoth(E_NOTIMPL, vrc,
10291 tr("Compacting is not implemented, medium '%s'"),
10292 location.c_str());
10293 else
10294 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10295 tr("Could not compact medium '%s'%s"),
10296 location.c_str(),
10297 i_vdError(vrc).c_str());
10298 }
10299 }
10300 catch (HRESULT aRC) { rc = aRC; }
10301
10302 VDDestroy(hdd);
10303 }
10304 catch (HRESULT aRC) { rc = aRC; }
10305
10306 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
10307 m->pVirtualBox->i_onMediumConfigChanged(this);
10308
10309 /* Everything is explicitly unlocked when the task exits,
10310 * as the task destruction also destroys the media chain. */
10311
10312 return rc;
10313}
10314
10315/**
10316 * Implementation code for the "resize" task.
10317 *
10318 * @param task
10319 * @return
10320 */
10321HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
10322{
10323 HRESULT rc = S_OK;
10324
10325 uint64_t size = 0, logicalSize = 0;
10326
10327 try
10328 {
10329 /* The lock is also used as a signal from the task initiator (which
10330 * releases it only after RTThreadCreate()) that we can start the job */
10331 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10332
10333 PVDISK hdd;
10334 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10335 ComAssertRCThrow(vrc, E_FAIL);
10336
10337 try
10338 {
10339 /* Open all media in the chain. */
10340 MediumLockList::Base::const_iterator mediumListBegin =
10341 task.mpMediumLockList->GetBegin();
10342 MediumLockList::Base::const_iterator mediumListEnd =
10343 task.mpMediumLockList->GetEnd();
10344 MediumLockList::Base::const_iterator mediumListLast =
10345 mediumListEnd;
10346 --mediumListLast;
10347 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10348 it != mediumListEnd;
10349 ++it)
10350 {
10351 const MediumLock &mediumLock = *it;
10352 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10353 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10354
10355 /* sanity check */
10356 if (it == mediumListLast)
10357 Assert(pMedium->m->state == MediumState_LockedWrite);
10358 else
10359 Assert(pMedium->m->state == MediumState_LockedRead ||
10360 // Allow resize the target image during mergeTo in case
10361 // of direction from parent to child because all intermediate
10362 // images are marked to MediumState_Deleting and will be
10363 // destroyed after successful merge
10364 pMedium->m->state == MediumState_Deleting);
10365
10366 /* Open all media but last in read-only mode. Do not handle
10367 * shareable media, as compaction and sharing are mutually
10368 * exclusive. */
10369 vrc = VDOpen(hdd,
10370 pMedium->m->strFormat.c_str(),
10371 pMedium->m->strLocationFull.c_str(),
10372 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10373 pMedium->m->vdImageIfaces);
10374 if (RT_FAILURE(vrc))
10375 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10376 tr("Could not open the medium storage unit '%s'%s"),
10377 pMedium->m->strLocationFull.c_str(),
10378 i_vdError(vrc).c_str());
10379 }
10380
10381 Assert(m->state == MediumState_LockedWrite);
10382
10383 Utf8Str location(m->strLocationFull);
10384
10385 /* unlock before the potentially lengthy operation */
10386 thisLock.release();
10387
10388 VDGEOMETRY geo = {0, 0, 0}; /* auto */
10389 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
10390 if (RT_FAILURE(vrc))
10391 {
10392 if (vrc == VERR_VD_SHRINK_NOT_SUPPORTED)
10393 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10394 tr("Shrinking is not yet supported for medium '%s'"),
10395 location.c_str());
10396 if (vrc == VERR_NOT_SUPPORTED)
10397 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10398 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
10399 task.mSize, location.c_str());
10400 else if (vrc == VERR_NOT_IMPLEMENTED)
10401 throw setErrorBoth(E_NOTIMPL, vrc,
10402 tr("Resizing is not implemented, medium '%s'"),
10403 location.c_str());
10404 else
10405 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10406 tr("Could not resize medium '%s'%s"),
10407 location.c_str(),
10408 i_vdError(vrc).c_str());
10409 }
10410 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10411 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10412 }
10413 catch (HRESULT aRC) { rc = aRC; }
10414
10415 VDDestroy(hdd);
10416 }
10417 catch (HRESULT aRC) { rc = aRC; }
10418
10419 if (SUCCEEDED(rc))
10420 {
10421 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10422 m->size = size;
10423 m->logicalSize = logicalSize;
10424
10425 if (task.NotifyAboutChanges())
10426 m->pVirtualBox->i_onMediumConfigChanged(this);
10427 }
10428
10429 /* Everything is explicitly unlocked when the task exits,
10430 * as the task destruction also destroys the media chain. */
10431
10432 return rc;
10433}
10434
10435/**
10436 * Implementation code for the "import" task.
10437 *
10438 * This only gets started from Medium::importFile() and always runs
10439 * asynchronously. It potentially touches the media registry, so we
10440 * always save the VirtualBox.xml file when we're done here.
10441 *
10442 * @param task
10443 * @return
10444 */
10445HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
10446{
10447 /** @todo r=klaus The code below needs to be double checked with regard
10448 * to lock order violations, it probably causes lock order issues related
10449 * to the AutoCaller usage. */
10450 HRESULT rcTmp = S_OK;
10451
10452 const ComObjPtr<Medium> &pParent = task.mParent;
10453
10454 bool fCreatingTarget = false;
10455
10456 uint64_t size = 0, logicalSize = 0;
10457 MediumVariant_T variant = MediumVariant_Standard;
10458 bool fGenerateUuid = false;
10459
10460 try
10461 {
10462 if (!pParent.isNull())
10463 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
10464 {
10465 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
10466 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10467 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
10468 pParent->m->strLocationFull.c_str());
10469 }
10470
10471 /* Lock all in {parent,child} order. The lock is also used as a
10472 * signal from the task initiator (which releases it only after
10473 * RTThreadCreate()) that we can start the job. */
10474 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
10475
10476 fCreatingTarget = m->state == MediumState_Creating;
10477
10478 /* The object may request a specific UUID (through a special form of
10479 * the moveTo() argument). Otherwise we have to generate it */
10480 Guid targetId = m->id;
10481
10482 fGenerateUuid = targetId.isZero();
10483 if (fGenerateUuid)
10484 {
10485 targetId.create();
10486 /* VirtualBox::i_registerMedium() will need UUID */
10487 unconst(m->id) = targetId;
10488 }
10489
10490
10491 PVDISK hdd;
10492 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10493 ComAssertRCThrow(vrc, E_FAIL);
10494
10495 try
10496 {
10497 /* Open source medium. */
10498 vrc = VDOpen(hdd,
10499 task.mFormat->i_getId().c_str(),
10500 task.mFilename.c_str(),
10501 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
10502 task.mVDImageIfaces);
10503 if (RT_FAILURE(vrc))
10504 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10505 tr("Could not open the medium storage unit '%s'%s"),
10506 task.mFilename.c_str(),
10507 i_vdError(vrc).c_str());
10508
10509 Utf8Str targetFormat(m->strFormat);
10510 Utf8Str targetLocation(m->strLocationFull);
10511 uint64_t capabilities = task.mFormat->i_getCapabilities();
10512
10513 Assert( m->state == MediumState_Creating
10514 || m->state == MediumState_LockedWrite);
10515 Assert( pParent.isNull()
10516 || pParent->m->state == MediumState_LockedRead);
10517
10518 /* unlock before the potentially lengthy operation */
10519 thisLock.release();
10520
10521 /* ensure the target directory exists */
10522 if (capabilities & MediumFormatCapabilities_File)
10523 {
10524 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
10525 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
10526 if (FAILED(rc))
10527 throw rc;
10528 }
10529
10530 PVDISK targetHdd;
10531 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
10532 ComAssertRCThrow(vrc, E_FAIL);
10533
10534 try
10535 {
10536 /* Open all media in the target chain. */
10537 MediumLockList::Base::const_iterator targetListBegin =
10538 task.mpTargetMediumLockList->GetBegin();
10539 MediumLockList::Base::const_iterator targetListEnd =
10540 task.mpTargetMediumLockList->GetEnd();
10541 for (MediumLockList::Base::const_iterator it = targetListBegin;
10542 it != targetListEnd;
10543 ++it)
10544 {
10545 const MediumLock &mediumLock = *it;
10546 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10547
10548 /* If the target medium is not created yet there's no
10549 * reason to open it. */
10550 if (pMedium == this && fCreatingTarget)
10551 continue;
10552
10553 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10554
10555 /* sanity check */
10556 Assert( pMedium->m->state == MediumState_LockedRead
10557 || pMedium->m->state == MediumState_LockedWrite);
10558
10559 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
10560 if (pMedium->m->state != MediumState_LockedWrite)
10561 uOpenFlags = VD_OPEN_FLAGS_READONLY;
10562 if (pMedium->m->type == MediumType_Shareable)
10563 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
10564
10565 /* Open all media in appropriate mode. */
10566 vrc = VDOpen(targetHdd,
10567 pMedium->m->strFormat.c_str(),
10568 pMedium->m->strLocationFull.c_str(),
10569 uOpenFlags | m->uOpenFlagsDef,
10570 pMedium->m->vdImageIfaces);
10571 if (RT_FAILURE(vrc))
10572 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10573 tr("Could not open the medium storage unit '%s'%s"),
10574 pMedium->m->strLocationFull.c_str(),
10575 i_vdError(vrc).c_str());
10576 }
10577
10578 vrc = VDCopy(hdd,
10579 VD_LAST_IMAGE,
10580 targetHdd,
10581 targetFormat.c_str(),
10582 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
10583 false /* fMoveByRename */,
10584 0 /* cbSize */,
10585 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
10586 targetId.raw(),
10587 VD_OPEN_FLAGS_NORMAL,
10588 NULL /* pVDIfsOperation */,
10589 m->vdImageIfaces,
10590 task.mVDOperationIfaces);
10591 if (RT_FAILURE(vrc))
10592 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10593 tr("Could not create the imported medium '%s'%s"),
10594 targetLocation.c_str(), i_vdError(vrc).c_str());
10595
10596 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
10597 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
10598 unsigned uImageFlags;
10599 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
10600 if (RT_SUCCESS(vrc))
10601 variant = (MediumVariant_T)uImageFlags;
10602 }
10603 catch (HRESULT aRC) { rcTmp = aRC; }
10604
10605 VDDestroy(targetHdd);
10606 }
10607 catch (HRESULT aRC) { rcTmp = aRC; }
10608
10609 VDDestroy(hdd);
10610 }
10611 catch (HRESULT aRC) { rcTmp = aRC; }
10612
10613 ErrorInfoKeeper eik;
10614 MultiResult mrc(rcTmp);
10615
10616 /* Only do the parent changes for newly created media. */
10617 if (SUCCEEDED(mrc) && fCreatingTarget)
10618 {
10619 /* we set m->pParent & children() */
10620 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10621
10622 Assert(m->pParent.isNull());
10623
10624 if (pParent)
10625 {
10626 /* Associate the imported medium with the parent and deassociate
10627 * from VirtualBox. Depth check above. */
10628 i_setParent(pParent);
10629
10630 /* register with mVirtualBox as the last step and move to
10631 * Created state only on success (leaving an orphan file is
10632 * better than breaking media registry consistency) */
10633 eik.restore();
10634 ComObjPtr<Medium> pMedium;
10635 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
10636 treeLock);
10637 Assert(this == pMedium);
10638 eik.fetch();
10639
10640 if (FAILED(mrc))
10641 /* break parent association on failure to register */
10642 this->i_deparent(); // removes target from parent
10643 }
10644 else
10645 {
10646 /* just register */
10647 eik.restore();
10648 ComObjPtr<Medium> pMedium;
10649 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
10650 Assert(this == pMedium);
10651 eik.fetch();
10652 }
10653 }
10654
10655 if (fCreatingTarget)
10656 {
10657 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
10658
10659 if (SUCCEEDED(mrc))
10660 {
10661 m->state = MediumState_Created;
10662
10663 m->size = size;
10664 m->logicalSize = logicalSize;
10665 m->variant = variant;
10666 }
10667 else
10668 {
10669 /* back to NotCreated on failure */
10670 m->state = MediumState_NotCreated;
10671
10672 /* reset UUID to prevent it from being reused next time */
10673 if (fGenerateUuid)
10674 unconst(m->id).clear();
10675 }
10676 }
10677
10678 // now, at the end of this task (always asynchronous), save the settings
10679 {
10680 // save the settings
10681 i_markRegistriesModified();
10682 /* collect multiple errors */
10683 eik.restore();
10684 m->pVirtualBox->i_saveModifiedRegistries();
10685 eik.fetch();
10686 }
10687
10688 /* Everything is explicitly unlocked when the task exits,
10689 * as the task destruction also destroys the target chain. */
10690
10691 /* Make sure the target chain is released early, otherwise it can
10692 * lead to deadlocks with concurrent IAppliance activities. */
10693 task.mpTargetMediumLockList->Clear();
10694
10695 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
10696 {
10697 if (pParent)
10698 m->pVirtualBox->i_onMediumConfigChanged(pParent);
10699 if (fCreatingTarget)
10700 m->pVirtualBox->i_onMediumConfigChanged(this);
10701 else
10702 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
10703 }
10704
10705 return mrc;
10706}
10707
10708/**
10709 * Sets up the encryption settings for a filter.
10710 */
10711void Medium::i_taskEncryptSettingsSetup(MediumCryptoFilterSettings *pSettings, const char *pszCipher,
10712 const char *pszKeyStore, const char *pszPassword,
10713 bool fCreateKeyStore)
10714{
10715 pSettings->pszCipher = pszCipher;
10716 pSettings->pszPassword = pszPassword;
10717 pSettings->pszKeyStoreLoad = pszKeyStore;
10718 pSettings->fCreateKeyStore = fCreateKeyStore;
10719 pSettings->pbDek = NULL;
10720 pSettings->cbDek = 0;
10721 pSettings->vdFilterIfaces = NULL;
10722
10723 pSettings->vdIfCfg.pfnAreKeysValid = i_vdCryptoConfigAreKeysValid;
10724 pSettings->vdIfCfg.pfnQuerySize = i_vdCryptoConfigQuerySize;
10725 pSettings->vdIfCfg.pfnQuery = i_vdCryptoConfigQuery;
10726 pSettings->vdIfCfg.pfnQueryBytes = NULL;
10727
10728 pSettings->vdIfCrypto.pfnKeyRetain = i_vdCryptoKeyRetain;
10729 pSettings->vdIfCrypto.pfnKeyRelease = i_vdCryptoKeyRelease;
10730 pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = i_vdCryptoKeyStorePasswordRetain;
10731 pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = i_vdCryptoKeyStorePasswordRelease;
10732 pSettings->vdIfCrypto.pfnKeyStoreSave = i_vdCryptoKeyStoreSave;
10733 pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = i_vdCryptoKeyStoreReturnParameters;
10734
10735 int vrc = VDInterfaceAdd(&pSettings->vdIfCfg.Core,
10736 "Medium::vdInterfaceCfgCrypto",
10737 VDINTERFACETYPE_CONFIG, pSettings,
10738 sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces);
10739 AssertRC(vrc);
10740
10741 vrc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core,
10742 "Medium::vdInterfaceCrypto",
10743 VDINTERFACETYPE_CRYPTO, pSettings,
10744 sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces);
10745 AssertRC(vrc);
10746}
10747
10748/**
10749 * Implementation code for the "encrypt" task.
10750 *
10751 * @param task
10752 * @return
10753 */
10754HRESULT Medium::i_taskEncryptHandler(Medium::EncryptTask &task)
10755{
10756# ifndef VBOX_WITH_EXTPACK
10757 RT_NOREF(task);
10758# endif
10759 HRESULT rc = S_OK;
10760
10761 /* Lock all in {parent,child} order. The lock is also used as a
10762 * signal from the task initiator (which releases it only after
10763 * RTThreadCreate()) that we can start the job. */
10764 ComObjPtr<Medium> pBase = i_getBase();
10765 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10766
10767 try
10768 {
10769# ifdef VBOX_WITH_EXTPACK
10770 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
10771 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
10772 {
10773 /* Load the plugin */
10774 Utf8Str strPlugin;
10775 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
10776 if (SUCCEEDED(rc))
10777 {
10778 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
10779 if (RT_FAILURE(vrc))
10780 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10781 tr("Encrypting the image failed because the encryption plugin could not be loaded (%s)"),
10782 i_vdError(vrc).c_str());
10783 }
10784 else
10785 throw setError(VBOX_E_NOT_SUPPORTED,
10786 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
10787 ORACLE_PUEL_EXTPACK_NAME);
10788 }
10789 else
10790 throw setError(VBOX_E_NOT_SUPPORTED,
10791 tr("Encryption is not supported because the extension pack '%s' is missing"),
10792 ORACLE_PUEL_EXTPACK_NAME);
10793
10794 PVDISK pDisk = NULL;
10795 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
10796 ComAssertRCThrow(vrc, E_FAIL);
10797
10798 MediumCryptoFilterSettings CryptoSettingsRead;
10799 MediumCryptoFilterSettings CryptoSettingsWrite;
10800
10801 void *pvBuf = NULL;
10802 const char *pszPasswordNew = NULL;
10803 try
10804 {
10805 /* Set up disk encryption filters. */
10806 if (task.mstrCurrentPassword.isEmpty())
10807 {
10808 /*
10809 * Query whether the medium property indicating that encryption is
10810 * configured is existing.
10811 */
10812 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10813 if (it != pBase->m->mapProperties.end())
10814 throw setError(VBOX_E_PASSWORD_INCORRECT,
10815 tr("The password given for the encrypted image is incorrect"));
10816 }
10817 else
10818 {
10819 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10820 if (it == pBase->m->mapProperties.end())
10821 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10822 tr("The image is not configured for encryption"));
10823
10824 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, it->second.c_str(), task.mstrCurrentPassword.c_str(),
10825 false /* fCreateKeyStore */);
10826 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
10827 if (vrc == VERR_VD_PASSWORD_INCORRECT)
10828 throw setError(VBOX_E_PASSWORD_INCORRECT,
10829 tr("The password to decrypt the image is incorrect"));
10830 else if (RT_FAILURE(vrc))
10831 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10832 tr("Failed to load the decryption filter: %s"),
10833 i_vdError(vrc).c_str());
10834 }
10835
10836 if (task.mstrCipher.isNotEmpty())
10837 {
10838 if ( task.mstrNewPassword.isEmpty()
10839 && task.mstrNewPasswordId.isEmpty()
10840 && task.mstrCurrentPassword.isNotEmpty())
10841 {
10842 /* An empty password and password ID will default to the current password. */
10843 pszPasswordNew = task.mstrCurrentPassword.c_str();
10844 }
10845 else if (task.mstrNewPassword.isEmpty())
10846 throw setError(VBOX_E_OBJECT_NOT_FOUND,
10847 tr("A password must be given for the image encryption"));
10848 else if (task.mstrNewPasswordId.isEmpty())
10849 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10850 tr("A valid identifier for the password must be given"));
10851 else
10852 pszPasswordNew = task.mstrNewPassword.c_str();
10853
10854 i_taskEncryptSettingsSetup(&CryptoSettingsWrite, task.mstrCipher.c_str(), NULL,
10855 pszPasswordNew, true /* fCreateKeyStore */);
10856 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_WRITE, CryptoSettingsWrite.vdFilterIfaces);
10857 if (RT_FAILURE(vrc))
10858 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
10859 tr("Failed to load the encryption filter: %s"),
10860 i_vdError(vrc).c_str());
10861 }
10862 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
10863 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10864 tr("The password and password identifier must be empty if the output should be unencrypted"));
10865
10866 /* Open all media in the chain. */
10867 MediumLockList::Base::const_iterator mediumListBegin =
10868 task.mpMediumLockList->GetBegin();
10869 MediumLockList::Base::const_iterator mediumListEnd =
10870 task.mpMediumLockList->GetEnd();
10871 MediumLockList::Base::const_iterator mediumListLast =
10872 mediumListEnd;
10873 --mediumListLast;
10874 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10875 it != mediumListEnd;
10876 ++it)
10877 {
10878 const MediumLock &mediumLock = *it;
10879 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10880 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10881
10882 Assert(pMedium->m->state == MediumState_LockedWrite);
10883
10884 /* Open all media but last in read-only mode. Do not handle
10885 * shareable media, as compaction and sharing are mutually
10886 * exclusive. */
10887 vrc = VDOpen(pDisk,
10888 pMedium->m->strFormat.c_str(),
10889 pMedium->m->strLocationFull.c_str(),
10890 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10891 pMedium->m->vdImageIfaces);
10892 if (RT_FAILURE(vrc))
10893 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10894 tr("Could not open the medium storage unit '%s'%s"),
10895 pMedium->m->strLocationFull.c_str(),
10896 i_vdError(vrc).c_str());
10897 }
10898
10899 Assert(m->state == MediumState_LockedWrite);
10900
10901 Utf8Str location(m->strLocationFull);
10902
10903 /* unlock before the potentially lengthy operation */
10904 thisLock.release();
10905
10906 vrc = VDPrepareWithFilters(pDisk, task.mVDOperationIfaces);
10907 if (RT_FAILURE(vrc))
10908 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10909 tr("Could not prepare disk images for encryption (%Rrc): %s"),
10910 vrc, i_vdError(vrc).c_str());
10911
10912 thisLock.acquire();
10913 /* If everything went well set the new key store. */
10914 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10915 if (it != pBase->m->mapProperties.end())
10916 pBase->m->mapProperties.erase(it);
10917
10918 /* Delete KeyId if encryption is removed or the password did change. */
10919 if ( task.mstrNewPasswordId.isNotEmpty()
10920 || task.mstrCipher.isEmpty())
10921 {
10922 it = pBase->m->mapProperties.find("CRYPT/KeyId");
10923 if (it != pBase->m->mapProperties.end())
10924 pBase->m->mapProperties.erase(it);
10925 }
10926
10927 if (CryptoSettingsWrite.pszKeyStore)
10928 {
10929 pBase->m->mapProperties["CRYPT/KeyStore"] = Utf8Str(CryptoSettingsWrite.pszKeyStore);
10930 if (task.mstrNewPasswordId.isNotEmpty())
10931 pBase->m->mapProperties["CRYPT/KeyId"] = task.mstrNewPasswordId;
10932 }
10933
10934 if (CryptoSettingsRead.pszCipherReturned)
10935 RTStrFree(CryptoSettingsRead.pszCipherReturned);
10936
10937 if (CryptoSettingsWrite.pszCipherReturned)
10938 RTStrFree(CryptoSettingsWrite.pszCipherReturned);
10939
10940 thisLock.release();
10941 pBase->i_markRegistriesModified();
10942 m->pVirtualBox->i_saveModifiedRegistries();
10943 }
10944 catch (HRESULT aRC) { rc = aRC; }
10945
10946 if (pvBuf)
10947 RTMemFree(pvBuf);
10948
10949 VDDestroy(pDisk);
10950# else
10951 throw setError(VBOX_E_NOT_SUPPORTED,
10952 tr("Encryption is not supported because extension pack support is not built in"));
10953# endif
10954 }
10955 catch (HRESULT aRC) { rc = aRC; }
10956
10957 /* Everything is explicitly unlocked when the task exits,
10958 * as the task destruction also destroys the media chain. */
10959
10960 return rc;
10961}
10962
10963/* 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