VirtualBox

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

Last change on this file since 99991 was 99991, checked in by vboxsync, 12 months ago

Main: reimplements resizeAndCloneTo fix. removes bypass. bugref:10090

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

© 2023 Oracle
ContactPrivacy policyTerms of Use