VirtualBox

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

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

Main/Medium+VirtualBox: If two Machine instances of the same VM are
being torn down at the same time they can wrestle over the media tree
lock between Medium::i_close() and VirtualBox::i_unregisterMachineMedia()
and end up closing the same medium twice. Medium::i_close() drops the
media tree lock before calling Medium::uninit() and during this window
i_unregisterMachineMedia() can grab the lock and call i_close() for all
of the attached media including the medium in the process of being
closed. The fix checks inside i_close() whether the current medium is
already in the process of being closed and returns an error in such
cases. bugref:6447

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 397.1 KB
Line 
1/* $Id: MediumImpl.cpp 98847 2023-03-06 22:28:59Z 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 MediaList::const_reverse_iterator itBegin = llMediaTocleanup.rbegin();
1494 MediaList::const_reverse_iterator itEnd = llMediaTocleanup.rend();
1495 for (MediaList::const_reverse_iterator it = itBegin; it != itEnd; --it)
1496 {
1497 ComObjPtr<Medium> pMedium = *it;
1498 AutoCaller mediumCaller(pMedium);
1499 if (FAILED(mediumCaller.hrc())) continue;
1500 const Guid &id = pMedium->i_getId();
1501 uIdsForNotify.push_back(std::pair<Guid, DeviceType_T>(id, aDeviceType));
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 /** @todo r=jack: Remove redundancy. Call Medium::resizeAndCloneTo. */
3020
3021 /** @todo r=klaus The code below needs to be double checked with regard
3022 * to lock order violations, it probably causes lock order issues related
3023 * to the AutoCaller usage. */
3024 ComAssertRet(aTarget != this, E_INVALIDARG);
3025
3026 IMedium *aT = aTarget;
3027 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aT);
3028 ComObjPtr<Medium> pParent;
3029 if (aParent)
3030 {
3031 IMedium *aP = aParent;
3032 pParent = static_cast<Medium*>(aP);
3033 }
3034
3035 HRESULT hrc = S_OK;
3036 ComObjPtr<Progress> pProgress;
3037 Medium::Task *pTask = NULL;
3038
3039 try
3040 {
3041 // locking: we need the tree lock first because we access parent pointers
3042 // and we need to write-lock the media involved
3043 uint32_t cHandles = 3;
3044 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
3045 this->lockHandle(),
3046 pTarget->lockHandle() };
3047 /* Only add parent to the lock if it is not null */
3048 if (!pParent.isNull())
3049 pHandles[cHandles++] = pParent->lockHandle();
3050 AutoWriteLock alock(cHandles,
3051 pHandles
3052 COMMA_LOCKVAL_SRC_POS);
3053
3054 if ( pTarget->m->state != MediumState_NotCreated
3055 && pTarget->m->state != MediumState_Created)
3056 throw pTarget->i_setStateError();
3057
3058 /* Build the source lock list. */
3059 MediumLockList *pSourceMediumLockList(new MediumLockList());
3060 alock.release();
3061 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
3062 NULL /* pToLockWrite */,
3063 false /* fMediumLockWriteAll */,
3064 NULL,
3065 *pSourceMediumLockList);
3066 alock.acquire();
3067 if (FAILED(hrc))
3068 {
3069 delete pSourceMediumLockList;
3070 throw hrc;
3071 }
3072
3073 /* Build the target lock list (including the to-be parent chain). */
3074 MediumLockList *pTargetMediumLockList(new MediumLockList());
3075 alock.release();
3076 hrc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
3077 pTarget /* pToLockWrite */,
3078 false /* fMediumLockWriteAll */,
3079 pParent,
3080 *pTargetMediumLockList);
3081 alock.acquire();
3082 if (FAILED(hrc))
3083 {
3084 delete pSourceMediumLockList;
3085 delete pTargetMediumLockList;
3086 throw hrc;
3087 }
3088
3089 alock.release();
3090 hrc = pSourceMediumLockList->Lock();
3091 alock.acquire();
3092 if (FAILED(hrc))
3093 {
3094 delete pSourceMediumLockList;
3095 delete pTargetMediumLockList;
3096 throw setError(hrc,
3097 tr("Failed to lock source media '%s'"),
3098 i_getLocationFull().c_str());
3099 }
3100 alock.release();
3101 hrc = pTargetMediumLockList->Lock();
3102 alock.acquire();
3103 if (FAILED(hrc))
3104 {
3105 delete pSourceMediumLockList;
3106 delete pTargetMediumLockList;
3107 throw setError(hrc,
3108 tr("Failed to lock target media '%s'"),
3109 pTarget->i_getLocationFull().c_str());
3110 }
3111
3112 pProgress.createObject();
3113 hrc = pProgress->init(m->pVirtualBox,
3114 static_cast <IMedium *>(this),
3115 BstrFmt(tr("Creating clone medium '%s'"), pTarget->m->strLocationFull.c_str()).raw(),
3116 TRUE /* aCancelable */);
3117 if (FAILED(hrc))
3118 {
3119 delete pSourceMediumLockList;
3120 delete pTargetMediumLockList;
3121 throw hrc;
3122 }
3123
3124 ULONG mediumVariantFlags = 0;
3125
3126 if (aVariant.size())
3127 {
3128 for (size_t i = 0; i < aVariant.size(); i++)
3129 mediumVariantFlags |= (ULONG)aVariant[i];
3130 }
3131
3132 if (mediumVariantFlags & MediumVariant_Formatted)
3133 {
3134 delete pSourceMediumLockList;
3135 delete pTargetMediumLockList;
3136 throw setError(VBOX_E_NOT_SUPPORTED,
3137 tr("Medium variant 'formatted' applies to floppy images only"));
3138 }
3139
3140 /* setup task object to carry out the operation asynchronously */
3141 pTask = new Medium::CloneTask(this, pProgress, pTarget,
3142 (MediumVariant_T)mediumVariantFlags,
3143 pParent, UINT32_MAX, UINT32_MAX,
3144 pSourceMediumLockList, pTargetMediumLockList);
3145 hrc = pTask->hrc();
3146 AssertComRC(hrc);
3147 if (FAILED(hrc))
3148 throw hrc;
3149
3150 if (pTarget->m->state == MediumState_NotCreated)
3151 pTarget->m->state = MediumState_Creating;
3152 }
3153 catch (HRESULT hrcXcpt)
3154 {
3155 hrc = hrcXcpt;
3156 }
3157
3158 if (SUCCEEDED(hrc))
3159 {
3160 hrc = pTask->createThread();
3161 pTask = NULL;
3162 if (SUCCEEDED(hrc))
3163 pProgress.queryInterfaceTo(aProgress.asOutParam());
3164 }
3165 else if (pTask != NULL)
3166 delete pTask;
3167
3168 return hrc;
3169}
3170
3171/**
3172 * This is a helper function that combines the functionality of
3173 * Medium::cloneTo() and Medium::resize(). The target medium will take the
3174 * contents of the calling medium.
3175 *
3176 * @param aTarget Medium to resize and clone to
3177 * @param aLogicalSize Desired size for targer medium
3178 * @param aVariant
3179 * @param aParent
3180 * @param aProgress
3181 * @return HRESULT
3182 */
3183HRESULT Medium::resizeAndCloneTo(const ComPtr<IMedium> &aTarget,
3184 LONG64 aLogicalSize,
3185 const std::vector<MediumVariant_T> &aVariant,
3186 const ComPtr<IMedium> &aParent,
3187 ComPtr<IProgress> &aProgress)
3188{
3189 /* Check for valid args */
3190 ComAssertRet(aTarget != this, E_INVALIDARG);
3191 CheckComArgExpr(aLogicalSize, aLogicalSize >= 0);
3192
3193 /* Convert args to usable/needed types */
3194 IMedium *aT = aTarget;
3195 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aT);
3196 ComObjPtr<Medium> pParent;
3197 if (aParent)
3198 {
3199 IMedium *aP = aParent;
3200 pParent = static_cast<Medium*>(aP);
3201 }
3202
3203 /* Set up variables. Fetch needed data in lockable blocks */
3204 HRESULT hrc = S_OK;
3205 Medium::Task *pTask = NULL;
3206
3207 Utf8Str strSourceName;
3208 {
3209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3210 strSourceName = i_getName();
3211 }
3212
3213 uint64_t uTargetExistingSize = 0;
3214 Utf8Str strTargetName;
3215 {
3216 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
3217 uTargetExistingSize = pTarget->i_getLogicalSize();
3218 strTargetName = pTarget->i_getName();
3219 }
3220
3221 /* Set up internal multi-subprocess progress object */
3222 ComObjPtr<Progress> pProgress;
3223 pProgress.createObject();
3224 hrc = pProgress->init(m->pVirtualBox,
3225 static_cast<IMedium*>(this),
3226 BstrFmt(tr("Resizing medium and cloning into it")).raw(),
3227 TRUE, /* aCancelable */
3228 2, /* Number of opearations */
3229 BstrFmt(tr("Resizing medium before clone")).raw()
3230 );
3231 if (FAILED(hrc))
3232 return hrc;
3233
3234 /* If target does not exist, handle resize. */
3235 if (pTarget->m->state != MediumState_NotCreated && aLogicalSize > 0)
3236 {
3237 if ((LONG64)uTargetExistingSize != aLogicalSize) {
3238 if (!i_isMediumFormatFile())
3239 {
3240 hrc = setError(VBOX_E_NOT_SUPPORTED,
3241 tr("Sizes of '%s' and '%s' are different and medium format does not support resing"),
3242 strSourceName.c_str(), strTargetName.c_str());
3243 return hrc;
3244 }
3245
3246 /**
3247 * Need to lock the target medium as i_resize does do so
3248 * automatically.
3249 */
3250
3251 ComPtr<IToken> pToken;
3252 hrc = pTarget->LockWrite(pToken.asOutParam());
3253
3254 if (FAILED(hrc)) return hrc;
3255
3256 /**
3257 * Have to make own lock list, because "resize" method resizes only
3258 * last image in the lock chain.
3259 */
3260
3261 MediumLockList* pMediumLockListForResize = new MediumLockList();
3262 pMediumLockListForResize->Append(pTarget, pTarget->m->state == MediumState_LockedWrite);
3263
3264 hrc = pMediumLockListForResize->Lock(true /* fSkipOverLockedMedia */);
3265 if (FAILED(hrc))
3266 {
3267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3268 hrc = setError(hrc,
3269 tr("Failed to lock the medium '%s' to resize before merge"),
3270 strTargetName.c_str());
3271 delete pMediumLockListForResize;
3272 return hrc;
3273 }
3274
3275
3276 hrc = pTarget->i_resize((uint64_t)aLogicalSize, pMediumLockListForResize, &pProgress, true, false);
3277 if (FAILED(hrc))
3278 {
3279 /* No need to setError becasue i_resize and i_taskResizeHandler handle this automatically. */
3280 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3281 delete pMediumLockListForResize;
3282 return hrc;
3283 }
3284
3285 delete pMediumLockListForResize;
3286
3287 pTarget->m->logicalSize = (uint64_t)aLogicalSize;
3288
3289 pToken->Abandon();
3290 pToken.setNull();
3291 }
3292 }
3293
3294 /* Report progress to supplied progress argument */
3295 if (SUCCEEDED(hrc))
3296 {
3297 pProgress.queryInterfaceTo(aProgress.asOutParam());
3298 }
3299
3300 try
3301 {
3302 // locking: we need the tree lock first because we access parent pointers
3303 // and we need to write-lock the media involved
3304 uint32_t cHandles = 3;
3305 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
3306 this->lockHandle(),
3307 pTarget->lockHandle() };
3308 /* Only add parent to the lock if it is not null */
3309 if (!pParent.isNull())
3310 pHandles[cHandles++] = pParent->lockHandle();
3311 AutoWriteLock alock(cHandles,
3312 pHandles
3313 COMMA_LOCKVAL_SRC_POS);
3314
3315 if ( pTarget->m->state != MediumState_NotCreated
3316 && pTarget->m->state != MediumState_Created)
3317 throw pTarget->i_setStateError();
3318
3319 /* Build the source lock list. */
3320 MediumLockList *pSourceMediumLockList(new MediumLockList());
3321 alock.release();
3322 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
3323 NULL /* pToLockWrite */,
3324 false /* fMediumLockWriteAll */,
3325 NULL,
3326 *pSourceMediumLockList);
3327 alock.acquire();
3328 if (FAILED(hrc))
3329 {
3330 delete pSourceMediumLockList;
3331 throw hrc;
3332 }
3333
3334 /* Build the target lock list (including the to-be parent chain). */
3335 MediumLockList *pTargetMediumLockList(new MediumLockList());
3336 alock.release();
3337 hrc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
3338 pTarget /* pToLockWrite */,
3339 false /* fMediumLockWriteAll */,
3340 pParent,
3341 *pTargetMediumLockList);
3342 alock.acquire();
3343 if (FAILED(hrc))
3344 {
3345 delete pSourceMediumLockList;
3346 delete pTargetMediumLockList;
3347 throw hrc;
3348 }
3349
3350 alock.release();
3351 hrc = pSourceMediumLockList->Lock();
3352 alock.acquire();
3353 if (FAILED(hrc))
3354 {
3355 delete pSourceMediumLockList;
3356 delete pTargetMediumLockList;
3357 throw setError(hrc,
3358 tr("Failed to lock source media '%s'"),
3359 i_getLocationFull().c_str());
3360 }
3361 alock.release();
3362 hrc = pTargetMediumLockList->Lock();
3363 alock.acquire();
3364 if (FAILED(hrc))
3365 {
3366 delete pSourceMediumLockList;
3367 delete pTargetMediumLockList;
3368 throw setError(hrc,
3369 tr("Failed to lock target media '%s'"),
3370 pTarget->i_getLocationFull().c_str());
3371 }
3372
3373 ULONG mediumVariantFlags = 0;
3374
3375 if (aVariant.size())
3376 {
3377 for (size_t i = 0; i < aVariant.size(); i++)
3378 mediumVariantFlags |= (ULONG)aVariant[i];
3379 }
3380
3381 if (mediumVariantFlags & MediumVariant_Formatted)
3382 {
3383 delete pSourceMediumLockList;
3384 delete pTargetMediumLockList;
3385 throw setError(VBOX_E_NOT_SUPPORTED,
3386 tr("Medium variant 'formatted' applies to floppy images only"));
3387 }
3388
3389 if (pTarget->m->state != MediumState_NotCreated || aLogicalSize == 0)
3390 {
3391 /* setup task object to carry out the operation asynchronously */
3392 pTask = new Medium::CloneTask(this, pProgress, pTarget,
3393 (MediumVariant_T)mediumVariantFlags,
3394 pParent, UINT32_MAX, UINT32_MAX,
3395 pSourceMediumLockList, pTargetMediumLockList,
3396 false, false, true, 0);
3397 }
3398 else
3399 {
3400 /* setup task object to carry out the operation asynchronously */
3401 pTask = new Medium::CloneTask(this, pProgress, pTarget,
3402 (MediumVariant_T)mediumVariantFlags,
3403 pParent, UINT32_MAX, UINT32_MAX,
3404 pSourceMediumLockList, pTargetMediumLockList,
3405 false, false, true, (uint64_t)aLogicalSize);
3406 }
3407
3408 hrc = pTask->hrc();
3409 AssertComRC(hrc);
3410 if (FAILED(hrc))
3411 throw hrc;
3412
3413 if (pTarget->m->state == MediumState_NotCreated)
3414 pTarget->m->state = MediumState_Creating;
3415 }
3416 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
3417
3418 if (SUCCEEDED(hrc))
3419 {
3420 hrc = pTask->createThread();
3421 pTask = NULL;
3422 if (SUCCEEDED(hrc))
3423 pProgress.queryInterfaceTo(aProgress.asOutParam());
3424 }
3425 else if (pTask != NULL)
3426 delete pTask;
3427
3428 return hrc;
3429}
3430
3431HRESULT Medium::moveTo(AutoCaller &autoCaller, const com::Utf8Str &aLocation, ComPtr<IProgress> &aProgress)
3432{
3433 ComObjPtr<Medium> pParent;
3434 ComObjPtr<Progress> pProgress;
3435 HRESULT hrc = S_OK;
3436 Medium::Task *pTask = NULL;
3437
3438 try
3439 {
3440 /// @todo NEWMEDIA for file names, add the default extension if no extension
3441 /// is present (using the information from the VD backend which also implies
3442 /// that one more parameter should be passed to moveTo() requesting
3443 /// that functionality since it is only allowed when called from this method
3444
3445 /// @todo NEWMEDIA rename the file and set m->location on success, then save
3446 /// the global registry (and local registries of portable VMs referring to
3447 /// this medium), this will also require to add the mRegistered flag to data
3448
3449 autoCaller.release();
3450
3451 // locking: we need the tree lock first because we access parent pointers
3452 // and we need to write-lock the media involved
3453 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3454
3455 autoCaller.add();
3456 AssertComRCThrowRC(autoCaller.hrc());
3457
3458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3459
3460 /* play with locations */
3461 {
3462 /* get source path and filename */
3463 Utf8Str sourcePath = i_getLocationFull();
3464 Utf8Str sourceFName = i_getName();
3465
3466 if (aLocation.isEmpty())
3467 {
3468 hrc = setErrorVrc(VERR_PATH_ZERO_LENGTH,
3469 tr("Medium '%s' can't be moved. Destination path is empty."),
3470 i_getLocationFull().c_str());
3471 throw hrc;
3472 }
3473
3474 /* extract destination path and filename */
3475 Utf8Str destPath(aLocation);
3476 Utf8Str destFName(destPath);
3477 destFName.stripPath();
3478
3479 if (destFName.isNotEmpty() && !RTPathHasSuffix(destFName.c_str()))
3480 {
3481 /*
3482 * The target path has no filename: Either "/path/to/new/location" or
3483 * just "newname" (no trailing backslash or there is no filename extension).
3484 */
3485 if (destPath.equals(destFName))
3486 {
3487 /* new path contains only "newname", no path, no extension */
3488 destFName.append(RTPathSuffix(sourceFName.c_str()));
3489 destPath = destFName;
3490 }
3491 else
3492 {
3493 /* new path looks like "/path/to/new/location" */
3494 destFName.setNull();
3495 destPath.append(RTPATH_SLASH);
3496 }
3497 }
3498
3499 if (destFName.isEmpty())
3500 {
3501 /* No target name */
3502 destPath.append(sourceFName);
3503 }
3504 else
3505 {
3506 if (destPath.equals(destFName))
3507 {
3508 /*
3509 * The target path contains of only a filename without a directory.
3510 * Move the medium within the source directory to the new name
3511 * (actually rename operation).
3512 * Scratches sourcePath!
3513 */
3514 destPath = sourcePath.stripFilename().append(RTPATH_SLASH).append(destFName);
3515 }
3516
3517 const char *pszSuffix = RTPathSuffix(sourceFName.c_str());
3518
3519 /* Suffix is empty and one is deduced from the medium format */
3520 if (pszSuffix == NULL)
3521 {
3522 Utf8Str strExt = i_getFormat();
3523 if (strExt.compare("RAW", Utf8Str::CaseInsensitive) == 0)
3524 {
3525 DeviceType_T devType = i_getDeviceType();
3526 switch (devType)
3527 {
3528 case DeviceType_DVD:
3529 strExt = "iso";
3530 break;
3531 case DeviceType_Floppy:
3532 strExt = "img";
3533 break;
3534 default:
3535 hrc = setErrorVrc(VERR_NOT_A_FILE, /** @todo r=bird: Mixing status codes again. */
3536 tr("Medium '%s' has RAW type. \"Move\" operation isn't supported for this type."),
3537 i_getLocationFull().c_str());
3538 throw hrc;
3539 }
3540 }
3541 else if (strExt.compare("Parallels", Utf8Str::CaseInsensitive) == 0)
3542 {
3543 strExt = "hdd";
3544 }
3545
3546 /* Set the target extension like on the source. Any conversions are prohibited */
3547 strExt.toLower();
3548 destPath.stripSuffix().append('.').append(strExt);
3549 }
3550 else
3551 destPath.stripSuffix().append(pszSuffix);
3552 }
3553
3554 /* Simple check for existence */
3555 if (RTFileExists(destPath.c_str()))
3556 throw setError(VBOX_E_FILE_ERROR,
3557 tr("The given path '%s' is an existing file. Delete or rename this file."),
3558 destPath.c_str());
3559
3560 if (!i_isMediumFormatFile())
3561 throw setErrorVrc(VERR_NOT_A_FILE,
3562 tr("Medium '%s' isn't a file object. \"Move\" operation isn't supported."),
3563 i_getLocationFull().c_str());
3564 /* Path must be absolute */
3565 if (!RTPathStartsWithRoot(destPath.c_str()))
3566 throw setError(VBOX_E_FILE_ERROR,
3567 tr("The given path '%s' is not fully qualified"),
3568 destPath.c_str());
3569 /* Check path for a new file object */
3570 hrc = VirtualBox::i_ensureFilePathExists(destPath, true);
3571 if (FAILED(hrc))
3572 throw hrc;
3573
3574 /* Set needed variables for "moving" procedure. It'll be used later in separate thread task */
3575 hrc = i_preparationForMoving(destPath);
3576 if (FAILED(hrc))
3577 throw setErrorVrc(VERR_NO_CHANGE,
3578 tr("Medium '%s' is already in the correct location"),
3579 i_getLocationFull().c_str());
3580 }
3581
3582 /* Check VMs which have this medium attached to*/
3583 std::vector<com::Guid> aMachineIds;
3584 hrc = getMachineIds(aMachineIds);
3585 std::vector<com::Guid>::const_iterator currMachineID = aMachineIds.begin();
3586 std::vector<com::Guid>::const_iterator lastMachineID = aMachineIds.end();
3587
3588 while (currMachineID != lastMachineID)
3589 {
3590 Guid id(*currMachineID);
3591 ComObjPtr<Machine> aMachine;
3592
3593 alock.release();
3594 autoCaller.release();
3595 treeLock.release();
3596 hrc = m->pVirtualBox->i_findMachine(id, false, true, &aMachine);
3597 treeLock.acquire();
3598 autoCaller.add();
3599 AssertComRCThrowRC(autoCaller.hrc());
3600 alock.acquire();
3601
3602 if (SUCCEEDED(hrc))
3603 {
3604 ComObjPtr<SessionMachine> sm;
3605 ComPtr<IInternalSessionControl> ctl;
3606
3607 alock.release();
3608 autoCaller.release();
3609 treeLock.release();
3610 bool ses = aMachine->i_isSessionOpenVM(sm, &ctl);
3611 treeLock.acquire();
3612 autoCaller.add();
3613 AssertComRCThrowRC(autoCaller.hrc());
3614 alock.acquire();
3615
3616 if (ses)
3617 throw setError(VBOX_E_INVALID_VM_STATE,
3618 tr("At least the VM '%s' to whom this medium '%s' attached has currently an opened session. Stop all VMs before relocating this medium"),
3619 id.toString().c_str(), i_getLocationFull().c_str());
3620 }
3621 ++currMachineID;
3622 }
3623
3624 /* Build the source lock list. */
3625 MediumLockList *pMediumLockList(new MediumLockList());
3626 alock.release();
3627 autoCaller.release();
3628 treeLock.release();
3629 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
3630 this /* pToLockWrite */,
3631 true /* fMediumLockWriteAll */,
3632 NULL,
3633 *pMediumLockList);
3634 treeLock.acquire();
3635 autoCaller.add();
3636 AssertComRCThrowRC(autoCaller.hrc());
3637 alock.acquire();
3638 if (FAILED(hrc))
3639 {
3640 delete pMediumLockList;
3641 throw setError(hrc, tr("Failed to create medium lock list for '%s'"), i_getLocationFull().c_str());
3642 }
3643 alock.release();
3644 autoCaller.release();
3645 treeLock.release();
3646 hrc = pMediumLockList->Lock();
3647 treeLock.acquire();
3648 autoCaller.add();
3649 AssertComRCThrowRC(autoCaller.hrc());
3650 alock.acquire();
3651 if (FAILED(hrc))
3652 {
3653 delete pMediumLockList;
3654 throw setError(hrc, tr("Failed to lock media '%s'"), i_getLocationFull().c_str());
3655 }
3656
3657 pProgress.createObject();
3658 hrc = pProgress->init(m->pVirtualBox,
3659 static_cast <IMedium *>(this),
3660 BstrFmt(tr("Moving medium '%s'"), m->strLocationFull.c_str()).raw(),
3661 TRUE /* aCancelable */);
3662
3663 /* Do the disk moving. */
3664 if (SUCCEEDED(hrc))
3665 {
3666 ULONG mediumVariantFlags = i_getVariant();
3667
3668 /* setup task object to carry out the operation asynchronously */
3669 pTask = new Medium::MoveTask(this, pProgress,
3670 (MediumVariant_T)mediumVariantFlags,
3671 pMediumLockList);
3672 hrc = pTask->hrc();
3673 AssertComRC(hrc);
3674 if (FAILED(hrc))
3675 throw hrc;
3676 }
3677
3678 }
3679 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
3680
3681 if (SUCCEEDED(hrc))
3682 {
3683 hrc = pTask->createThread();
3684 pTask = NULL;
3685 if (SUCCEEDED(hrc))
3686 pProgress.queryInterfaceTo(aProgress.asOutParam());
3687 }
3688 else
3689 {
3690 if (pTask)
3691 delete pTask;
3692 }
3693
3694 return hrc;
3695}
3696
3697HRESULT Medium::setLocation(const com::Utf8Str &aLocation)
3698{
3699 HRESULT hrc = S_OK;
3700
3701 try
3702 {
3703 // locking: we need the tree lock first because we access parent pointers
3704 // and we need to write-lock the media involved
3705 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3706
3707 AutoCaller autoCaller(this);
3708 AssertComRCThrowRC(autoCaller.hrc());
3709
3710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3711
3712 Utf8Str destPath(aLocation);
3713
3714 // some check for file based medium
3715 if (i_isMediumFormatFile())
3716 {
3717 /* Path must be absolute */
3718 if (!RTPathStartsWithRoot(destPath.c_str()))
3719 throw setError(VBOX_E_FILE_ERROR, tr("The given path '%s' is not fully qualified"), destPath.c_str());
3720
3721 /* Simple check for existence */
3722 if (!RTFileExists(destPath.c_str()))
3723 throw setError(VBOX_E_FILE_ERROR,
3724 tr("The given path '%s' is not an existing file. New location is invalid."),
3725 destPath.c_str());
3726 }
3727
3728 /* Check VMs which have this medium attached to*/
3729 std::vector<com::Guid> aMachineIds;
3730 hrc = getMachineIds(aMachineIds);
3731
3732 // switch locks only if there are machines with this medium attached
3733 if (!aMachineIds.empty())
3734 {
3735 std::vector<com::Guid>::const_iterator currMachineID = aMachineIds.begin();
3736 std::vector<com::Guid>::const_iterator lastMachineID = aMachineIds.end();
3737
3738 alock.release();
3739 autoCaller.release();
3740 treeLock.release();
3741
3742 while (currMachineID != lastMachineID)
3743 {
3744 Guid id(*currMachineID);
3745 ComObjPtr<Machine> aMachine;
3746 hrc = m->pVirtualBox->i_findMachine(id, false, true, &aMachine);
3747 if (SUCCEEDED(hrc))
3748 {
3749 ComObjPtr<SessionMachine> sm;
3750 ComPtr<IInternalSessionControl> ctl;
3751
3752 bool ses = aMachine->i_isSessionOpenVM(sm, &ctl);
3753 if (ses)
3754 {
3755 treeLock.acquire();
3756 autoCaller.add();
3757 AssertComRCThrowRC(autoCaller.hrc());
3758 alock.acquire();
3759
3760 throw setError(VBOX_E_INVALID_VM_STATE,
3761 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"),
3762 id.toString().c_str(),
3763 i_getLocationFull().c_str());
3764 }
3765 }
3766 ++currMachineID;
3767 }
3768
3769 treeLock.acquire();
3770 autoCaller.add();
3771 AssertComRCThrowRC(autoCaller.hrc());
3772 alock.acquire();
3773 }
3774
3775 m->strLocationFull = destPath;
3776
3777 // save the settings
3778 alock.release();
3779 autoCaller.release();
3780 treeLock.release();
3781
3782 i_markRegistriesModified();
3783 m->pVirtualBox->i_saveModifiedRegistries();
3784
3785 MediumState_T mediumState;
3786 refreshState(autoCaller, &mediumState);
3787 m->pVirtualBox->i_onMediumConfigChanged(this);
3788 }
3789 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
3790
3791 return hrc;
3792}
3793
3794HRESULT Medium::compact(ComPtr<IProgress> &aProgress)
3795{
3796 HRESULT hrc = S_OK;
3797 ComObjPtr<Progress> pProgress;
3798 Medium::Task *pTask = NULL;
3799
3800 try
3801 {
3802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3803
3804 /* Build the medium lock list. */
3805 MediumLockList *pMediumLockList(new MediumLockList());
3806 alock.release();
3807 hrc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3808 this /* pToLockWrite */,
3809 false /* fMediumLockWriteAll */,
3810 NULL,
3811 *pMediumLockList);
3812 alock.acquire();
3813 if (FAILED(hrc))
3814 {
3815 delete pMediumLockList;
3816 throw hrc;
3817 }
3818
3819 alock.release();
3820 hrc = pMediumLockList->Lock();
3821 alock.acquire();
3822 if (FAILED(hrc))
3823 {
3824 delete pMediumLockList;
3825 throw setError(hrc,
3826 tr("Failed to lock media when compacting '%s'"),
3827 i_getLocationFull().c_str());
3828 }
3829
3830 pProgress.createObject();
3831 hrc = pProgress->init(m->pVirtualBox,
3832 static_cast <IMedium *>(this),
3833 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
3834 TRUE /* aCancelable */);
3835 if (FAILED(hrc))
3836 {
3837 delete pMediumLockList;
3838 throw hrc;
3839 }
3840
3841 /* setup task object to carry out the operation asynchronously */
3842 pTask = new Medium::CompactTask(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 return hrc;
3861}
3862
3863HRESULT Medium::resize(LONG64 aLogicalSize,
3864 ComPtr<IProgress> &aProgress)
3865{
3866 CheckComArgExpr(aLogicalSize, aLogicalSize > 0);
3867 HRESULT hrc = S_OK;
3868 ComObjPtr<Progress> pProgress;
3869
3870 /* Build the medium lock list. */
3871 MediumLockList *pMediumLockList(new MediumLockList());
3872
3873 try
3874 {
3875 const char *pszError = NULL;
3876
3877 hrc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3878 this /* pToLockWrite */,
3879 false /* fMediumLockWriteAll */,
3880 NULL,
3881 *pMediumLockList);
3882 if (FAILED(hrc))
3883 pszError = tr("Failed to create medium lock list when resizing '%s'");
3884 else
3885 {
3886 hrc = pMediumLockList->Lock();
3887 if (FAILED(hrc))
3888 pszError = tr("Failed to lock media when resizing '%s'");
3889 }
3890
3891
3892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3893
3894 if (FAILED(hrc))
3895 {
3896 throw setError(hrc, pszError, i_getLocationFull().c_str());
3897 }
3898
3899 pProgress.createObject();
3900 hrc = pProgress->init(m->pVirtualBox,
3901 static_cast <IMedium *>(this),
3902 BstrFmt(tr("Resizing medium '%s'"), m->strLocationFull.c_str()).raw(),
3903 TRUE /* aCancelable */);
3904 if (FAILED(hrc))
3905 {
3906 throw hrc;
3907 }
3908 }
3909 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
3910
3911 if (SUCCEEDED(hrc))
3912 hrc = i_resize((uint64_t)aLogicalSize, pMediumLockList, &pProgress, false /* aWait */, true /* aNotify */);
3913
3914 if (SUCCEEDED(hrc))
3915 pProgress.queryInterfaceTo(aProgress.asOutParam());
3916 else
3917 delete pMediumLockList;
3918
3919 return hrc;
3920}
3921
3922HRESULT Medium::reset(AutoCaller &autoCaller, ComPtr<IProgress> &aProgress)
3923{
3924 HRESULT hrc = S_OK;
3925 ComObjPtr<Progress> pProgress;
3926 Medium::Task *pTask = NULL;
3927
3928 try
3929 {
3930 autoCaller.release();
3931
3932 /* It is possible that some previous/concurrent uninit has already
3933 * cleared the pVirtualBox reference, see #uninit(). */
3934 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
3935
3936 /* i_canClose() needs the tree lock */
3937 AutoMultiWriteLock2 multilock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL,
3938 this->lockHandle()
3939 COMMA_LOCKVAL_SRC_POS);
3940
3941 autoCaller.add();
3942 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3943
3944 LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
3945
3946 if (m->pParent.isNull())
3947 throw setError(VBOX_E_NOT_SUPPORTED,
3948 tr("Medium type of '%s' is not differencing"),
3949 m->strLocationFull.c_str());
3950
3951 hrc = i_canClose();
3952 if (FAILED(hrc))
3953 throw hrc;
3954
3955 /* Build the medium lock list. */
3956 MediumLockList *pMediumLockList(new MediumLockList());
3957 multilock.release();
3958 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
3959 this /* pToLockWrite */,
3960 false /* fMediumLockWriteAll */,
3961 NULL,
3962 *pMediumLockList);
3963 multilock.acquire();
3964 if (FAILED(hrc))
3965 {
3966 delete pMediumLockList;
3967 throw hrc;
3968 }
3969
3970 multilock.release();
3971 hrc = pMediumLockList->Lock();
3972 multilock.acquire();
3973 if (FAILED(hrc))
3974 {
3975 delete pMediumLockList;
3976 throw setError(hrc,
3977 tr("Failed to lock media when resetting '%s'"),
3978 i_getLocationFull().c_str());
3979 }
3980
3981 pProgress.createObject();
3982 hrc = pProgress->init(m->pVirtualBox,
3983 static_cast<IMedium*>(this),
3984 BstrFmt(tr("Resetting differencing medium '%s'"), m->strLocationFull.c_str()).raw(),
3985 FALSE /* aCancelable */);
3986 if (FAILED(hrc))
3987 throw hrc;
3988
3989 /* setup task object to carry out the operation asynchronously */
3990 pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
3991 hrc = pTask->hrc();
3992 AssertComRC(hrc);
3993 if (FAILED(hrc))
3994 throw hrc;
3995 }
3996 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
3997
3998 if (SUCCEEDED(hrc))
3999 {
4000 hrc = pTask->createThread();
4001 pTask = NULL;
4002 if (SUCCEEDED(hrc))
4003 pProgress.queryInterfaceTo(aProgress.asOutParam());
4004 }
4005 else if (pTask != NULL)
4006 delete pTask;
4007
4008 LogFlowThisFunc(("LEAVE, hrc=%Rhrc\n", hrc));
4009
4010 return hrc;
4011}
4012
4013HRESULT Medium::changeEncryption(const com::Utf8Str &aCurrentPassword, const com::Utf8Str &aCipher,
4014 const com::Utf8Str &aNewPassword, const com::Utf8Str &aNewPasswordId,
4015 ComPtr<IProgress> &aProgress)
4016{
4017 HRESULT hrc = S_OK;
4018 ComObjPtr<Progress> pProgress;
4019 Medium::Task *pTask = NULL;
4020
4021 try
4022 {
4023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4024
4025 DeviceType_T devType = i_getDeviceType();
4026 /* Cannot encrypt DVD or floppy images so far. */
4027 if ( devType == DeviceType_DVD
4028 || devType == DeviceType_Floppy)
4029 return setError(VBOX_E_INVALID_OBJECT_STATE,
4030 tr("Cannot encrypt DVD or Floppy medium '%s'"),
4031 m->strLocationFull.c_str());
4032
4033 /* Cannot encrypt media which are attached to more than one virtual machine. */
4034 if (m->backRefs.size() > 1)
4035 return setError(VBOX_E_INVALID_OBJECT_STATE,
4036 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", m->backRefs.size()),
4037 m->strLocationFull.c_str(), m->backRefs.size());
4038
4039 if (i_getChildren().size() != 0)
4040 return setError(VBOX_E_INVALID_OBJECT_STATE,
4041 tr("Cannot encrypt medium '%s' because it has %d children", "", i_getChildren().size()),
4042 m->strLocationFull.c_str(), i_getChildren().size());
4043
4044 /* Build the medium lock list. */
4045 MediumLockList *pMediumLockList(new MediumLockList());
4046 alock.release();
4047 hrc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
4048 this /* pToLockWrite */,
4049 true /* fMediumLockAllWrite */,
4050 NULL,
4051 *pMediumLockList);
4052 alock.acquire();
4053 if (FAILED(hrc))
4054 {
4055 delete pMediumLockList;
4056 throw hrc;
4057 }
4058
4059 alock.release();
4060 hrc = pMediumLockList->Lock();
4061 alock.acquire();
4062 if (FAILED(hrc))
4063 {
4064 delete pMediumLockList;
4065 throw setError(hrc,
4066 tr("Failed to lock media for encryption '%s'"),
4067 i_getLocationFull().c_str());
4068 }
4069
4070 /*
4071 * Check all media in the chain to not contain any branches or references to
4072 * other virtual machines, we support encrypting only a list of differencing media at the moment.
4073 */
4074 MediumLockList::Base::const_iterator mediumListBegin = pMediumLockList->GetBegin();
4075 MediumLockList::Base::const_iterator mediumListEnd = pMediumLockList->GetEnd();
4076 for (MediumLockList::Base::const_iterator it = mediumListBegin;
4077 it != mediumListEnd;
4078 ++it)
4079 {
4080 const MediumLock &mediumLock = *it;
4081 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4082 AutoReadLock mediumReadLock(pMedium COMMA_LOCKVAL_SRC_POS);
4083
4084 Assert(pMedium->m->state == MediumState_LockedWrite);
4085
4086 if (pMedium->m->backRefs.size() > 1)
4087 {
4088 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
4089 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "",
4090 pMedium->m->backRefs.size()),
4091 pMedium->m->strLocationFull.c_str(), pMedium->m->backRefs.size());
4092 break;
4093 }
4094 else if (pMedium->i_getChildren().size() > 1)
4095 {
4096 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
4097 tr("Cannot encrypt medium '%s' because it has %d children", "", pMedium->i_getChildren().size()),
4098 pMedium->m->strLocationFull.c_str(), pMedium->i_getChildren().size());
4099 break;
4100 }
4101 }
4102
4103 if (FAILED(hrc))
4104 {
4105 delete pMediumLockList;
4106 throw hrc;
4107 }
4108
4109 const char *pszAction = tr("Encrypting medium");
4110 if ( aCurrentPassword.isNotEmpty()
4111 && aCipher.isEmpty())
4112 pszAction = tr("Decrypting medium");
4113
4114 pProgress.createObject();
4115 hrc = pProgress->init(m->pVirtualBox,
4116 static_cast <IMedium *>(this),
4117 BstrFmt("%s '%s'", pszAction, m->strLocationFull.c_str()).raw(),
4118 TRUE /* aCancelable */);
4119 if (FAILED(hrc))
4120 {
4121 delete pMediumLockList;
4122 throw hrc;
4123 }
4124
4125 /* setup task object to carry out the operation asynchronously */
4126 pTask = new Medium::EncryptTask(this, aNewPassword, aCurrentPassword,
4127 aCipher, aNewPasswordId, pProgress, pMediumLockList);
4128 hrc = pTask->hrc();
4129 AssertComRC(hrc);
4130 if (FAILED(hrc))
4131 throw hrc;
4132 }
4133 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
4134
4135 if (SUCCEEDED(hrc))
4136 {
4137 hrc = pTask->createThread();
4138 pTask = NULL;
4139 if (SUCCEEDED(hrc))
4140 pProgress.queryInterfaceTo(aProgress.asOutParam());
4141 }
4142 else if (pTask != NULL)
4143 delete pTask;
4144
4145 return hrc;
4146}
4147
4148HRESULT Medium::getEncryptionSettings(AutoCaller &autoCaller, com::Utf8Str &aCipher, com::Utf8Str &aPasswordId)
4149{
4150#ifndef VBOX_WITH_EXTPACK
4151 RT_NOREF(aCipher, aPasswordId);
4152#endif
4153 HRESULT hrc = S_OK;
4154
4155 try
4156 {
4157 autoCaller.release();
4158 ComObjPtr<Medium> pBase = i_getBase();
4159 autoCaller.add();
4160 if (FAILED(autoCaller.hrc()))
4161 throw hrc;
4162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4163
4164 /* Check whether encryption is configured for this medium. */
4165 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
4166 if (it == pBase->m->mapProperties.end())
4167 throw VBOX_E_NOT_SUPPORTED;
4168
4169# ifdef VBOX_WITH_EXTPACK
4170 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
4171 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
4172 {
4173 /* Load the plugin */
4174 Utf8Str strPlugin;
4175 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
4176 if (SUCCEEDED(hrc))
4177 {
4178 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
4179 if (RT_FAILURE(vrc))
4180 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
4181 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
4182 i_vdError(vrc).c_str());
4183 }
4184 else
4185 throw setError(VBOX_E_NOT_SUPPORTED,
4186 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
4187 ORACLE_PUEL_EXTPACK_NAME);
4188 }
4189 else
4190 throw setError(VBOX_E_NOT_SUPPORTED,
4191 tr("Encryption is not supported because the extension pack '%s' is missing"),
4192 ORACLE_PUEL_EXTPACK_NAME);
4193
4194 PVDISK pDisk = NULL;
4195 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
4196 ComAssertRCThrow(vrc, E_FAIL);
4197
4198 MediumCryptoFilterSettings CryptoSettings;
4199
4200 i_taskEncryptSettingsSetup(&CryptoSettings, NULL, it->second.c_str(), NULL, false /* fCreateKeyStore */);
4201 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ | VD_FILTER_FLAGS_INFO, CryptoSettings.vdFilterIfaces);
4202 if (RT_FAILURE(vrc))
4203 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
4204 tr("Failed to load the encryption filter: %s"),
4205 i_vdError(vrc).c_str());
4206
4207 it = pBase->m->mapProperties.find("CRYPT/KeyId");
4208 if (it == pBase->m->mapProperties.end())
4209 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4210 tr("Image is configured for encryption but doesn't has a KeyId set"));
4211
4212 aPasswordId = it->second.c_str();
4213 aCipher = CryptoSettings.pszCipherReturned;
4214 RTStrFree(CryptoSettings.pszCipherReturned);
4215
4216 VDDestroy(pDisk);
4217# else
4218 throw setError(VBOX_E_NOT_SUPPORTED,
4219 tr("Encryption is not supported because extension pack support is not built in"));
4220# endif
4221 }
4222 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
4223
4224 return hrc;
4225}
4226
4227HRESULT Medium::checkEncryptionPassword(const com::Utf8Str &aPassword)
4228{
4229 HRESULT hrc = S_OK;
4230
4231 try
4232 {
4233 ComObjPtr<Medium> pBase = i_getBase();
4234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4235
4236 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
4237 if (it == pBase->m->mapProperties.end())
4238 throw setError(VBOX_E_NOT_SUPPORTED,
4239 tr("The image is not configured for encryption"));
4240
4241 if (aPassword.isEmpty())
4242 throw setError(E_INVALIDARG,
4243 tr("The given password must not be empty"));
4244
4245# ifdef VBOX_WITH_EXTPACK
4246 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
4247 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
4248 {
4249 /* Load the plugin */
4250 Utf8Str strPlugin;
4251 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
4252 if (SUCCEEDED(hrc))
4253 {
4254 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
4255 if (RT_FAILURE(vrc))
4256 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
4257 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
4258 i_vdError(vrc).c_str());
4259 }
4260 else
4261 throw setError(VBOX_E_NOT_SUPPORTED,
4262 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
4263 ORACLE_PUEL_EXTPACK_NAME);
4264 }
4265 else
4266 throw setError(VBOX_E_NOT_SUPPORTED,
4267 tr("Encryption is not supported because the extension pack '%s' is missing"),
4268 ORACLE_PUEL_EXTPACK_NAME);
4269
4270 PVDISK pDisk = NULL;
4271 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
4272 ComAssertRCThrow(vrc, E_FAIL);
4273
4274 MediumCryptoFilterSettings CryptoSettings;
4275
4276 i_taskEncryptSettingsSetup(&CryptoSettings, NULL, it->second.c_str(), aPassword.c_str(),
4277 false /* fCreateKeyStore */);
4278 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettings.vdFilterIfaces);
4279 if (vrc == VERR_VD_PASSWORD_INCORRECT)
4280 throw setError(VBOX_E_PASSWORD_INCORRECT,
4281 tr("The given password is incorrect"));
4282 else if (RT_FAILURE(vrc))
4283 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
4284 tr("Failed to load the encryption filter: %s"),
4285 i_vdError(vrc).c_str());
4286
4287 VDDestroy(pDisk);
4288# else
4289 throw setError(VBOX_E_NOT_SUPPORTED,
4290 tr("Encryption is not supported because extension pack support is not built in"));
4291# endif
4292 }
4293 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
4294
4295 return hrc;
4296}
4297
4298HRESULT Medium::openForIO(BOOL aWritable, com::Utf8Str const &aPassword, ComPtr<IMediumIO> &aMediumIO)
4299{
4300 /*
4301 * Input validation.
4302 */
4303 if (aWritable && i_isReadOnly())
4304 return setError(E_ACCESSDENIED, tr("Write access denied: read-only"));
4305
4306 com::Utf8Str const strKeyId = i_getKeyId();
4307 if (strKeyId.isEmpty() && aPassword.isNotEmpty())
4308 return setError(E_INVALIDARG, tr("Password given for unencrypted medium"));
4309 if (strKeyId.isNotEmpty() && aPassword.isEmpty())
4310 return setError(E_INVALIDARG, tr("Password needed for encrypted medium"));
4311
4312 /*
4313 * Create IO object and return it.
4314 */
4315 ComObjPtr<MediumIO> ptrIO;
4316 HRESULT hrc = ptrIO.createObject();
4317 if (SUCCEEDED(hrc))
4318 {
4319 hrc = ptrIO->initForMedium(this, m->pVirtualBox, aWritable != FALSE, strKeyId, aPassword);
4320 if (SUCCEEDED(hrc))
4321 ptrIO.queryInterfaceTo(aMediumIO.asOutParam());
4322 }
4323 return hrc;
4324}
4325
4326
4327////////////////////////////////////////////////////////////////////////////////
4328//
4329// Medium public internal methods
4330//
4331////////////////////////////////////////////////////////////////////////////////
4332
4333/**
4334 * Internal method to return the medium's parent medium. Must have caller + locking!
4335 * @return
4336 */
4337const ComObjPtr<Medium>& Medium::i_getParent() const
4338{
4339 return m->pParent;
4340}
4341
4342/**
4343 * Internal method to return the medium's list of child media. Must have caller + locking!
4344 * @return
4345 */
4346const MediaList& Medium::i_getChildren() const
4347{
4348 return m->llChildren;
4349}
4350
4351/**
4352 * Internal method to return the medium's GUID. Must have caller + locking!
4353 * @return
4354 */
4355const Guid& Medium::i_getId() const
4356{
4357 return m->id;
4358}
4359
4360/**
4361 * Internal method to return the medium's state. Must have caller + locking!
4362 * @return
4363 */
4364MediumState_T Medium::i_getState() const
4365{
4366 return m->state;
4367}
4368
4369/**
4370 * Internal method to return the medium's variant. Must have caller + locking!
4371 * @return
4372 */
4373MediumVariant_T Medium::i_getVariant() const
4374{
4375 return m->variant;
4376}
4377
4378/**
4379 * Internal method which returns true if this medium represents a host drive.
4380 * @return
4381 */
4382bool Medium::i_isHostDrive() const
4383{
4384 return m->hostDrive;
4385}
4386
4387/**
4388 * Internal method which returns true if this medium is in the process of being closed.
4389 * @return
4390 */
4391bool Medium::i_isClosing() const
4392{
4393 return m->fClosing;
4394}
4395
4396/**
4397 * Internal method to return the medium's full location. Must have caller + locking!
4398 * @return
4399 */
4400const Utf8Str& Medium::i_getLocationFull() const
4401{
4402 return m->strLocationFull;
4403}
4404
4405/**
4406 * Internal method to return the medium's format string. Must have caller + locking!
4407 * @return
4408 */
4409const Utf8Str& Medium::i_getFormat() const
4410{
4411 return m->strFormat;
4412}
4413
4414/**
4415 * Internal method to return the medium's format object. Must have caller + locking!
4416 * @return
4417 */
4418const ComObjPtr<MediumFormat>& Medium::i_getMediumFormat() const
4419{
4420 return m->formatObj;
4421}
4422
4423/**
4424 * Internal method that returns true if the medium is represented by a file on the host disk
4425 * (and not iSCSI or something).
4426 * @return
4427 */
4428bool Medium::i_isMediumFormatFile() const
4429{
4430 if ( m->formatObj
4431 && (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
4432 )
4433 return true;
4434 return false;
4435}
4436
4437/**
4438 * Internal method to return the medium's size. Must have caller + locking!
4439 * @return
4440 */
4441uint64_t Medium::i_getSize() const
4442{
4443 return m->size;
4444}
4445
4446/**
4447 * Internal method to return the medium's size. Must have caller + locking!
4448 * @return
4449 */
4450uint64_t Medium::i_getLogicalSize() const
4451{
4452 return m->logicalSize;
4453}
4454
4455/**
4456 * Returns the medium device type. Must have caller + locking!
4457 * @return
4458 */
4459DeviceType_T Medium::i_getDeviceType() const
4460{
4461 return m->devType;
4462}
4463
4464/**
4465 * Returns the medium type. Must have caller + locking!
4466 * @return
4467 */
4468MediumType_T Medium::i_getType() const
4469{
4470 return m->type;
4471}
4472
4473/**
4474 * Returns a short version of the location attribute.
4475 *
4476 * @note Must be called from under this object's read or write lock.
4477 */
4478Utf8Str Medium::i_getName()
4479{
4480 Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
4481 return name;
4482}
4483
4484/**
4485 * Same as i_addRegistry() except that we don't check the object state, making
4486 * it safe to call with initFromSettings() on the call stack.
4487 */
4488bool Medium::i_addRegistryNoCallerCheck(const Guid &id)
4489{
4490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4491
4492 bool fAdd = true;
4493
4494 // hard disks cannot be in more than one registry
4495 if ( m->devType == DeviceType_HardDisk
4496 && m->llRegistryIDs.size() > 0)
4497 fAdd = false;
4498
4499 // no need to add the UUID twice
4500 if (fAdd)
4501 {
4502 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
4503 it != m->llRegistryIDs.end();
4504 ++it)
4505 {
4506 if ((*it) == id)
4507 {
4508 fAdd = false;
4509 break;
4510 }
4511 }
4512 }
4513
4514 if (fAdd)
4515 m->llRegistryIDs.push_back(id);
4516
4517 return fAdd;
4518}
4519
4520/**
4521 * This adds the given UUID to the list of media registries in which this
4522 * medium should be registered. The UUID can either be a machine UUID,
4523 * to add a machine registry, or the global registry UUID as returned by
4524 * VirtualBox::getGlobalRegistryId().
4525 *
4526 * Note that for hard disks, this method does nothing if the medium is
4527 * already in another registry to avoid having hard disks in more than
4528 * one registry, which causes trouble with keeping diff images in sync.
4529 * See getFirstRegistryMachineId() for details.
4530 *
4531 * @param id
4532 * @return true if the registry was added; false if the given id was already on the list.
4533 */
4534bool Medium::i_addRegistry(const Guid &id)
4535{
4536 AutoCaller autoCaller(this);
4537 if (FAILED(autoCaller.hrc()))
4538 return false;
4539 return i_addRegistryNoCallerCheck(id);
4540}
4541
4542/**
4543 * This adds the given UUID to the list of media registries in which this
4544 * medium should be registered. The UUID can either be a machine UUID,
4545 * to add a machine registry, or the global registry UUID as returned by
4546 * VirtualBox::getGlobalRegistryId(). Thisis applied to all children.
4547 *
4548 * Note that for hard disks, this method does nothing if the medium is
4549 * already in another registry to avoid having hard disks in more than
4550 * one registry, which causes trouble with keeping diff images in sync.
4551 * See getFirstRegistryMachineId() for details.
4552 *
4553 * @note the caller must hold the media tree lock for reading.
4554 *
4555 * @param id
4556 * @return true if the registry was added; false if the given id was already on the list.
4557 */
4558bool Medium::i_addRegistryAll(const Guid &id)
4559{
4560 MediaList llMediaTodo;
4561 llMediaTodo.push_back(this);
4562
4563 bool fAdd = false;
4564
4565 while (!llMediaTodo.empty())
4566 {
4567 ComObjPtr<Medium> pMedium = llMediaTodo.front();
4568 llMediaTodo.pop_front();
4569
4570 AutoCaller mediumCaller(pMedium);
4571 if (FAILED(mediumCaller.hrc())) continue;
4572
4573 fAdd |= pMedium->i_addRegistryNoCallerCheck(id);
4574
4575 // protected by the medium tree lock held by our original caller
4576 MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
4577 MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
4578 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
4579 llMediaTodo.push_back(*it);
4580 }
4581
4582 return fAdd;
4583}
4584
4585/**
4586 * Removes the given UUID from the list of media registry UUIDs of this medium.
4587 *
4588 * @param id
4589 * @return true if the UUID was found or false if not.
4590 */
4591bool Medium::i_removeRegistry(const Guid &id)
4592{
4593 AutoCaller autoCaller(this);
4594 if (FAILED(autoCaller.hrc()))
4595 return false;
4596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4597
4598 bool fRemove = false;
4599
4600 /// @todo r=klaus eliminate this code, replace it by using find.
4601 for (GuidList::iterator it = m->llRegistryIDs.begin();
4602 it != m->llRegistryIDs.end();
4603 ++it)
4604 {
4605 if ((*it) == id)
4606 {
4607 // getting away with this as the iterator isn't used after
4608 m->llRegistryIDs.erase(it);
4609 fRemove = true;
4610 break;
4611 }
4612 }
4613
4614 return fRemove;
4615}
4616
4617/**
4618 * Removes the given UUID from the list of media registry UUIDs, for this
4619 * medium and all its children.
4620 *
4621 * @note the caller must hold the media tree lock for reading.
4622 *
4623 * @param id
4624 * @return true if the UUID was found or false if not.
4625 */
4626bool Medium::i_removeRegistryAll(const Guid &id)
4627{
4628 MediaList llMediaTodo;
4629 llMediaTodo.push_back(this);
4630
4631 bool fRemove = false;
4632
4633 while (!llMediaTodo.empty())
4634 {
4635 ComObjPtr<Medium> pMedium = llMediaTodo.front();
4636 llMediaTodo.pop_front();
4637
4638 AutoCaller mediumCaller(pMedium);
4639 if (FAILED(mediumCaller.hrc())) continue;
4640
4641 fRemove |= pMedium->i_removeRegistry(id);
4642
4643 // protected by the medium tree lock held by our original caller
4644 MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
4645 MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
4646 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
4647 llMediaTodo.push_back(*it);
4648 }
4649
4650 return fRemove;
4651}
4652
4653/**
4654 * Returns true if id is in the list of media registries for this medium.
4655 *
4656 * Must have caller + read locking!
4657 *
4658 * @param id
4659 * @return
4660 */
4661bool Medium::i_isInRegistry(const Guid &id)
4662{
4663 /// @todo r=klaus eliminate this code, replace it by using find.
4664 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
4665 it != m->llRegistryIDs.end();
4666 ++it)
4667 {
4668 if (*it == id)
4669 return true;
4670 }
4671
4672 return false;
4673}
4674
4675/**
4676 * Internal method to return the medium's first registry machine (i.e. the machine in whose
4677 * machine XML this medium is listed).
4678 *
4679 * Every attached medium must now (4.0) reside in at least one media registry, which is identified
4680 * by a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
4681 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
4682 * object if the machine is old and still needs the global registry in VirtualBox.xml.
4683 *
4684 * By definition, hard disks may only be in one media registry, in which all its children
4685 * will be stored as well. Otherwise we run into problems with having keep multiple registries
4686 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
4687 * case, only VM2's registry is used for the disk in question.)
4688 *
4689 * If there is no medium registry, particularly if the medium has not been attached yet, this
4690 * does not modify uuid and returns false.
4691 *
4692 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
4693 * the user.
4694 *
4695 * Must have caller + locking!
4696 *
4697 * @param uuid Receives first registry machine UUID, if available.
4698 * @return true if uuid was set.
4699 */
4700bool Medium::i_getFirstRegistryMachineId(Guid &uuid) const
4701{
4702 if (m->llRegistryIDs.size())
4703 {
4704 uuid = m->llRegistryIDs.front();
4705 return true;
4706 }
4707 return false;
4708}
4709
4710/**
4711 * Marks all the registries in which this medium is registered as modified.
4712 */
4713void Medium::i_markRegistriesModified()
4714{
4715 AutoCaller autoCaller(this);
4716 if (FAILED(autoCaller.hrc())) return;
4717
4718 // Get local copy, as keeping the lock over VirtualBox::markRegistryModified
4719 // causes trouble with the lock order
4720 GuidList llRegistryIDs;
4721 {
4722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4723 llRegistryIDs = m->llRegistryIDs;
4724 }
4725
4726 autoCaller.release();
4727
4728 /* Save the error information now, the implicit restore when this goes
4729 * out of scope will throw away spurious additional errors created below. */
4730 ErrorInfoKeeper eik;
4731 for (GuidList::const_iterator it = llRegistryIDs.begin();
4732 it != llRegistryIDs.end();
4733 ++it)
4734 {
4735 m->pVirtualBox->i_markRegistryModified(*it);
4736 }
4737}
4738
4739/**
4740 * Adds the given machine and optionally the snapshot to the list of the objects
4741 * this medium is attached to.
4742 *
4743 * @param aMachineId Machine ID.
4744 * @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
4745 */
4746HRESULT Medium::i_addBackReference(const Guid &aMachineId,
4747 const Guid &aSnapshotId /*= Guid::Empty*/)
4748{
4749 AssertReturn(aMachineId.isValid(), E_FAIL);
4750
4751 LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
4752
4753 AutoCaller autoCaller(this);
4754 AssertComRCReturnRC(autoCaller.hrc());
4755
4756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4757
4758 switch (m->state)
4759 {
4760 case MediumState_Created:
4761 case MediumState_Inaccessible:
4762 case MediumState_LockedRead:
4763 case MediumState_LockedWrite:
4764 break;
4765
4766 default:
4767 return i_setStateError();
4768 }
4769
4770 if (m->numCreateDiffTasks > 0)
4771 return setError(VBOX_E_OBJECT_IN_USE,
4772 tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created", "",
4773 m->numCreateDiffTasks),
4774 m->strLocationFull.c_str(),
4775 m->id.raw(),
4776 m->numCreateDiffTasks);
4777
4778 BackRefList::iterator it = std::find_if(m->backRefs.begin(),
4779 m->backRefs.end(),
4780 BackRef::EqualsTo(aMachineId));
4781 if (it == m->backRefs.end())
4782 {
4783 BackRef ref(aMachineId, aSnapshotId);
4784 m->backRefs.push_back(ref);
4785
4786 return S_OK;
4787 }
4788 bool fDvd = false;
4789 {
4790 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
4791 /*
4792 * Check the medium is DVD and readonly. It's for the case if DVD
4793 * will be able to be writable sometime in the future.
4794 */
4795 fDvd = m->type == MediumType_Readonly && m->devType == DeviceType_DVD;
4796 }
4797
4798 // if the caller has not supplied a snapshot ID, then we're attaching
4799 // to a machine a medium which represents the machine's current state,
4800 // so set the flag
4801
4802 if (aSnapshotId.isZero())
4803 {
4804 // Allow DVD having MediumType_Readonly to be attached twice.
4805 // (the medium already had been added to back reference)
4806 if (fDvd)
4807 {
4808 it->iRefCnt++;
4809 return S_OK;
4810 }
4811
4812 /* sanity: no duplicate attachments */
4813 if (it->fInCurState)
4814 return setError(VBOX_E_OBJECT_IN_USE,
4815 tr("Cannot attach medium '%s' {%RTuuid}: medium is already associated with the current state of machine uuid {%RTuuid}!"),
4816 m->strLocationFull.c_str(),
4817 m->id.raw(),
4818 aMachineId.raw());
4819 it->fInCurState = true;
4820
4821 return S_OK;
4822 }
4823
4824 // otherwise: a snapshot medium is being attached
4825
4826 /* sanity: no duplicate attachments */
4827 for (std::list<SnapshotRef>::iterator jt = it->llSnapshotIds.begin();
4828 jt != it->llSnapshotIds.end();
4829 ++jt)
4830 {
4831 const Guid &idOldSnapshot = jt->snapshotId;
4832
4833 if (idOldSnapshot == aSnapshotId)
4834 {
4835 if (fDvd)
4836 {
4837 jt->iRefCnt++;
4838 return S_OK;
4839 }
4840#ifdef DEBUG
4841 i_dumpBackRefs();
4842#endif
4843 return setError(VBOX_E_OBJECT_IN_USE,
4844 tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
4845 m->strLocationFull.c_str(),
4846 m->id.raw(),
4847 aSnapshotId.raw());
4848 }
4849 }
4850
4851 it->llSnapshotIds.push_back(SnapshotRef(aSnapshotId));
4852 // Do not touch fInCurState, as the image may be attached to the current
4853 // state *and* a snapshot, otherwise we lose the current state association!
4854
4855 LogFlowThisFuncLeave();
4856
4857 return S_OK;
4858}
4859
4860/**
4861 * Removes the given machine and optionally the snapshot from the list of the
4862 * objects this medium is attached to.
4863 *
4864 * @param aMachineId Machine ID.
4865 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
4866 * attachment.
4867 */
4868HRESULT Medium::i_removeBackReference(const Guid &aMachineId,
4869 const Guid &aSnapshotId /*= Guid::Empty*/)
4870{
4871 AssertReturn(aMachineId.isValid(), E_FAIL);
4872
4873 AutoCaller autoCaller(this);
4874 AssertComRCReturnRC(autoCaller.hrc());
4875
4876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4877
4878 BackRefList::iterator it =
4879 std::find_if(m->backRefs.begin(), m->backRefs.end(),
4880 BackRef::EqualsTo(aMachineId));
4881 AssertReturn(it != m->backRefs.end(), E_FAIL);
4882
4883 if (aSnapshotId.isZero())
4884 {
4885 it->iRefCnt--;
4886 if (it->iRefCnt > 0)
4887 return S_OK;
4888
4889 /* remove the current state attachment */
4890 it->fInCurState = false;
4891 }
4892 else
4893 {
4894 /* remove the snapshot attachment */
4895 std::list<SnapshotRef>::iterator jt =
4896 std::find_if(it->llSnapshotIds.begin(),
4897 it->llSnapshotIds.end(),
4898 SnapshotRef::EqualsTo(aSnapshotId));
4899
4900 AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
4901
4902 jt->iRefCnt--;
4903 if (jt->iRefCnt > 0)
4904 return S_OK;
4905
4906 it->llSnapshotIds.erase(jt);
4907 }
4908
4909 /* if the backref becomes empty, remove it */
4910 if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
4911 m->backRefs.erase(it);
4912
4913 return S_OK;
4914}
4915
4916/**
4917 * Internal method to return the medium's list of backrefs. Must have caller + locking!
4918 * @return
4919 */
4920const Guid* Medium::i_getFirstMachineBackrefId() const
4921{
4922 if (!m->backRefs.size())
4923 return NULL;
4924
4925 return &m->backRefs.front().machineId;
4926}
4927
4928/**
4929 * Internal method which returns a machine that either this medium or one of its children
4930 * is attached to. This is used for finding a replacement media registry when an existing
4931 * media registry is about to be deleted in VirtualBox::unregisterMachine().
4932 *
4933 * Must have caller + locking, *and* caller must hold the media tree lock!
4934 * @param aId Id to ignore when looking for backrefs.
4935 * @return
4936 */
4937const Guid* Medium::i_getAnyMachineBackref(const Guid &aId) const
4938{
4939 std::list<const Medium *> llMediaTodo;
4940 llMediaTodo.push_back(this);
4941
4942 while (!llMediaTodo.empty())
4943 {
4944 const Medium *pMedium = llMediaTodo.front();
4945 llMediaTodo.pop_front();
4946
4947 if (pMedium->m->backRefs.size())
4948 {
4949 if (pMedium->m->backRefs.front().machineId != aId)
4950 return &pMedium->m->backRefs.front().machineId;
4951 if (pMedium->m->backRefs.size() > 1)
4952 {
4953 BackRefList::const_iterator it = pMedium->m->backRefs.begin();
4954 ++it;
4955 return &it->machineId;
4956 }
4957 }
4958
4959 MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
4960 MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
4961 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
4962 llMediaTodo.push_back(*it);
4963 }
4964
4965 return NULL;
4966}
4967
4968const Guid* Medium::i_getFirstMachineBackrefSnapshotId() const
4969{
4970 if (!m->backRefs.size())
4971 return NULL;
4972
4973 const BackRef &ref = m->backRefs.front();
4974 if (ref.llSnapshotIds.empty())
4975 return NULL;
4976
4977 return &ref.llSnapshotIds.front().snapshotId;
4978}
4979
4980size_t Medium::i_getMachineBackRefCount() const
4981{
4982 return m->backRefs.size();
4983}
4984
4985#ifdef DEBUG
4986/**
4987 * Debugging helper that gets called after VirtualBox initialization that writes all
4988 * machine backreferences to the debug log.
4989 */
4990void Medium::i_dumpBackRefs()
4991{
4992 AutoCaller autoCaller(this);
4993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4994
4995 LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
4996
4997 for (BackRefList::iterator it2 = m->backRefs.begin();
4998 it2 != m->backRefs.end();
4999 ++it2)
5000 {
5001 const BackRef &ref = *it2;
5002 LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d, iRefCnt: %d)\n", ref.machineId.raw(), ref.fInCurState, ref.iRefCnt));
5003
5004 for (std::list<SnapshotRef>::const_iterator jt2 = it2->llSnapshotIds.begin();
5005 jt2 != it2->llSnapshotIds.end();
5006 ++jt2)
5007 {
5008 const Guid &id = jt2->snapshotId;
5009 LogFlowThisFunc((" Backref from snapshot {%RTuuid} (iRefCnt = %d)\n", id.raw(), jt2->iRefCnt));
5010 }
5011 }
5012}
5013#endif
5014
5015/**
5016 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
5017 * of this media and updates it if necessary to reflect the new location.
5018 *
5019 * @param strOldPath Old path (full).
5020 * @param strNewPath New path (full).
5021 *
5022 * @note Locks this object for writing.
5023 */
5024HRESULT Medium::i_updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
5025{
5026 AssertReturn(!strOldPath.isEmpty(), E_FAIL);
5027 AssertReturn(!strNewPath.isEmpty(), E_FAIL);
5028
5029 AutoCaller autoCaller(this);
5030 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5031
5032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5033
5034 LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
5035
5036 const char *pcszMediumPath = m->strLocationFull.c_str();
5037
5038 if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
5039 {
5040 Utf8Str newPath(strNewPath);
5041 newPath.append(pcszMediumPath + strOldPath.length());
5042 unconst(m->strLocationFull) = newPath;
5043
5044 m->pVirtualBox->i_onMediumConfigChanged(this);
5045
5046 LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
5047 // we changed something
5048 return S_OK;
5049 }
5050
5051 // no change was necessary, signal error which the caller needs to interpret
5052 return VBOX_E_FILE_ERROR;
5053}
5054
5055/**
5056 * Returns the base medium of the media chain this medium is part of.
5057 *
5058 * The base medium is found by walking up the parent-child relationship axis.
5059 * If the medium doesn't have a parent (i.e. it's a base medium), it
5060 * returns itself in response to this method.
5061 *
5062 * @param aLevel Where to store the number of ancestors of this medium
5063 * (zero for the base), may be @c NULL.
5064 *
5065 * @note Locks medium tree for reading.
5066 */
5067ComObjPtr<Medium> Medium::i_getBase(uint32_t *aLevel /*= NULL*/)
5068{
5069 ComObjPtr<Medium> pBase;
5070
5071 /* it is possible that some previous/concurrent uninit has already cleared
5072 * the pVirtualBox reference, and in this case we don't need to continue */
5073 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
5074 if (!pVirtualBox)
5075 return pBase;
5076
5077 /* we access m->pParent */
5078 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5079
5080 AutoCaller autoCaller(this);
5081 AssertReturn(autoCaller.isOk(), pBase);
5082
5083 pBase = this;
5084 uint32_t level = 0;
5085
5086 if (m->pParent)
5087 {
5088 for (;;)
5089 {
5090 AutoCaller baseCaller(pBase);
5091 AssertReturn(baseCaller.isOk(), pBase);
5092
5093 if (pBase->m->pParent.isNull())
5094 break;
5095
5096 pBase = pBase->m->pParent;
5097 ++level;
5098 }
5099 }
5100
5101 if (aLevel != NULL)
5102 *aLevel = level;
5103
5104 return pBase;
5105}
5106
5107/**
5108 * Returns the depth of this medium in the media chain.
5109 *
5110 * @note Locks medium tree for reading.
5111 */
5112uint32_t Medium::i_getDepth()
5113{
5114 /* it is possible that some previous/concurrent uninit has already cleared
5115 * the pVirtualBox reference, and in this case we don't need to continue */
5116 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
5117 if (!pVirtualBox)
5118 return 1;
5119
5120 /* we access m->pParent */
5121 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5122
5123 uint32_t cDepth = 0;
5124 ComObjPtr<Medium> pMedium(this);
5125 while (!pMedium.isNull())
5126 {
5127 AutoCaller autoCaller(this);
5128 AssertReturn(autoCaller.isOk(), cDepth + 1);
5129
5130 pMedium = pMedium->m->pParent;
5131 cDepth++;
5132 }
5133
5134 return cDepth;
5135}
5136
5137/**
5138 * Returns @c true if this medium cannot be modified because it has
5139 * dependents (children) or is part of the snapshot. Related to the medium
5140 * type and posterity, not to the current media state.
5141 *
5142 * @note Locks this object and medium tree for reading.
5143 */
5144bool Medium::i_isReadOnly()
5145{
5146 /* it is possible that some previous/concurrent uninit has already cleared
5147 * the pVirtualBox reference, and in this case we don't need to continue */
5148 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
5149 if (!pVirtualBox)
5150 return false;
5151
5152 /* we access children */
5153 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5154
5155 AutoCaller autoCaller(this);
5156 AssertComRCReturn(autoCaller.hrc(), false);
5157
5158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5159
5160 switch (m->type)
5161 {
5162 case MediumType_Normal:
5163 {
5164 if (i_getChildren().size() != 0)
5165 return true;
5166
5167 for (BackRefList::const_iterator it = m->backRefs.begin();
5168 it != m->backRefs.end(); ++it)
5169 if (it->llSnapshotIds.size() != 0)
5170 return true;
5171
5172 if (m->variant & MediumVariant_VmdkStreamOptimized)
5173 return true;
5174
5175 return false;
5176 }
5177 case MediumType_Immutable:
5178 case MediumType_MultiAttach:
5179 return true;
5180 case MediumType_Writethrough:
5181 case MediumType_Shareable:
5182 case MediumType_Readonly: /* explicit readonly media has no diffs */
5183 return false;
5184 default:
5185 break;
5186 }
5187
5188 AssertFailedReturn(false);
5189}
5190
5191/**
5192 * Internal method to update the medium's id. Must have caller + locking!
5193 * @return
5194 */
5195void Medium::i_updateId(const Guid &id)
5196{
5197 unconst(m->id) = id;
5198}
5199
5200/**
5201 * Saves the settings of one medium.
5202 *
5203 * @note Caller MUST take care of the medium tree lock and caller.
5204 *
5205 * @param data Settings struct to be updated.
5206 * @param strHardDiskFolder Folder for which paths should be relative.
5207 */
5208void Medium::i_saveSettingsOne(settings::Medium &data, const Utf8Str &strHardDiskFolder)
5209{
5210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5211
5212 data.uuid = m->id;
5213
5214 // make path relative if needed
5215 if ( !strHardDiskFolder.isEmpty()
5216 && RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
5217 )
5218 data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
5219 else
5220 data.strLocation = m->strLocationFull;
5221 data.strFormat = m->strFormat;
5222
5223 /* optional, only for diffs, default is false */
5224 if (m->pParent)
5225 data.fAutoReset = m->autoReset;
5226 else
5227 data.fAutoReset = false;
5228
5229 /* optional */
5230 data.strDescription = m->strDescription;
5231
5232 /* optional properties */
5233 data.properties.clear();
5234
5235 /* handle iSCSI initiator secrets transparently */
5236 bool fHaveInitiatorSecretEncrypted = false;
5237 Utf8Str strCiphertext;
5238 settings::StringsMap::const_iterator itPln = m->mapProperties.find("InitiatorSecret");
5239 if ( itPln != m->mapProperties.end()
5240 && !itPln->second.isEmpty())
5241 {
5242 /* Encrypt the plain secret. If that does not work (i.e. no or wrong settings key
5243 * specified), just use the encrypted secret (if there is any). */
5244 int vrc = m->pVirtualBox->i_encryptSetting(itPln->second, &strCiphertext);
5245 if (RT_SUCCESS(vrc))
5246 fHaveInitiatorSecretEncrypted = true;
5247 }
5248 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
5249 it != m->mapProperties.end();
5250 ++it)
5251 {
5252 /* only save properties that have non-default values */
5253 if (!it->second.isEmpty())
5254 {
5255 const Utf8Str &name = it->first;
5256 const Utf8Str &value = it->second;
5257 bool fCreateOnly = false;
5258 for (MediumFormat::PropertyArray::const_iterator itf = m->formatObj->i_getProperties().begin();
5259 itf != m->formatObj->i_getProperties().end();
5260 ++itf)
5261 {
5262 if ( itf->strName.equals(name)
5263 && (itf->flags & VD_CFGKEY_CREATEONLY))
5264 {
5265 fCreateOnly = true;
5266 break;
5267 }
5268 }
5269 if (!fCreateOnly)
5270 /* do NOT store the plain InitiatorSecret */
5271 if ( !fHaveInitiatorSecretEncrypted
5272 || !name.equals("InitiatorSecret"))
5273 data.properties[name] = value;
5274 }
5275 }
5276 if (fHaveInitiatorSecretEncrypted)
5277 data.properties["InitiatorSecretEncrypted"] = strCiphertext;
5278
5279 /* only for base media */
5280 if (m->pParent.isNull())
5281 data.hdType = m->type;
5282}
5283
5284/**
5285 * Saves medium data by putting it into the provided data structure.
5286 * The settings of all children is saved, too.
5287 *
5288 * @param data Settings struct to be updated.
5289 * @param strHardDiskFolder Folder for which paths should be relative.
5290 *
5291 * @note Locks this object, medium tree and children for reading.
5292 */
5293HRESULT Medium::i_saveSettings(settings::Medium &data,
5294 const Utf8Str &strHardDiskFolder)
5295{
5296 /* we access m->pParent */
5297 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5298
5299 AutoCaller autoCaller(this);
5300 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5301
5302 MediaList llMediaTodo;
5303 llMediaTodo.push_back(this);
5304 std::list<settings::Medium *> llSettingsTodo;
5305 llSettingsTodo.push_back(&data);
5306
5307 while (!llMediaTodo.empty())
5308 {
5309 ComObjPtr<Medium> pMedium = llMediaTodo.front();
5310 llMediaTodo.pop_front();
5311 settings::Medium *current = llSettingsTodo.front();
5312 llSettingsTodo.pop_front();
5313
5314 AutoCaller mediumCaller(pMedium);
5315 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
5316
5317 pMedium->i_saveSettingsOne(*current, strHardDiskFolder);
5318
5319 /* save all children */
5320 MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
5321 MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
5322 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
5323 {
5324 llMediaTodo.push_back(*it);
5325 current->llChildren.push_back(settings::Medium::Empty);
5326 llSettingsTodo.push_back(&current->llChildren.back());
5327 }
5328 }
5329
5330 return S_OK;
5331}
5332
5333/**
5334 * Constructs a medium lock list for this medium. The lock is not taken.
5335 *
5336 * @note Caller MUST NOT hold the media tree or medium lock.
5337 *
5338 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
5339 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
5340 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
5341 * @param pToLockWrite If not NULL, associate a write lock with this medium object.
5342 * @param fMediumLockWriteAll Whether to associate a write lock to all other media too.
5343 * @param pToBeParent Medium which will become the parent of this medium.
5344 * @param mediumLockList Where to store the resulting list.
5345 */
5346HRESULT Medium::i_createMediumLockList(bool fFailIfInaccessible,
5347 Medium *pToLockWrite,
5348 bool fMediumLockWriteAll,
5349 Medium *pToBeParent,
5350 MediumLockList &mediumLockList)
5351{
5352 /** @todo r=klaus this needs to be reworked, as the code below uses
5353 * i_getParent without holding the tree lock, and changing this is
5354 * a significant amount of effort. */
5355 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5356 Assert(!isWriteLockOnCurrentThread());
5357
5358 AutoCaller autoCaller(this);
5359 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5360
5361 HRESULT hrc = S_OK;
5362
5363 /* paranoid sanity checking if the medium has a to-be parent medium */
5364 if (pToBeParent)
5365 {
5366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5367 ComAssertRet(i_getParent().isNull(), E_FAIL);
5368 ComAssertRet(i_getChildren().size() == 0, E_FAIL);
5369 }
5370
5371 ErrorInfoKeeper eik;
5372 MultiResult mrc(S_OK);
5373
5374 ComObjPtr<Medium> pMedium = this;
5375 while (!pMedium.isNull())
5376 {
5377 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5378
5379 /* Accessibility check must be first, otherwise locking interferes
5380 * with getting the medium state. Lock lists are not created for
5381 * fun, and thus getting the medium status is no luxury. */
5382 MediumState_T mediumState = pMedium->i_getState();
5383 if (mediumState == MediumState_Inaccessible)
5384 {
5385 alock.release();
5386 hrc = pMedium->i_queryInfo(false /* fSetImageId */, false /* fSetParentId */, autoCaller);
5387 alock.acquire();
5388 if (FAILED(hrc)) return hrc;
5389
5390 mediumState = pMedium->i_getState();
5391 if (mediumState == MediumState_Inaccessible)
5392 {
5393 // ignore inaccessible ISO media and silently return S_OK,
5394 // otherwise VM startup (esp. restore) may fail without good reason
5395 if (!fFailIfInaccessible)
5396 return S_OK;
5397
5398 // otherwise report an error
5399 Bstr error;
5400 hrc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
5401 if (FAILED(hrc)) return hrc;
5402
5403 /* collect multiple errors */
5404 eik.restore();
5405 Assert(!error.isEmpty());
5406 mrc = setError(E_FAIL,
5407 "%ls",
5408 error.raw());
5409 // error message will be something like
5410 // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
5411 eik.fetch();
5412 }
5413 }
5414
5415 if (pMedium == pToLockWrite)
5416 mediumLockList.Prepend(pMedium, true);
5417 else
5418 mediumLockList.Prepend(pMedium, fMediumLockWriteAll);
5419
5420 pMedium = pMedium->i_getParent();
5421 if (pMedium.isNull() && pToBeParent)
5422 {
5423 pMedium = pToBeParent;
5424 pToBeParent = NULL;
5425 }
5426 }
5427
5428 return mrc;
5429}
5430
5431/**
5432 * Creates a new differencing storage unit using the format of the given target
5433 * medium and the location. Note that @c aTarget must be NotCreated.
5434 *
5435 * The @a aMediumLockList parameter contains the associated medium lock list,
5436 * which must be in locked state. If @a aWait is @c true then the caller is
5437 * responsible for unlocking.
5438 *
5439 * If @a aProgress is not NULL but the object it points to is @c null then a
5440 * new progress object will be created and assigned to @a *aProgress on
5441 * success, otherwise the existing progress object is used. If @a aProgress is
5442 * NULL, then no progress object is created/used at all.
5443 *
5444 * When @a aWait is @c false, this method will create a thread to perform the
5445 * create operation asynchronously and will return immediately. Otherwise, it
5446 * will perform the operation on the calling thread and will not return to the
5447 * caller until the operation is completed. Note that @a aProgress cannot be
5448 * NULL when @a aWait is @c false (this method will assert in this case).
5449 *
5450 * @param aTarget Target medium.
5451 * @param aVariant Precise medium variant to create.
5452 * @param aMediumLockList List of media which should be locked.
5453 * @param aProgress Where to find/store a Progress object to track
5454 * operation completion.
5455 * @param aWait @c true if this method should block instead of
5456 * creating an asynchronous thread.
5457 * @param aNotify Notify about media for which metadata is changed
5458 * during execution of the function.
5459 *
5460 * @note Locks this object and @a aTarget for writing.
5461 */
5462HRESULT Medium::i_createDiffStorage(ComObjPtr<Medium> &aTarget,
5463 MediumVariant_T aVariant,
5464 MediumLockList *aMediumLockList,
5465 ComObjPtr<Progress> *aProgress,
5466 bool aWait,
5467 bool aNotify)
5468{
5469 AssertReturn(!aTarget.isNull(), E_FAIL);
5470 AssertReturn(aMediumLockList, E_FAIL);
5471 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5472
5473 AutoCaller autoCaller(this);
5474 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5475
5476 AutoCaller targetCaller(aTarget);
5477 if (FAILED(targetCaller.hrc())) return targetCaller.hrc();
5478
5479 HRESULT hrc = S_OK;
5480 ComObjPtr<Progress> pProgress;
5481 Medium::Task *pTask = NULL;
5482
5483 try
5484 {
5485 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
5486
5487 ComAssertThrow( m->type != MediumType_Writethrough
5488 && m->type != MediumType_Shareable
5489 && m->type != MediumType_Readonly, E_FAIL);
5490 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
5491
5492 if (aTarget->m->state != MediumState_NotCreated)
5493 throw aTarget->i_setStateError();
5494
5495 /* Check that the medium is not attached to the current state of
5496 * any VM referring to it. */
5497 for (BackRefList::const_iterator it = m->backRefs.begin();
5498 it != m->backRefs.end();
5499 ++it)
5500 {
5501 if (it->fInCurState)
5502 {
5503 /* Note: when a VM snapshot is being taken, all normal media
5504 * attached to the VM in the current state will be, as an
5505 * exception, also associated with the snapshot which is about
5506 * to create (see SnapshotMachine::init()) before deassociating
5507 * them from the current state (which takes place only on
5508 * success in Machine::fixupHardDisks()), so that the size of
5509 * snapshotIds will be 1 in this case. The extra condition is
5510 * used to filter out this legal situation. */
5511 if (it->llSnapshotIds.size() == 0)
5512 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5513 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"),
5514 m->strLocationFull.c_str(), it->machineId.raw());
5515
5516 Assert(it->llSnapshotIds.size() == 1);
5517 }
5518 }
5519
5520 if (aProgress != NULL)
5521 {
5522 /* use the existing progress object... */
5523 pProgress = *aProgress;
5524
5525 /* ...but create a new one if it is null */
5526 if (pProgress.isNull())
5527 {
5528 pProgress.createObject();
5529 hrc = pProgress->init(m->pVirtualBox,
5530 static_cast<IMedium*>(this),
5531 BstrFmt(tr("Creating differencing medium storage unit '%s'"),
5532 aTarget->m->strLocationFull.c_str()).raw(),
5533 TRUE /* aCancelable */);
5534 if (FAILED(hrc))
5535 throw hrc;
5536 }
5537 }
5538
5539 /* setup task object to carry out the operation sync/async */
5540 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
5541 aMediumLockList,
5542 aWait /* fKeepMediumLockList */,
5543 aNotify);
5544 hrc = pTask->hrc();
5545 AssertComRC(hrc);
5546 if (FAILED(hrc))
5547 throw hrc;
5548
5549 /* register a task (it will deregister itself when done) */
5550 ++m->numCreateDiffTasks;
5551 Assert(m->numCreateDiffTasks != 0); /* overflow? */
5552
5553 aTarget->m->state = MediumState_Creating;
5554 }
5555 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
5556
5557 if (SUCCEEDED(hrc))
5558 {
5559 if (aWait)
5560 {
5561 hrc = pTask->runNow();
5562 delete pTask;
5563 }
5564 else
5565 hrc = pTask->createThread();
5566 pTask = NULL;
5567 if (SUCCEEDED(hrc) && aProgress != NULL)
5568 *aProgress = pProgress;
5569 }
5570 else if (pTask != NULL)
5571 delete pTask;
5572
5573 return hrc;
5574}
5575
5576/**
5577 * Returns a preferred format for differencing media.
5578 */
5579Utf8Str Medium::i_getPreferredDiffFormat()
5580{
5581 AutoCaller autoCaller(this);
5582 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
5583
5584 /* check that our own format supports diffs */
5585 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5586 {
5587 /* use the default format if not */
5588 Utf8Str tmp;
5589 m->pVirtualBox->i_getDefaultHardDiskFormat(tmp);
5590 return tmp;
5591 }
5592
5593 /* m->strFormat is const, no need to lock */
5594 return m->strFormat;
5595}
5596
5597/**
5598 * Returns a preferred variant for differencing media.
5599 */
5600MediumVariant_T Medium::i_getPreferredDiffVariant()
5601{
5602 AutoCaller autoCaller(this);
5603 AssertComRCReturn(autoCaller.hrc(), MediumVariant_Standard);
5604
5605 /* check that our own format supports diffs */
5606 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5607 return MediumVariant_Standard;
5608
5609 /* m->variant is const, no need to lock */
5610 ULONG mediumVariantFlags = (ULONG)m->variant;
5611 mediumVariantFlags &= ~(ULONG)(MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk);
5612 mediumVariantFlags |= MediumVariant_Diff;
5613 return (MediumVariant_T)mediumVariantFlags;
5614}
5615
5616/**
5617 * Implementation for the public Medium::Close() with the exception of calling
5618 * VirtualBox::saveRegistries(), in case someone wants to call this for several
5619 * media.
5620 *
5621 * After this returns with success, uninit() has been called on the medium, and
5622 * the object is no longer usable ("not ready" state).
5623 *
5624 * @param autoCaller AutoCaller instance which must have been created on the caller's
5625 * stack for this medium. This gets released hereupon
5626 * which the Medium instance gets uninitialized.
5627 * @return
5628 */
5629HRESULT Medium::i_close(AutoCaller &autoCaller)
5630{
5631 // must temporarily drop the caller, need the tree lock first
5632 autoCaller.release();
5633
5634 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
5635 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
5636 this->lockHandle()
5637 COMMA_LOCKVAL_SRC_POS);
5638
5639 autoCaller.add();
5640 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5641
5642 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5643 while (m->queryInfoRunning)
5644 {
5645 autoCaller.release();
5646 multilock.release();
5647 /* Must not hold the media tree lock, as Medium::i_queryInfo needs
5648 * this lock and thus we would run into a deadlock here. */
5649 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5650 /* must not hold the object lock now */
5651 Assert(!isWriteLockOnCurrentThread());
5652 {
5653 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5654 }
5655 multilock.acquire();
5656 autoCaller.add();
5657 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5658 }
5659
5660 LogFlowFunc(("ENTER for %s\n", i_getLocationFull().c_str()));
5661
5662 bool wasCreated = true;
5663
5664 switch (m->state)
5665 {
5666 case MediumState_NotCreated:
5667 wasCreated = false;
5668 break;
5669 case MediumState_Created:
5670 case MediumState_Inaccessible:
5671 break;
5672 default:
5673 return i_setStateError();
5674 }
5675
5676 if (m->fClosing)
5677 return setError(VBOX_E_OBJECT_IN_USE,
5678 tr("Medium '%s' cannot be closed because it is already in the process of being closed", ""),
5679 m->strLocationFull.c_str());
5680
5681 if (m->backRefs.size() != 0)
5682 return setError(VBOX_E_OBJECT_IN_USE,
5683 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines", "",
5684 m->backRefs.size()),
5685 m->strLocationFull.c_str(), m->backRefs.size());
5686
5687 // perform extra media-dependent close checks
5688 HRESULT hrc = i_canClose();
5689 if (FAILED(hrc)) return hrc;
5690
5691 m->fClosing = true;
5692
5693 if (wasCreated)
5694 {
5695 // remove from the list of known media before performing actual
5696 // uninitialization (to keep the media registry consistent on
5697 // failure to do so)
5698 hrc = i_unregisterWithVirtualBox();
5699 if (FAILED(hrc)) return hrc;
5700
5701 multilock.release();
5702 // Release the AutoCaller now, as otherwise uninit() will simply hang.
5703 // Needs to be done before mark the registries as modified and saving
5704 // the registry, as otherwise there may be a deadlock with someone else
5705 // closing this object while we're in i_saveModifiedRegistries(), which
5706 // needs the media tree lock, which the other thread holds until after
5707 // uninit() below.
5708 autoCaller.release();
5709 i_markRegistriesModified();
5710 m->pVirtualBox->i_saveModifiedRegistries();
5711 }
5712 else
5713 {
5714 multilock.release();
5715 // release the AutoCaller, as otherwise uninit() will simply hang
5716 autoCaller.release();
5717 }
5718
5719 uninit();
5720
5721 LogFlowFuncLeave();
5722
5723 return hrc;
5724}
5725
5726/**
5727 * Deletes the medium storage unit.
5728 *
5729 * If @a aProgress is not NULL but the object it points to is @c null then a new
5730 * progress object will be created and assigned to @a *aProgress on success,
5731 * otherwise the existing progress object is used. If Progress is NULL, then no
5732 * progress object is created/used at all.
5733 *
5734 * When @a aWait is @c false, this method will create a thread to perform the
5735 * delete operation asynchronously and will return immediately. Otherwise, it
5736 * will perform the operation on the calling thread and will not return to the
5737 * caller until the operation is completed. Note that @a aProgress cannot be
5738 * NULL when @a aWait is @c false (this method will assert in this case).
5739 *
5740 * @param aProgress Where to find/store a Progress object to track operation
5741 * completion.
5742 * @param aWait @c true if this method should block instead of creating
5743 * an asynchronous thread.
5744 * @param aNotify Notify about media for which metadata is changed
5745 * during execution of the function.
5746 *
5747 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
5748 * writing.
5749 */
5750HRESULT Medium::i_deleteStorage(ComObjPtr<Progress> *aProgress,
5751 bool aWait, bool aNotify)
5752{
5753 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5754
5755 HRESULT hrc = S_OK;
5756 ComObjPtr<Progress> pProgress;
5757 Medium::Task *pTask = NULL;
5758
5759 try
5760 {
5761 /* we're accessing the media tree, and i_canClose() needs it too */
5762 AutoWriteLock treelock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5763
5764 AutoCaller autoCaller(this);
5765 AssertComRCThrowRC(autoCaller.hrc());
5766
5767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5768
5769 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, i_getLocationFull().c_str() ));
5770
5771 if ( !(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
5772 | MediumFormatCapabilities_CreateFixed)))
5773 throw setError(VBOX_E_NOT_SUPPORTED,
5774 tr("Medium format '%s' does not support storage deletion"),
5775 m->strFormat.c_str());
5776
5777 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5778 /** @todo r=klaus would be great if this could be moved to the async
5779 * part of the operation as it can take quite a while */
5780 while (m->queryInfoRunning)
5781 {
5782 alock.release();
5783 autoCaller.release();
5784 treelock.release();
5785 /* Must not hold the media tree lock or the object lock, as
5786 * Medium::i_queryInfo needs this lock and thus we would run
5787 * into a deadlock here. */
5788 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5789 Assert(!isWriteLockOnCurrentThread());
5790 {
5791 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5792 }
5793 treelock.acquire();
5794 autoCaller.add();
5795 AssertComRCThrowRC(autoCaller.hrc());
5796 alock.acquire();
5797 }
5798
5799 /* Note that we are fine with Inaccessible state too: a) for symmetry
5800 * with create calls and b) because it doesn't really harm to try, if
5801 * it is really inaccessible, the delete operation will fail anyway.
5802 * Accepting Inaccessible state is especially important because all
5803 * registered media are initially Inaccessible upon VBoxSVC startup
5804 * until COMGETTER(RefreshState) is called. Accept Deleting state
5805 * because some callers need to put the medium in this state early
5806 * to prevent races. */
5807 switch (m->state)
5808 {
5809 case MediumState_Created:
5810 case MediumState_Deleting:
5811 case MediumState_Inaccessible:
5812 break;
5813 default:
5814 throw i_setStateError();
5815 }
5816
5817 if (m->backRefs.size() != 0)
5818 {
5819 Utf8Str strMachines;
5820 for (BackRefList::const_iterator it = m->backRefs.begin();
5821 it != m->backRefs.end();
5822 ++it)
5823 {
5824 const BackRef &b = *it;
5825 if (strMachines.length())
5826 strMachines.append(", ");
5827 strMachines.append(b.machineId.toString().c_str());
5828 }
5829#ifdef DEBUG
5830 i_dumpBackRefs();
5831#endif
5832 throw setError(VBOX_E_OBJECT_IN_USE,
5833 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s",
5834 "", m->backRefs.size()),
5835 m->strLocationFull.c_str(),
5836 m->backRefs.size(),
5837 strMachines.c_str());
5838 }
5839
5840 hrc = i_canClose();
5841 if (FAILED(hrc))
5842 throw hrc;
5843
5844 /* go to Deleting state, so that the medium is not actually locked */
5845 if (m->state != MediumState_Deleting)
5846 {
5847 hrc = i_markForDeletion();
5848 if (FAILED(hrc))
5849 throw hrc;
5850 }
5851
5852 /* Build the medium lock list. */
5853 MediumLockList *pMediumLockList(new MediumLockList());
5854 alock.release();
5855 autoCaller.release();
5856 treelock.release();
5857 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
5858 this /* pToLockWrite */,
5859 false /* fMediumLockWriteAll */,
5860 NULL,
5861 *pMediumLockList);
5862 treelock.acquire();
5863 autoCaller.add();
5864 AssertComRCThrowRC(autoCaller.hrc());
5865 alock.acquire();
5866 if (FAILED(hrc))
5867 {
5868 delete pMediumLockList;
5869 throw hrc;
5870 }
5871
5872 alock.release();
5873 autoCaller.release();
5874 treelock.release();
5875 hrc = pMediumLockList->Lock();
5876 treelock.acquire();
5877 autoCaller.add();
5878 AssertComRCThrowRC(autoCaller.hrc());
5879 alock.acquire();
5880 if (FAILED(hrc))
5881 {
5882 delete pMediumLockList;
5883 throw setError(hrc,
5884 tr("Failed to lock media when deleting '%s'"),
5885 i_getLocationFull().c_str());
5886 }
5887
5888 /* try to remove from the list of known media before performing
5889 * actual deletion (we favor the consistency of the media registry
5890 * which would have been broken if unregisterWithVirtualBox() failed
5891 * after we successfully deleted the storage) */
5892 hrc = i_unregisterWithVirtualBox();
5893 if (FAILED(hrc))
5894 throw hrc;
5895 // no longer need lock
5896 alock.release();
5897 autoCaller.release();
5898 treelock.release();
5899 i_markRegistriesModified();
5900
5901 if (aProgress != NULL)
5902 {
5903 /* use the existing progress object... */
5904 pProgress = *aProgress;
5905
5906 /* ...but create a new one if it is null */
5907 if (pProgress.isNull())
5908 {
5909 pProgress.createObject();
5910 hrc = pProgress->init(m->pVirtualBox,
5911 static_cast<IMedium*>(this),
5912 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
5913 FALSE /* aCancelable */);
5914 if (FAILED(hrc))
5915 throw hrc;
5916 }
5917 }
5918
5919 /* setup task object to carry out the operation sync/async */
5920 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList, false, aNotify);
5921 hrc = pTask->hrc();
5922 AssertComRC(hrc);
5923 if (FAILED(hrc))
5924 throw hrc;
5925 }
5926 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
5927
5928 if (SUCCEEDED(hrc))
5929 {
5930 if (aWait)
5931 {
5932 hrc = pTask->runNow();
5933 delete pTask;
5934 }
5935 else
5936 hrc = pTask->createThread();
5937 pTask = NULL;
5938 if (SUCCEEDED(hrc) && aProgress != NULL)
5939 *aProgress = pProgress;
5940 }
5941 else
5942 {
5943 if (pTask)
5944 delete pTask;
5945
5946 /* Undo deleting state if necessary. */
5947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5948 /* Make sure that any error signalled by unmarkForDeletion() is not
5949 * ending up in the error list (if the caller uses MultiResult). It
5950 * usually is spurious, as in most cases the medium hasn't been marked
5951 * for deletion when the error was thrown above. */
5952 ErrorInfoKeeper eik;
5953 i_unmarkForDeletion();
5954 }
5955
5956 return hrc;
5957}
5958
5959/**
5960 * Mark a medium for deletion.
5961 *
5962 * @note Caller must hold the write lock on this medium!
5963 */
5964HRESULT Medium::i_markForDeletion()
5965{
5966 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5967 switch (m->state)
5968 {
5969 case MediumState_Created:
5970 case MediumState_Inaccessible:
5971 m->preLockState = m->state;
5972 m->state = MediumState_Deleting;
5973 return S_OK;
5974 default:
5975 return i_setStateError();
5976 }
5977}
5978
5979/**
5980 * Removes the "mark for deletion".
5981 *
5982 * @note Caller must hold the write lock on this medium!
5983 */
5984HRESULT Medium::i_unmarkForDeletion()
5985{
5986 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5987 switch (m->state)
5988 {
5989 case MediumState_Deleting:
5990 m->state = m->preLockState;
5991 return S_OK;
5992 default:
5993 return i_setStateError();
5994 }
5995}
5996
5997/**
5998 * Mark a medium for deletion which is in locked state.
5999 *
6000 * @note Caller must hold the write lock on this medium!
6001 */
6002HRESULT Medium::i_markLockedForDeletion()
6003{
6004 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
6005 if ( ( m->state == MediumState_LockedRead
6006 || m->state == MediumState_LockedWrite)
6007 && m->preLockState == MediumState_Created)
6008 {
6009 m->preLockState = MediumState_Deleting;
6010 return S_OK;
6011 }
6012 else
6013 return i_setStateError();
6014}
6015
6016/**
6017 * Removes the "mark for deletion" for a medium in locked state.
6018 *
6019 * @note Caller must hold the write lock on this medium!
6020 */
6021HRESULT Medium::i_unmarkLockedForDeletion()
6022{
6023 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
6024 if ( ( m->state == MediumState_LockedRead
6025 || m->state == MediumState_LockedWrite)
6026 && m->preLockState == MediumState_Deleting)
6027 {
6028 m->preLockState = MediumState_Created;
6029 return S_OK;
6030 }
6031 else
6032 return i_setStateError();
6033}
6034
6035/**
6036 * Queries the preferred merge direction from this to the other medium, i.e.
6037 * the one which requires the least amount of I/O and therefore time and
6038 * disk consumption.
6039 *
6040 * @returns Status code.
6041 * @retval E_FAIL in case determining the merge direction fails for some reason,
6042 * for example if getting the size of the media fails. There is no
6043 * error set though and the caller is free to continue to find out
6044 * what was going wrong later. Leaves fMergeForward unset.
6045 * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
6046 * An error is set.
6047 * @param pOther The other medium to merge with.
6048 * @param fMergeForward Resulting preferred merge direction (out).
6049 */
6050HRESULT Medium::i_queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
6051 bool &fMergeForward)
6052{
6053 AssertReturn(pOther != NULL, E_FAIL);
6054 AssertReturn(pOther != this, E_FAIL);
6055
6056 HRESULT hrc = S_OK;
6057 bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
6058
6059 try
6060 {
6061 // locking: we need the tree lock first because we access parent pointers
6062 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6063
6064 AutoCaller autoCaller(this);
6065 AssertComRCThrowRC(autoCaller.hrc());
6066
6067 AutoCaller otherCaller(pOther);
6068 AssertComRCThrowRC(otherCaller.hrc());
6069
6070 /* more sanity checking and figuring out the current merge direction */
6071 ComObjPtr<Medium> pMedium = i_getParent();
6072 while (!pMedium.isNull() && pMedium != pOther)
6073 pMedium = pMedium->i_getParent();
6074 if (pMedium == pOther)
6075 fThisParent = false;
6076 else
6077 {
6078 pMedium = pOther->i_getParent();
6079 while (!pMedium.isNull() && pMedium != this)
6080 pMedium = pMedium->i_getParent();
6081 if (pMedium == this)
6082 fThisParent = true;
6083 else
6084 {
6085 Utf8Str tgtLoc;
6086 {
6087 AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
6088 tgtLoc = pOther->i_getLocationFull();
6089 }
6090
6091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6092 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6093 tr("Media '%s' and '%s' are unrelated"),
6094 m->strLocationFull.c_str(), tgtLoc.c_str());
6095 }
6096 }
6097
6098 /*
6099 * Figure out the preferred merge direction. The current way is to
6100 * get the current sizes of file based images and select the merge
6101 * direction depending on the size.
6102 *
6103 * Can't use the VD API to get current size here as the media might
6104 * be write locked by a running VM. Resort to RTFileQuerySize().
6105 */
6106 int vrc = VINF_SUCCESS;
6107 uint64_t cbMediumThis = 0;
6108 uint64_t cbMediumOther = 0;
6109
6110 if (i_isMediumFormatFile() && pOther->i_isMediumFormatFile())
6111 {
6112 vrc = RTFileQuerySizeByPath(this->i_getLocationFull().c_str(), &cbMediumThis);
6113 if (RT_SUCCESS(vrc))
6114 {
6115 vrc = RTFileQuerySizeByPath(pOther->i_getLocationFull().c_str(),
6116 &cbMediumOther);
6117 }
6118
6119 if (RT_FAILURE(vrc))
6120 hrc = E_FAIL;
6121 else
6122 {
6123 /*
6124 * Check which merge direction might be more optimal.
6125 * This method is not bullet proof of course as there might
6126 * be overlapping blocks in the images so the file size is
6127 * not the best indicator but it is good enough for our purpose
6128 * and everything else is too complicated, especially when the
6129 * media are used by a running VM.
6130 */
6131
6132 uint32_t mediumVariants = MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized;
6133 uint32_t mediumCaps = MediumFormatCapabilities_CreateDynamic | MediumFormatCapabilities_File;
6134
6135 bool fDynamicOther = pOther->i_getMediumFormat()->i_getCapabilities() & mediumCaps
6136 && pOther->i_getVariant() & ~mediumVariants;
6137 bool fDynamicThis = i_getMediumFormat()->i_getCapabilities() & mediumCaps
6138 && i_getVariant() & ~mediumVariants;
6139 bool fMergeIntoThis = (fDynamicThis && !fDynamicOther)
6140 || (fDynamicThis == fDynamicOther && cbMediumThis > cbMediumOther);
6141 fMergeForward = fMergeIntoThis != fThisParent;
6142 }
6143 }
6144 }
6145 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6146
6147 return hrc;
6148}
6149
6150/**
6151 * Prepares this (source) medium, target medium and all intermediate media
6152 * for the merge operation.
6153 *
6154 * This method is to be called prior to calling the #mergeTo() to perform
6155 * necessary consistency checks and place involved media to appropriate
6156 * states. If #mergeTo() is not called or fails, the state modifications
6157 * performed by this method must be undone by #i_cancelMergeTo().
6158 *
6159 * See #mergeTo() for more information about merging.
6160 *
6161 * @param pTarget Target medium.
6162 * @param aMachineId Allowed machine attachment. NULL means do not check.
6163 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
6164 * do not check.
6165 * @param fLockMedia Flag whether to lock the medium lock list or not.
6166 * If set to false and the medium lock list locking fails
6167 * later you must call #i_cancelMergeTo().
6168 * @param fMergeForward Resulting merge direction (out).
6169 * @param pParentForTarget New parent for target medium after merge (out).
6170 * @param aChildrenToReparent Medium lock list containing all children of the
6171 * source which will have to be reparented to the target
6172 * after merge (out).
6173 * @param aMediumLockList Medium locking information (out).
6174 *
6175 * @note Locks medium tree for reading. Locks this object, aTarget and all
6176 * intermediate media for writing.
6177 */
6178HRESULT Medium::i_prepareMergeTo(const ComObjPtr<Medium> &pTarget,
6179 const Guid *aMachineId,
6180 const Guid *aSnapshotId,
6181 bool fLockMedia,
6182 bool &fMergeForward,
6183 ComObjPtr<Medium> &pParentForTarget,
6184 MediumLockList * &aChildrenToReparent,
6185 MediumLockList * &aMediumLockList)
6186{
6187 AssertReturn(pTarget != NULL, E_FAIL);
6188 AssertReturn(pTarget != this, E_FAIL);
6189
6190 HRESULT hrc = S_OK;
6191 fMergeForward = false;
6192 pParentForTarget.setNull();
6193 Assert(aChildrenToReparent == NULL);
6194 aChildrenToReparent = NULL;
6195 Assert(aMediumLockList == NULL);
6196 aMediumLockList = NULL;
6197
6198 try
6199 {
6200 // locking: we need the tree lock first because we access parent pointers
6201 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6202
6203 AutoCaller autoCaller(this);
6204 AssertComRCThrowRC(autoCaller.hrc());
6205
6206 AutoCaller targetCaller(pTarget);
6207 AssertComRCThrowRC(targetCaller.hrc());
6208
6209 /* more sanity checking and figuring out the merge direction */
6210 ComObjPtr<Medium> pMedium = i_getParent();
6211 while (!pMedium.isNull() && pMedium != pTarget)
6212 pMedium = pMedium->i_getParent();
6213 if (pMedium == pTarget)
6214 fMergeForward = false;
6215 else
6216 {
6217 pMedium = pTarget->i_getParent();
6218 while (!pMedium.isNull() && pMedium != this)
6219 pMedium = pMedium->i_getParent();
6220 if (pMedium == this)
6221 fMergeForward = true;
6222 else
6223 {
6224 Utf8Str tgtLoc;
6225 {
6226 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6227 tgtLoc = pTarget->i_getLocationFull();
6228 }
6229
6230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6231 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6232 tr("Media '%s' and '%s' are unrelated"),
6233 m->strLocationFull.c_str(), tgtLoc.c_str());
6234 }
6235 }
6236
6237 /* Build the lock list. */
6238 aMediumLockList = new MediumLockList();
6239 targetCaller.release();
6240 autoCaller.release();
6241 treeLock.release();
6242 if (fMergeForward)
6243 hrc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
6244 pTarget /* pToLockWrite */,
6245 false /* fMediumLockWriteAll */,
6246 NULL,
6247 *aMediumLockList);
6248 else
6249 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
6250 pTarget /* pToLockWrite */,
6251 false /* fMediumLockWriteAll */,
6252 NULL,
6253 *aMediumLockList);
6254 treeLock.acquire();
6255 autoCaller.add();
6256 AssertComRCThrowRC(autoCaller.hrc());
6257 targetCaller.add();
6258 AssertComRCThrowRC(targetCaller.hrc());
6259 if (FAILED(hrc))
6260 throw hrc;
6261
6262 /* Sanity checking, must be after lock list creation as it depends on
6263 * valid medium states. The medium objects must be accessible. Only
6264 * do this if immediate locking is requested, otherwise it fails when
6265 * we construct a medium lock list for an already running VM. Snapshot
6266 * deletion uses this to simplify its life. */
6267 if (fLockMedia)
6268 {
6269 {
6270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6271 if (m->state != MediumState_Created)
6272 throw i_setStateError();
6273 }
6274 {
6275 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6276 if (pTarget->m->state != MediumState_Created)
6277 throw pTarget->i_setStateError();
6278 }
6279 }
6280
6281 /* check medium attachment and other sanity conditions */
6282 if (fMergeForward)
6283 {
6284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6285 if (i_getChildren().size() > 1)
6286 {
6287 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6288 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6289 m->strLocationFull.c_str(), i_getChildren().size());
6290 }
6291 /* One backreference is only allowed if the machine ID is not empty
6292 * and it matches the machine the medium is attached to (including
6293 * the snapshot ID if not empty). */
6294 if ( m->backRefs.size() != 0
6295 && ( !aMachineId
6296 || m->backRefs.size() != 1
6297 || aMachineId->isZero()
6298 || *i_getFirstMachineBackrefId() != *aMachineId
6299 || ( (!aSnapshotId || !aSnapshotId->isZero())
6300 && *i_getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
6301 throw setError(VBOX_E_OBJECT_IN_USE,
6302 tr("Medium '%s' is attached to %d virtual machines", "", m->backRefs.size()),
6303 m->strLocationFull.c_str(), m->backRefs.size());
6304 if (m->type == MediumType_Immutable)
6305 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6306 tr("Medium '%s' is immutable"),
6307 m->strLocationFull.c_str());
6308 if (m->type == MediumType_MultiAttach)
6309 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6310 tr("Medium '%s' is multi-attach"),
6311 m->strLocationFull.c_str());
6312 }
6313 else
6314 {
6315 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6316 if (pTarget->i_getChildren().size() > 1)
6317 {
6318 throw setError(VBOX_E_OBJECT_IN_USE,
6319 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6320 pTarget->m->strLocationFull.c_str(),
6321 pTarget->i_getChildren().size());
6322 }
6323 if (pTarget->m->type == MediumType_Immutable)
6324 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6325 tr("Medium '%s' is immutable"),
6326 pTarget->m->strLocationFull.c_str());
6327 if (pTarget->m->type == MediumType_MultiAttach)
6328 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6329 tr("Medium '%s' is multi-attach"),
6330 pTarget->m->strLocationFull.c_str());
6331 }
6332 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
6333 ComObjPtr<Medium> pLastIntermediate = pLast->i_getParent();
6334 for (pLast = pLastIntermediate;
6335 !pLast.isNull() && pLast != pTarget && pLast != this;
6336 pLast = pLast->i_getParent())
6337 {
6338 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
6339 if (pLast->i_getChildren().size() > 1)
6340 {
6341 throw setError(VBOX_E_OBJECT_IN_USE,
6342 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6343 pLast->m->strLocationFull.c_str(),
6344 pLast->i_getChildren().size());
6345 }
6346 if (pLast->m->backRefs.size() != 0)
6347 throw setError(VBOX_E_OBJECT_IN_USE,
6348 tr("Medium '%s' is attached to %d virtual machines", "", pLast->m->backRefs.size()),
6349 pLast->m->strLocationFull.c_str(),
6350 pLast->m->backRefs.size());
6351
6352 }
6353
6354 /* Update medium states appropriately */
6355 {
6356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6357
6358 if (m->state == MediumState_Created)
6359 {
6360 hrc = i_markForDeletion();
6361 if (FAILED(hrc))
6362 throw hrc;
6363 }
6364 else
6365 {
6366 if (fLockMedia)
6367 throw i_setStateError();
6368 else if ( m->state == MediumState_LockedWrite
6369 || m->state == MediumState_LockedRead)
6370 {
6371 /* Either mark it for deletion in locked state or allow
6372 * others to have done so. */
6373 if (m->preLockState == MediumState_Created)
6374 i_markLockedForDeletion();
6375 else if (m->preLockState != MediumState_Deleting)
6376 throw i_setStateError();
6377 }
6378 else
6379 throw i_setStateError();
6380 }
6381 }
6382
6383 if (fMergeForward)
6384 {
6385 /* we will need parent to reparent target */
6386 pParentForTarget = i_getParent();
6387 }
6388 else
6389 {
6390 /* we will need to reparent children of the source */
6391 aChildrenToReparent = new MediumLockList();
6392 for (MediaList::const_iterator it = i_getChildren().begin();
6393 it != i_getChildren().end();
6394 ++it)
6395 {
6396 pMedium = *it;
6397 aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
6398 }
6399 if (fLockMedia && aChildrenToReparent)
6400 {
6401 targetCaller.release();
6402 autoCaller.release();
6403 treeLock.release();
6404 hrc = aChildrenToReparent->Lock();
6405 treeLock.acquire();
6406 autoCaller.add();
6407 AssertComRCThrowRC(autoCaller.hrc());
6408 targetCaller.add();
6409 AssertComRCThrowRC(targetCaller.hrc());
6410 if (FAILED(hrc))
6411 throw hrc;
6412 }
6413 }
6414 for (pLast = pLastIntermediate;
6415 !pLast.isNull() && pLast != pTarget && pLast != this;
6416 pLast = pLast->i_getParent())
6417 {
6418 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
6419 if (pLast->m->state == MediumState_Created)
6420 {
6421 hrc = pLast->i_markForDeletion();
6422 if (FAILED(hrc))
6423 throw hrc;
6424 }
6425 else
6426 throw pLast->i_setStateError();
6427 }
6428
6429 /* Tweak the lock list in the backward merge case, as the target
6430 * isn't marked to be locked for writing yet. */
6431 if (!fMergeForward)
6432 {
6433 MediumLockList::Base::iterator lockListBegin =
6434 aMediumLockList->GetBegin();
6435 MediumLockList::Base::iterator lockListEnd =
6436 aMediumLockList->GetEnd();
6437 ++lockListEnd;
6438 for (MediumLockList::Base::iterator it = lockListBegin;
6439 it != lockListEnd;
6440 ++it)
6441 {
6442 MediumLock &mediumLock = *it;
6443 if (mediumLock.GetMedium() == pTarget)
6444 {
6445 HRESULT hrc2 = mediumLock.UpdateLock(true);
6446 AssertComRC(hrc2);
6447 break;
6448 }
6449 }
6450 }
6451
6452 if (fLockMedia)
6453 {
6454 targetCaller.release();
6455 autoCaller.release();
6456 treeLock.release();
6457 hrc = aMediumLockList->Lock();
6458 treeLock.acquire();
6459 autoCaller.add();
6460 AssertComRCThrowRC(autoCaller.hrc());
6461 targetCaller.add();
6462 AssertComRCThrowRC(targetCaller.hrc());
6463 if (FAILED(hrc))
6464 {
6465 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6466 throw setError(hrc,
6467 tr("Failed to lock media when merging to '%s'"),
6468 pTarget->i_getLocationFull().c_str());
6469 }
6470 }
6471 }
6472 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6473
6474 if (FAILED(hrc))
6475 {
6476 if (aMediumLockList)
6477 {
6478 delete aMediumLockList;
6479 aMediumLockList = NULL;
6480 }
6481 if (aChildrenToReparent)
6482 {
6483 delete aChildrenToReparent;
6484 aChildrenToReparent = NULL;
6485 }
6486 }
6487
6488 return hrc;
6489}
6490
6491/**
6492 * Merges this medium to the specified medium which must be either its
6493 * direct ancestor or descendant.
6494 *
6495 * Given this medium is SOURCE and the specified medium is TARGET, we will
6496 * get two variants of the merge operation:
6497 *
6498 * forward merge
6499 * ------------------------->
6500 * [Extra] <- SOURCE <- Intermediate <- TARGET
6501 * Any Del Del LockWr
6502 *
6503 *
6504 * backward merge
6505 * <-------------------------
6506 * TARGET <- Intermediate <- SOURCE <- [Extra]
6507 * LockWr Del Del LockWr
6508 *
6509 * Each diagram shows the involved media on the media chain where
6510 * SOURCE and TARGET belong. Under each medium there is a state value which
6511 * the medium must have at a time of the mergeTo() call.
6512 *
6513 * The media in the square braces may be absent (e.g. when the forward
6514 * operation takes place and SOURCE is the base medium, or when the backward
6515 * merge operation takes place and TARGET is the last child in the chain) but if
6516 * they present they are involved too as shown.
6517 *
6518 * Neither the source medium nor intermediate media may be attached to
6519 * any VM directly or in the snapshot, otherwise this method will assert.
6520 *
6521 * The #i_prepareMergeTo() method must be called prior to this method to place
6522 * all involved to necessary states and perform other consistency checks.
6523 *
6524 * If @a aWait is @c true then this method will perform the operation on the
6525 * calling thread and will not return to the caller until the operation is
6526 * completed. When this method succeeds, all intermediate medium objects in
6527 * the chain will be uninitialized, the state of the target medium (and all
6528 * involved extra media) will be restored. @a aMediumLockList will not be
6529 * deleted, whether the operation is successful or not. The caller has to do
6530 * this if appropriate. Note that this (source) medium is not uninitialized
6531 * because of possible AutoCaller instances held by the caller of this method
6532 * on the current thread. It's therefore the responsibility of the caller to
6533 * call Medium::uninit() after releasing all callers.
6534 *
6535 * If @a aWait is @c false then this method will create a thread to perform the
6536 * operation asynchronously and will return immediately. If the operation
6537 * succeeds, the thread will uninitialize the source medium object and all
6538 * intermediate medium objects in the chain, reset the state of the target
6539 * medium (and all involved extra media) and delete @a aMediumLockList.
6540 * If the operation fails, the thread will only reset the states of all
6541 * involved media and delete @a aMediumLockList.
6542 *
6543 * When this method fails (regardless of the @a aWait mode), it is a caller's
6544 * responsibility to undo state changes and delete @a aMediumLockList using
6545 * #i_cancelMergeTo().
6546 *
6547 * If @a aProgress is not NULL but the object it points to is @c null then a new
6548 * progress object will be created and assigned to @a *aProgress on success,
6549 * otherwise the existing progress object is used. If Progress is NULL, then no
6550 * progress object is created/used at all. Note that @a aProgress cannot be
6551 * NULL when @a aWait is @c false (this method will assert in this case).
6552 *
6553 * @param pTarget Target medium.
6554 * @param fMergeForward Merge direction.
6555 * @param pParentForTarget New parent for target medium after merge.
6556 * @param aChildrenToReparent List of children of the source which will have
6557 * to be reparented to the target after merge.
6558 * @param aMediumLockList Medium locking information.
6559 * @param aProgress Where to find/store a Progress object to track operation
6560 * completion.
6561 * @param aWait @c true if this method should block instead of creating
6562 * an asynchronous thread.
6563 * @param aNotify Notify about media for which metadata is changed
6564 * during execution of the function.
6565 *
6566 * @note Locks the tree lock for writing. Locks the media from the chain
6567 * for writing.
6568 */
6569HRESULT Medium::i_mergeTo(const ComObjPtr<Medium> &pTarget,
6570 bool fMergeForward,
6571 const ComObjPtr<Medium> &pParentForTarget,
6572 MediumLockList *aChildrenToReparent,
6573 MediumLockList *aMediumLockList,
6574 ComObjPtr<Progress> *aProgress,
6575 bool aWait, bool aNotify)
6576{
6577 AssertReturn(pTarget != NULL, E_FAIL);
6578 AssertReturn(pTarget != this, E_FAIL);
6579 AssertReturn(aMediumLockList != NULL, E_FAIL);
6580 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6581
6582 AutoCaller autoCaller(this);
6583 AssertComRCReturnRC(autoCaller.hrc());
6584
6585 AutoCaller targetCaller(pTarget);
6586 AssertComRCReturnRC(targetCaller.hrc());
6587
6588 HRESULT hrc = S_OK;
6589 ComObjPtr<Progress> pProgress;
6590 Medium::Task *pTask = NULL;
6591
6592 try
6593 {
6594 if (aProgress != NULL)
6595 {
6596 /* use the existing progress object... */
6597 pProgress = *aProgress;
6598
6599 /* ...but create a new one if it is null */
6600 if (pProgress.isNull())
6601 {
6602 Utf8Str tgtName;
6603 {
6604 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6605 tgtName = pTarget->i_getName();
6606 }
6607
6608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6609
6610 pProgress.createObject();
6611 hrc = pProgress->init(m->pVirtualBox,
6612 static_cast<IMedium*>(this),
6613 BstrFmt(tr("Merging medium '%s' to '%s'"),
6614 i_getName().c_str(),
6615 tgtName.c_str()).raw(),
6616 TRUE, /* aCancelable */
6617 2, /* Number of opearations */
6618 BstrFmt(tr("Resizing medium '%s' before merge"),
6619 tgtName.c_str()).raw()
6620 );
6621 if (FAILED(hrc))
6622 throw hrc;
6623 }
6624 }
6625
6626 /* setup task object to carry out the operation sync/async */
6627 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
6628 pParentForTarget, aChildrenToReparent,
6629 pProgress, aMediumLockList,
6630 aWait /* fKeepMediumLockList */,
6631 aNotify);
6632 hrc = pTask->hrc();
6633 AssertComRC(hrc);
6634 if (FAILED(hrc))
6635 throw hrc;
6636 }
6637 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6638
6639 if (SUCCEEDED(hrc))
6640 {
6641 if (aWait)
6642 {
6643 hrc = pTask->runNow();
6644 delete pTask;
6645 }
6646 else
6647 hrc = pTask->createThread();
6648 pTask = NULL;
6649 if (SUCCEEDED(hrc) && aProgress != NULL)
6650 *aProgress = pProgress;
6651 }
6652 else if (pTask != NULL)
6653 delete pTask;
6654
6655 return hrc;
6656}
6657
6658/**
6659 * Undoes what #i_prepareMergeTo() did. Must be called if #mergeTo() is not
6660 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
6661 * the medium objects in @a aChildrenToReparent.
6662 *
6663 * @param aChildrenToReparent List of children of the source which will have
6664 * to be reparented to the target after merge.
6665 * @param aMediumLockList Medium locking information.
6666 *
6667 * @note Locks the tree lock for writing. Locks the media from the chain
6668 * for writing.
6669 */
6670void Medium::i_cancelMergeTo(MediumLockList *aChildrenToReparent,
6671 MediumLockList *aMediumLockList)
6672{
6673 AutoCaller autoCaller(this);
6674 AssertComRCReturnVoid(autoCaller.hrc());
6675
6676 AssertReturnVoid(aMediumLockList != NULL);
6677
6678 /* Revert media marked for deletion to previous state. */
6679 MediumLockList::Base::const_iterator mediumListBegin =
6680 aMediumLockList->GetBegin();
6681 MediumLockList::Base::const_iterator mediumListEnd =
6682 aMediumLockList->GetEnd();
6683 for (MediumLockList::Base::const_iterator it = mediumListBegin;
6684 it != mediumListEnd;
6685 ++it)
6686 {
6687 const MediumLock &mediumLock = *it;
6688 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6689 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6690
6691 if (pMedium->m->state == MediumState_Deleting)
6692 {
6693 HRESULT hrc = pMedium->i_unmarkForDeletion();
6694 AssertComRC(hrc);
6695 }
6696 else if ( ( pMedium->m->state == MediumState_LockedWrite
6697 || pMedium->m->state == MediumState_LockedRead)
6698 && pMedium->m->preLockState == MediumState_Deleting)
6699 {
6700 HRESULT hrc = pMedium->i_unmarkLockedForDeletion();
6701 AssertComRC(hrc);
6702 }
6703 }
6704
6705 /* the destructor will do the work */
6706 delete aMediumLockList;
6707
6708 /* unlock the children which had to be reparented, the destructor will do
6709 * the work */
6710 if (aChildrenToReparent)
6711 delete aChildrenToReparent;
6712}
6713
6714/**
6715 * Resizes the media.
6716 *
6717 * If @a aWait is @c true then this method will perform the operation on the
6718 * calling thread and will not return to the caller until the operation is
6719 * completed. When this method succeeds, the state of the target medium (and all
6720 * involved extra media) will be restored. @a aMediumLockList will not be
6721 * deleted, whether the operation is successful or not. The caller has to do
6722 * this if appropriate.
6723 *
6724 * If @a aWait is @c false then this method will create a thread to perform the
6725 * operation asynchronously and will return immediately. The thread will reset
6726 * the state of the target medium (and all involved extra media) and delete
6727 * @a aMediumLockList.
6728 *
6729 * When this method fails (regardless of the @a aWait mode), it is a caller's
6730 * responsibility to undo state changes and delete @a aMediumLockList.
6731 *
6732 * If @a aProgress is not NULL but the object it points to is @c null then a new
6733 * progress object will be created and assigned to @a *aProgress on success,
6734 * otherwise the existing progress object is used. If Progress is NULL, then no
6735 * progress object is created/used at all. Note that @a aProgress cannot be
6736 * NULL when @a aWait is @c false (this method will assert in this case).
6737 *
6738 * @param aLogicalSize New nominal capacity of the medium in bytes.
6739 * @param aMediumLockList Medium locking information.
6740 * @param aProgress Where to find/store a Progress object to track operation
6741 * completion.
6742 * @param aWait @c true if this method should block instead of creating
6743 * an asynchronous thread.
6744 * @param aNotify Notify about media for which metadata is changed
6745 * during execution of the function.
6746 *
6747 * @note Locks the media from the chain for writing.
6748 */
6749
6750HRESULT Medium::i_resize(uint64_t aLogicalSize,
6751 MediumLockList *aMediumLockList,
6752 ComObjPtr<Progress> *aProgress,
6753 bool aWait,
6754 bool aNotify)
6755{
6756 AssertReturn(aMediumLockList != NULL, E_FAIL);
6757 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6758
6759 AutoCaller autoCaller(this);
6760 AssertComRCReturnRC(autoCaller.hrc());
6761
6762 HRESULT hrc = S_OK;
6763 ComObjPtr<Progress> pProgress;
6764 Medium::Task *pTask = NULL;
6765
6766 try
6767 {
6768 if (aProgress != NULL)
6769 {
6770 /* use the existing progress object... */
6771 pProgress = *aProgress;
6772
6773 /* ...but create a new one if it is null */
6774 if (pProgress.isNull())
6775 {
6776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6777
6778 pProgress.createObject();
6779 hrc = pProgress->init(m->pVirtualBox,
6780 static_cast <IMedium *>(this),
6781 BstrFmt(tr("Resizing medium '%s'"), m->strLocationFull.c_str()).raw(),
6782 TRUE /* aCancelable */);
6783 if (FAILED(hrc))
6784 throw hrc;
6785 }
6786 }
6787
6788 /* setup task object to carry out the operation asynchronously */
6789 pTask = new Medium::ResizeTask(this,
6790 aLogicalSize,
6791 pProgress,
6792 aMediumLockList,
6793 aWait /* fKeepMediumLockList */,
6794 aNotify);
6795 hrc = pTask->hrc();
6796 AssertComRC(hrc);
6797 if (FAILED(hrc))
6798 throw hrc;
6799 }
6800 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6801
6802 if (SUCCEEDED(hrc))
6803 {
6804 if (aWait)
6805 {
6806 hrc = pTask->runNow();
6807 delete pTask;
6808 }
6809 else
6810 hrc = pTask->createThread();
6811 pTask = NULL;
6812 if (SUCCEEDED(hrc) && aProgress != NULL)
6813 *aProgress = pProgress;
6814 }
6815 else if (pTask != NULL)
6816 delete pTask;
6817
6818 return hrc;
6819}
6820
6821/**
6822 * Fix the parent UUID of all children to point to this medium as their
6823 * parent.
6824 */
6825HRESULT Medium::i_fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
6826{
6827 /** @todo r=klaus The code below needs to be double checked with regard
6828 * to lock order violations, it probably causes lock order issues related
6829 * to the AutoCaller usage. Likewise the code using this method seems
6830 * problematic. */
6831 Assert(!isWriteLockOnCurrentThread());
6832 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6833 MediumLockList mediumLockList;
6834 HRESULT hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
6835 NULL /* pToLockWrite */,
6836 false /* fMediumLockWriteAll */,
6837 this,
6838 mediumLockList);
6839 AssertComRCReturnRC(hrc);
6840
6841 try
6842 {
6843 PVDISK hdd;
6844 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6845 ComAssertRCThrow(vrc, E_FAIL);
6846
6847 try
6848 {
6849 MediumLockList::Base::iterator lockListBegin =
6850 mediumLockList.GetBegin();
6851 MediumLockList::Base::iterator lockListEnd =
6852 mediumLockList.GetEnd();
6853 for (MediumLockList::Base::iterator it = lockListBegin;
6854 it != lockListEnd;
6855 ++it)
6856 {
6857 MediumLock &mediumLock = *it;
6858 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6859 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6860
6861 // open the medium
6862 vrc = VDOpen(hdd,
6863 pMedium->m->strFormat.c_str(),
6864 pMedium->m->strLocationFull.c_str(),
6865 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
6866 pMedium->m->vdImageIfaces);
6867 if (RT_FAILURE(vrc))
6868 throw vrc;
6869 }
6870
6871 MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
6872 MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
6873 for (MediumLockList::Base::iterator it = childrenBegin;
6874 it != childrenEnd;
6875 ++it)
6876 {
6877 Medium *pMedium = it->GetMedium();
6878 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
6879 vrc = VDOpen(hdd,
6880 pMedium->m->strFormat.c_str(),
6881 pMedium->m->strLocationFull.c_str(),
6882 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
6883 pMedium->m->vdImageIfaces);
6884 if (RT_FAILURE(vrc))
6885 throw vrc;
6886
6887 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
6888 if (RT_FAILURE(vrc))
6889 throw vrc;
6890
6891 vrc = VDClose(hdd, false /* fDelete */);
6892 if (RT_FAILURE(vrc))
6893 throw vrc;
6894 }
6895 }
6896 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6897 catch (int vrcXcpt)
6898 {
6899 hrc = setErrorBoth(E_FAIL, vrcXcpt,
6900 tr("Could not update medium UUID references to parent '%s' (%s)"),
6901 m->strLocationFull.c_str(),
6902 i_vdError(vrcXcpt).c_str());
6903 }
6904
6905 VDDestroy(hdd);
6906 }
6907 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6908
6909 return hrc;
6910}
6911
6912/**
6913 *
6914 * @note Similar code exists in i_taskExportHandler.
6915 */
6916HRESULT Medium::i_addRawToFss(const char *aFilename, SecretKeyStore *pKeyStore, RTVFSFSSTREAM hVfsFssDst,
6917 const ComObjPtr<Progress> &aProgress, bool fSparse)
6918{
6919 AutoCaller autoCaller(this);
6920 HRESULT hrc = autoCaller.hrc();
6921 if (SUCCEEDED(hrc))
6922 {
6923 /*
6924 * Get a readonly hdd for this medium.
6925 */
6926 MediumCryptoFilterSettings CryptoSettingsRead;
6927 MediumLockList SourceMediumLockList;
6928 PVDISK pHdd;
6929 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pHdd, &SourceMediumLockList, &CryptoSettingsRead);
6930 if (SUCCEEDED(hrc))
6931 {
6932 /*
6933 * Create a VFS file interface to the HDD and attach a progress wrapper
6934 * that monitors the progress reading of the raw image. The image will
6935 * be read twice if hVfsFssDst does sparse processing.
6936 */
6937 RTVFSFILE hVfsFileDisk = NIL_RTVFSFILE;
6938 int vrc = VDCreateVfsFileFromDisk(pHdd, 0 /*fFlags*/, &hVfsFileDisk);
6939 if (RT_SUCCESS(vrc))
6940 {
6941 RTVFSFILE hVfsFileProgress = NIL_RTVFSFILE;
6942 vrc = RTVfsCreateProgressForFile(hVfsFileDisk, aProgress->i_iprtProgressCallback, &*aProgress,
6943 RTVFSPROGRESS_F_CANCELABLE | RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ,
6944 VDGetSize(pHdd, VD_LAST_IMAGE) * (fSparse ? 2 : 1) /*cbExpectedRead*/,
6945 0 /*cbExpectedWritten*/, &hVfsFileProgress);
6946 RTVfsFileRelease(hVfsFileDisk);
6947 if (RT_SUCCESS(vrc))
6948 {
6949 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFileProgress);
6950 RTVfsFileRelease(hVfsFileProgress);
6951
6952 vrc = RTVfsFsStrmAdd(hVfsFssDst, aFilename, hVfsObj, 0 /*fFlags*/);
6953 RTVfsObjRelease(hVfsObj);
6954 if (RT_FAILURE(vrc))
6955 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to add '%s' to output (%Rrc)"), aFilename, vrc);
6956 }
6957 else
6958 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
6959 tr("RTVfsCreateProgressForFile failed when processing '%s' (%Rrc)"), aFilename, vrc);
6960 }
6961 else
6962 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("VDCreateVfsFileFromDisk failed for '%s' (%Rrc)"), aFilename, vrc);
6963 VDDestroy(pHdd);
6964 }
6965 }
6966 return hrc;
6967}
6968
6969/**
6970 * Used by IAppliance to export disk images.
6971 *
6972 * @param aFilename Filename to create (UTF8).
6973 * @param aFormat Medium format for creating @a aFilename.
6974 * @param aVariant Which exact image format variant to use for the
6975 * destination image.
6976 * @param pKeyStore The optional key store for decrypting the data for
6977 * encrypted media during the export.
6978 * @param hVfsIosDst The destination I/O stream object.
6979 * @param aProgress Progress object to use.
6980 * @return
6981 *
6982 * @note The source format is defined by the Medium instance.
6983 */
6984HRESULT Medium::i_exportFile(const char *aFilename,
6985 const ComObjPtr<MediumFormat> &aFormat,
6986 MediumVariant_T aVariant,
6987 SecretKeyStore *pKeyStore,
6988 RTVFSIOSTREAM hVfsIosDst,
6989 const ComObjPtr<Progress> &aProgress)
6990{
6991 AssertPtrReturn(aFilename, E_INVALIDARG);
6992 AssertReturn(aFormat.isNotNull(), E_INVALIDARG);
6993 AssertReturn(aProgress.isNotNull(), E_INVALIDARG);
6994
6995 AutoCaller autoCaller(this);
6996 HRESULT hrc = autoCaller.hrc();
6997 if (SUCCEEDED(hrc))
6998 {
6999 /*
7000 * Setup VD interfaces.
7001 */
7002 PVDINTERFACE pVDImageIfaces = m->vdImageIfaces;
7003 PVDINTERFACEIO pVfsIoIf;
7004 int vrc = VDIfCreateFromVfsStream(hVfsIosDst, RTFILE_O_WRITE, &pVfsIoIf);
7005 if (RT_SUCCESS(vrc))
7006 {
7007 vrc = VDInterfaceAdd(&pVfsIoIf->Core, "Medium::ExportTaskVfsIos", VDINTERFACETYPE_IO,
7008 pVfsIoIf, sizeof(VDINTERFACEIO), &pVDImageIfaces);
7009 if (RT_SUCCESS(vrc))
7010 {
7011 /*
7012 * Get a readonly hdd for this medium (source).
7013 */
7014 MediumCryptoFilterSettings CryptoSettingsRead;
7015 MediumLockList SourceMediumLockList;
7016 PVDISK pSrcHdd;
7017 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pSrcHdd, &SourceMediumLockList, &CryptoSettingsRead);
7018 if (SUCCEEDED(hrc))
7019 {
7020 /*
7021 * Create the target medium.
7022 */
7023 Utf8Str strDstFormat(aFormat->i_getId());
7024
7025 /* ensure the target directory exists */
7026 uint64_t fDstCapabilities = aFormat->i_getCapabilities();
7027 if (fDstCapabilities & MediumFormatCapabilities_File)
7028 {
7029 Utf8Str strDstLocation(aFilename);
7030 hrc = VirtualBox::i_ensureFilePathExists(strDstLocation.c_str(),
7031 !(aVariant & MediumVariant_NoCreateDir) /* fCreate */);
7032 }
7033 if (SUCCEEDED(hrc))
7034 {
7035 PVDISK pDstHdd;
7036 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDstHdd);
7037 if (RT_SUCCESS(vrc))
7038 {
7039 /*
7040 * Create an interface for getting progress callbacks.
7041 */
7042 VDINTERFACEPROGRESS ProgressIf = VDINTERFACEPROGRESS_INITALIZER(aProgress->i_vdProgressCallback);
7043 PVDINTERFACE pProgress = NULL;
7044 vrc = VDInterfaceAdd(&ProgressIf.Core, "export-progress", VDINTERFACETYPE_PROGRESS,
7045 &*aProgress, sizeof(ProgressIf), &pProgress);
7046 AssertRC(vrc);
7047
7048 /*
7049 * Do the exporting.
7050 */
7051 vrc = VDCopy(pSrcHdd,
7052 VD_LAST_IMAGE,
7053 pDstHdd,
7054 strDstFormat.c_str(),
7055 aFilename,
7056 false /* fMoveByRename */,
7057 0 /* cbSize */,
7058 aVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk),
7059 NULL /* pDstUuid */,
7060 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
7061 pProgress,
7062 pVDImageIfaces,
7063 NULL);
7064 if (RT_SUCCESS(vrc))
7065 hrc = S_OK;
7066 else
7067 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Could not create the exported medium '%s'%s"),
7068 aFilename, i_vdError(vrc).c_str());
7069 VDDestroy(pDstHdd);
7070 }
7071 else
7072 hrc = setErrorVrc(vrc);
7073 }
7074 }
7075 VDDestroy(pSrcHdd);
7076 }
7077 else
7078 hrc = setErrorVrc(vrc, "VDInterfaceAdd -> %Rrc", vrc);
7079 VDIfDestroyFromVfsStream(pVfsIoIf);
7080 }
7081 else
7082 hrc = setErrorVrc(vrc, "VDIfCreateFromVfsStream -> %Rrc", vrc);
7083 }
7084 return hrc;
7085}
7086
7087/**
7088 * Used by IAppliance to import disk images.
7089 *
7090 * @param aFilename Filename to read (UTF8).
7091 * @param aFormat Medium format for reading @a aFilename.
7092 * @param aVariant Which exact image format variant to use
7093 * for the destination image.
7094 * @param aVfsIosSrc Handle to the source I/O stream.
7095 * @param aParent Parent medium. May be NULL.
7096 * @param aProgress Progress object to use.
7097 * @param aNotify Notify about media for which metadata is changed
7098 * during execution of the function.
7099 * @return
7100 * @note The destination format is defined by the Medium instance.
7101 *
7102 * @todo The only consumer of this method (Appliance::i_importOneDiskImage) is
7103 * already on a worker thread, so perhaps consider bypassing the thread
7104 * here and run in the task synchronously? VBoxSVC has enough threads as
7105 * it is...
7106 */
7107HRESULT Medium::i_importFile(const char *aFilename,
7108 const ComObjPtr<MediumFormat> &aFormat,
7109 MediumVariant_T aVariant,
7110 RTVFSIOSTREAM aVfsIosSrc,
7111 const ComObjPtr<Medium> &aParent,
7112 const ComObjPtr<Progress> &aProgress,
7113 bool aNotify)
7114{
7115 /** @todo r=klaus The code below needs to be double checked with regard
7116 * to lock order violations, it probably causes lock order issues related
7117 * to the AutoCaller usage. */
7118 AssertPtrReturn(aFilename, E_INVALIDARG);
7119 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
7120 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
7121
7122 AutoCaller autoCaller(this);
7123 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7124
7125 HRESULT hrc = S_OK;
7126 Medium::Task *pTask = NULL;
7127
7128 try
7129 {
7130 // locking: we need the tree lock first because we access parent pointers
7131 // and we need to write-lock the media involved
7132 uint32_t cHandles = 2;
7133 LockHandle* pHandles[3] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
7134 this->lockHandle() };
7135 /* Only add parent to the lock if it is not null */
7136 if (!aParent.isNull())
7137 pHandles[cHandles++] = aParent->lockHandle();
7138 AutoWriteLock alock(cHandles,
7139 pHandles
7140 COMMA_LOCKVAL_SRC_POS);
7141
7142 if ( m->state != MediumState_NotCreated
7143 && m->state != MediumState_Created)
7144 throw i_setStateError();
7145
7146 /* Build the target lock list. */
7147 MediumLockList *pTargetMediumLockList(new MediumLockList());
7148 alock.release();
7149 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
7150 this /* pToLockWrite */,
7151 false /* fMediumLockWriteAll */,
7152 aParent,
7153 *pTargetMediumLockList);
7154 alock.acquire();
7155 if (FAILED(hrc))
7156 {
7157 delete pTargetMediumLockList;
7158 throw hrc;
7159 }
7160
7161 alock.release();
7162 hrc = pTargetMediumLockList->Lock();
7163 alock.acquire();
7164 if (FAILED(hrc))
7165 {
7166 delete pTargetMediumLockList;
7167 throw setError(hrc,
7168 tr("Failed to lock target media '%s'"),
7169 i_getLocationFull().c_str());
7170 }
7171
7172 /* setup task object to carry out the operation asynchronously */
7173 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat, aVariant,
7174 aVfsIosSrc, aParent, pTargetMediumLockList, false, aNotify);
7175 hrc = pTask->hrc();
7176 AssertComRC(hrc);
7177 if (FAILED(hrc))
7178 throw hrc;
7179
7180 if (m->state == MediumState_NotCreated)
7181 m->state = MediumState_Creating;
7182 }
7183 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
7184
7185 if (SUCCEEDED(hrc))
7186 {
7187 hrc = pTask->createThread();
7188 pTask = NULL;
7189 }
7190 else if (pTask != NULL)
7191 delete pTask;
7192
7193 return hrc;
7194}
7195
7196/**
7197 * Internal version of the public CloneTo API which allows to enable certain
7198 * optimizations to improve speed during VM cloning.
7199 *
7200 * @param aTarget Target medium
7201 * @param aVariant Which exact image format variant to use
7202 * for the destination image.
7203 * @param aParent Parent medium. May be NULL.
7204 * @param aProgress Progress object to use.
7205 * @param idxSrcImageSame The last image in the source chain which has the
7206 * same content as the given image in the destination
7207 * chain. Use UINT32_MAX to disable this optimization.
7208 * @param idxDstImageSame The last image in the destination chain which has the
7209 * same content as the given image in the source chain.
7210 * Use UINT32_MAX to disable this optimization.
7211 * @param aNotify Notify about media for which metadata is changed
7212 * during execution of the function.
7213 * @return
7214 */
7215HRESULT Medium::i_cloneToEx(const ComObjPtr<Medium> &aTarget, MediumVariant_T aVariant,
7216 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
7217 uint32_t idxSrcImageSame, uint32_t idxDstImageSame, bool aNotify)
7218{
7219 /** @todo r=klaus The code below needs to be double checked with regard
7220 * to lock order violations, it probably causes lock order issues related
7221 * to the AutoCaller usage. */
7222 CheckComArgNotNull(aTarget);
7223 CheckComArgOutPointerValid(aProgress);
7224 ComAssertRet(aTarget != this, E_INVALIDARG);
7225
7226 AutoCaller autoCaller(this);
7227 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7228
7229 HRESULT hrc = S_OK;
7230 ComObjPtr<Progress> pProgress;
7231 Medium::Task *pTask = NULL;
7232
7233 try
7234 {
7235 // locking: we need the tree lock first because we access parent pointers
7236 // and we need to write-lock the media involved
7237 uint32_t cHandles = 3;
7238 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
7239 this->lockHandle(),
7240 aTarget->lockHandle() };
7241 /* Only add parent to the lock if it is not null */
7242 if (!aParent.isNull())
7243 pHandles[cHandles++] = aParent->lockHandle();
7244 AutoWriteLock alock(cHandles,
7245 pHandles
7246 COMMA_LOCKVAL_SRC_POS);
7247
7248 if ( aTarget->m->state != MediumState_NotCreated
7249 && aTarget->m->state != MediumState_Created)
7250 throw aTarget->i_setStateError();
7251
7252 /* Build the source lock list. */
7253 MediumLockList *pSourceMediumLockList(new MediumLockList());
7254 alock.release();
7255 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
7256 NULL /* pToLockWrite */,
7257 false /* fMediumLockWriteAll */,
7258 NULL,
7259 *pSourceMediumLockList);
7260 alock.acquire();
7261 if (FAILED(hrc))
7262 {
7263 delete pSourceMediumLockList;
7264 throw hrc;
7265 }
7266
7267 /* Build the target lock list (including the to-be parent chain). */
7268 MediumLockList *pTargetMediumLockList(new MediumLockList());
7269 alock.release();
7270 hrc = aTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
7271 aTarget /* pToLockWrite */,
7272 false /* fMediumLockWriteAll */,
7273 aParent,
7274 *pTargetMediumLockList);
7275 alock.acquire();
7276 if (FAILED(hrc))
7277 {
7278 delete pSourceMediumLockList;
7279 delete pTargetMediumLockList;
7280 throw hrc;
7281 }
7282
7283 alock.release();
7284 hrc = pSourceMediumLockList->Lock();
7285 alock.acquire();
7286 if (FAILED(hrc))
7287 {
7288 delete pSourceMediumLockList;
7289 delete pTargetMediumLockList;
7290 throw setError(hrc,
7291 tr("Failed to lock source media '%s'"),
7292 i_getLocationFull().c_str());
7293 }
7294 alock.release();
7295 hrc = pTargetMediumLockList->Lock();
7296 alock.acquire();
7297 if (FAILED(hrc))
7298 {
7299 delete pSourceMediumLockList;
7300 delete pTargetMediumLockList;
7301 throw setError(hrc,
7302 tr("Failed to lock target media '%s'"),
7303 aTarget->i_getLocationFull().c_str());
7304 }
7305
7306 pProgress.createObject();
7307 hrc = pProgress->init(m->pVirtualBox,
7308 static_cast <IMedium *>(this),
7309 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
7310 TRUE /* aCancelable */);
7311 if (FAILED(hrc))
7312 {
7313 delete pSourceMediumLockList;
7314 delete pTargetMediumLockList;
7315 throw hrc;
7316 }
7317
7318 /* setup task object to carry out the operation asynchronously */
7319 pTask = new Medium::CloneTask(this, pProgress, aTarget, aVariant,
7320 aParent, idxSrcImageSame,
7321 idxDstImageSame, pSourceMediumLockList,
7322 pTargetMediumLockList, false, false, aNotify);
7323 hrc = pTask->hrc();
7324 AssertComRC(hrc);
7325 if (FAILED(hrc))
7326 throw hrc;
7327
7328 if (aTarget->m->state == MediumState_NotCreated)
7329 aTarget->m->state = MediumState_Creating;
7330 }
7331 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
7332
7333 if (SUCCEEDED(hrc))
7334 {
7335 hrc = pTask->createThread();
7336 pTask = NULL;
7337 if (SUCCEEDED(hrc))
7338 pProgress.queryInterfaceTo(aProgress);
7339 }
7340 else if (pTask != NULL)
7341 delete pTask;
7342
7343 return hrc;
7344}
7345
7346/**
7347 * Returns the key identifier for this medium if encryption is configured.
7348 *
7349 * @returns Key identifier or empty string if no encryption is configured.
7350 */
7351const Utf8Str& Medium::i_getKeyId()
7352{
7353 ComObjPtr<Medium> pBase = i_getBase();
7354
7355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7356
7357 settings::StringsMap::const_iterator it = pBase->m->mapProperties.find("CRYPT/KeyId");
7358 if (it == pBase->m->mapProperties.end())
7359 return Utf8Str::Empty;
7360
7361 return it->second;
7362}
7363
7364
7365/**
7366 * Returns all filter related properties.
7367 *
7368 * @returns COM status code.
7369 * @param aReturnNames Where to store the properties names on success.
7370 * @param aReturnValues Where to store the properties values on success.
7371 */
7372HRESULT Medium::i_getFilterProperties(std::vector<com::Utf8Str> &aReturnNames,
7373 std::vector<com::Utf8Str> &aReturnValues)
7374{
7375 std::vector<com::Utf8Str> aPropNames;
7376 std::vector<com::Utf8Str> aPropValues;
7377 HRESULT hrc = getProperties(Utf8Str(""), aPropNames, aPropValues);
7378
7379 if (SUCCEEDED(hrc))
7380 {
7381 unsigned cReturnSize = 0;
7382 aReturnNames.resize(0);
7383 aReturnValues.resize(0);
7384 for (unsigned idx = 0; idx < aPropNames.size(); idx++)
7385 {
7386 if (i_isPropertyForFilter(aPropNames[idx]))
7387 {
7388 aReturnNames.resize(cReturnSize + 1);
7389 aReturnValues.resize(cReturnSize + 1);
7390 aReturnNames[cReturnSize] = aPropNames[idx];
7391 aReturnValues[cReturnSize] = aPropValues[idx];
7392 cReturnSize++;
7393 }
7394 }
7395 }
7396
7397 return hrc;
7398}
7399
7400/**
7401 * Preparation to move this medium to a new location
7402 *
7403 * @param aLocation Location of the storage unit. If the location is a FS-path,
7404 * then it can be relative to the VirtualBox home directory.
7405 *
7406 * @note Must be called from under this object's write lock.
7407 */
7408HRESULT Medium::i_preparationForMoving(const Utf8Str &aLocation)
7409{
7410 HRESULT hrc = E_FAIL;
7411
7412 if (i_getLocationFull() != aLocation)
7413 {
7414 m->strNewLocationFull = aLocation;
7415 m->fMoveThisMedium = true;
7416 hrc = S_OK;
7417 }
7418
7419 return hrc;
7420}
7421
7422/**
7423 * Checking whether current operation "moving" or not
7424 */
7425bool Medium::i_isMoveOperation(const ComObjPtr<Medium> &aTarget) const
7426{
7427 RT_NOREF(aTarget);
7428 return m->fMoveThisMedium;
7429}
7430
7431bool Medium::i_resetMoveOperationData()
7432{
7433 m->strNewLocationFull.setNull();
7434 m->fMoveThisMedium = false;
7435 return true;
7436}
7437
7438Utf8Str Medium::i_getNewLocationForMoving() const
7439{
7440 if (m->fMoveThisMedium == true)
7441 return m->strNewLocationFull;
7442 else
7443 return Utf8Str();
7444}
7445////////////////////////////////////////////////////////////////////////////////
7446//
7447// Private methods
7448//
7449////////////////////////////////////////////////////////////////////////////////
7450
7451/**
7452 * Queries information from the medium.
7453 *
7454 * As a result of this call, the accessibility state and data members such as
7455 * size and description will be updated with the current information.
7456 *
7457 * @note This method may block during a system I/O call that checks storage
7458 * accessibility.
7459 *
7460 * @note Caller MUST NOT hold the media tree or medium lock.
7461 *
7462 * @note Locks m->pParent for reading. Locks this object for writing.
7463 *
7464 * @param fSetImageId Whether to reset the UUID contained in the image file
7465 * to the UUID in the medium instance data (see SetIDs())
7466 * @param fSetParentId Whether to reset the parent UUID contained in the image
7467 * file to the parent UUID in the medium instance data (see
7468 * SetIDs())
7469 * @param autoCaller
7470 * @return
7471 */
7472HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
7473{
7474 Assert(!isWriteLockOnCurrentThread());
7475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7476
7477 if ( ( m->state != MediumState_Created
7478 && m->state != MediumState_Inaccessible
7479 && m->state != MediumState_LockedRead)
7480 || m->fClosing)
7481 return E_FAIL;
7482
7483 HRESULT hrc = S_OK;
7484
7485 int vrc = VINF_SUCCESS;
7486
7487 /* check if a blocking i_queryInfo() call is in progress on some other thread,
7488 * and wait for it to finish if so instead of querying data ourselves */
7489 if (m->queryInfoRunning)
7490 {
7491 Assert( m->state == MediumState_LockedRead
7492 || m->state == MediumState_LockedWrite);
7493
7494 while (m->queryInfoRunning)
7495 {
7496 alock.release();
7497 /* must not hold the object lock now */
7498 Assert(!isWriteLockOnCurrentThread());
7499 {
7500 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
7501 }
7502 alock.acquire();
7503 }
7504
7505 return S_OK;
7506 }
7507
7508 bool success = false;
7509 Utf8Str lastAccessError;
7510
7511 /* are we dealing with a new medium constructed using the existing
7512 * location? */
7513 bool isImport = m->id.isZero();
7514 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
7515
7516 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
7517 * media because that would prevent necessary modifications
7518 * when opening media of some third-party formats for the first
7519 * time in VirtualBox (such as VMDK for which VDOpen() needs to
7520 * generate an UUID if it is missing) */
7521 if ( m->hddOpenMode == OpenReadOnly
7522 || m->type == MediumType_Readonly
7523 || (!isImport && !fSetImageId && !fSetParentId)
7524 )
7525 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7526
7527 /* Open shareable medium with the appropriate flags */
7528 if (m->type == MediumType_Shareable)
7529 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7530
7531 /* Lock the medium, which makes the behavior much more consistent, must be
7532 * done before dropping the object lock and setting queryInfoRunning. */
7533 ComPtr<IToken> pToken;
7534 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
7535 hrc = LockRead(pToken.asOutParam());
7536 else
7537 hrc = LockWrite(pToken.asOutParam());
7538 if (FAILED(hrc)) return hrc;
7539
7540 /* Copies of the input state fields which are not read-only,
7541 * as we're dropping the lock. CAUTION: be extremely careful what
7542 * you do with the contents of this medium object, as you will
7543 * create races if there are concurrent changes. */
7544 Utf8Str format(m->strFormat);
7545 Utf8Str location(m->strLocationFull);
7546 ComObjPtr<MediumFormat> formatObj = m->formatObj;
7547
7548 /* "Output" values which can't be set because the lock isn't held
7549 * at the time the values are determined. */
7550 Guid mediumId = m->id;
7551 uint64_t mediumSize = 0;
7552 uint64_t mediumLogicalSize = 0;
7553
7554 /* Flag whether a base image has a non-zero parent UUID and thus
7555 * need repairing after it was closed again. */
7556 bool fRepairImageZeroParentUuid = false;
7557
7558 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
7559
7560 /* must be set before leaving the object lock the first time */
7561 m->queryInfoRunning = true;
7562
7563 /* must leave object lock now, because a lock from a higher lock class
7564 * is needed and also a lengthy operation is coming */
7565 alock.release();
7566 autoCaller.release();
7567
7568 /* Note that taking the queryInfoSem after leaving the object lock above
7569 * can lead to short spinning of the loops waiting for i_queryInfo() to
7570 * complete. This is unavoidable since the other order causes a lock order
7571 * violation: here it would be requesting the object lock (at the beginning
7572 * of the method), then queryInfoSem, and below the other way round. */
7573 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
7574
7575 /* take the opportunity to have a media tree lock, released initially */
7576 Assert(!isWriteLockOnCurrentThread());
7577 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7578 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7579 treeLock.release();
7580
7581 /* re-take the caller, but not the object lock, to keep uninit away */
7582 autoCaller.add();
7583 if (FAILED(autoCaller.hrc()))
7584 {
7585 m->queryInfoRunning = false;
7586 return autoCaller.hrc();
7587 }
7588
7589 try
7590 {
7591 /* skip accessibility checks for host drives */
7592 if (m->hostDrive)
7593 {
7594 success = true;
7595 throw S_OK;
7596 }
7597
7598 PVDISK hdd;
7599 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7600 ComAssertRCThrow(vrc, E_FAIL);
7601
7602 try
7603 {
7604 /** @todo This kind of opening of media is assuming that diff
7605 * media can be opened as base media. Should be documented that
7606 * it must work for all medium format backends. */
7607 vrc = VDOpen(hdd,
7608 format.c_str(),
7609 location.c_str(),
7610 uOpenFlags | m->uOpenFlagsDef,
7611 m->vdImageIfaces);
7612 if (RT_FAILURE(vrc))
7613 {
7614 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
7615 location.c_str(), i_vdError(vrc).c_str());
7616 throw S_OK;
7617 }
7618
7619 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
7620 {
7621 /* Modify the UUIDs if necessary. The associated fields are
7622 * not modified by other code, so no need to copy. */
7623 if (fSetImageId)
7624 {
7625 alock.acquire();
7626 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
7627 alock.release();
7628 if (RT_FAILURE(vrc))
7629 {
7630 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
7631 location.c_str(), i_vdError(vrc).c_str());
7632 throw S_OK;
7633 }
7634 mediumId = m->uuidImage;
7635 }
7636 if (fSetParentId)
7637 {
7638 alock.acquire();
7639 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
7640 alock.release();
7641 if (RT_FAILURE(vrc))
7642 {
7643 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
7644 location.c_str(), i_vdError(vrc).c_str());
7645 throw S_OK;
7646 }
7647 }
7648 /* zap the information, these are no long-term members */
7649 alock.acquire();
7650 unconst(m->uuidImage).clear();
7651 unconst(m->uuidParentImage).clear();
7652 alock.release();
7653
7654 /* check the UUID */
7655 RTUUID uuid;
7656 vrc = VDGetUuid(hdd, 0, &uuid);
7657 ComAssertRCThrow(vrc, E_FAIL);
7658
7659 if (isImport)
7660 {
7661 mediumId = uuid;
7662
7663 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
7664 // only when importing a VDMK that has no UUID, create one in memory
7665 mediumId.create();
7666 }
7667 else
7668 {
7669 Assert(!mediumId.isZero());
7670
7671 if (mediumId != uuid)
7672 {
7673 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7674 lastAccessError = Utf8StrFmt(
7675 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
7676 &uuid,
7677 location.c_str(),
7678 mediumId.raw(),
7679 pVirtualBox->i_settingsFilePath().c_str());
7680 throw S_OK;
7681 }
7682 }
7683 }
7684 else
7685 {
7686 /* the backend does not support storing UUIDs within the
7687 * underlying storage so use what we store in XML */
7688
7689 if (fSetImageId)
7690 {
7691 /* set the UUID if an API client wants to change it */
7692 alock.acquire();
7693 mediumId = m->uuidImage;
7694 alock.release();
7695 }
7696 else if (isImport)
7697 {
7698 /* generate an UUID for an imported UUID-less medium */
7699 mediumId.create();
7700 }
7701 }
7702
7703 /* set the image uuid before the below parent uuid handling code
7704 * might place it somewhere in the media tree, so that the medium
7705 * UUID is valid at this point */
7706 alock.acquire();
7707 if (isImport || fSetImageId)
7708 unconst(m->id) = mediumId;
7709 alock.release();
7710
7711 /* get the medium variant */
7712 unsigned uImageFlags;
7713 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7714 ComAssertRCThrow(vrc, E_FAIL);
7715 alock.acquire();
7716 m->variant = (MediumVariant_T)uImageFlags;
7717 alock.release();
7718
7719 /* check/get the parent uuid and update corresponding state */
7720 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
7721 {
7722 RTUUID parentId;
7723 vrc = VDGetParentUuid(hdd, 0, &parentId);
7724 ComAssertRCThrow(vrc, E_FAIL);
7725
7726 /* streamOptimized VMDK images are only accepted as base
7727 * images, as this allows automatic repair of OVF appliances.
7728 * Since such images don't support random writes they will not
7729 * be created for diff images. Only an overly smart user might
7730 * manually create this case. Too bad for him. */
7731 if ( (isImport || fSetParentId)
7732 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
7733 {
7734 /* the parent must be known to us. Note that we freely
7735 * call locking methods of mVirtualBox and parent, as all
7736 * relevant locks must be already held. There may be no
7737 * concurrent access to the just opened medium on other
7738 * threads yet (and init() will fail if this method reports
7739 * MediumState_Inaccessible) */
7740
7741 ComObjPtr<Medium> pParent;
7742 if (RTUuidIsNull(&parentId))
7743 hrc = VBOX_E_OBJECT_NOT_FOUND;
7744 else
7745 hrc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
7746 if (FAILED(hrc))
7747 {
7748 if (fSetImageId && !fSetParentId)
7749 {
7750 /* If the image UUID gets changed for an existing
7751 * image then the parent UUID can be stale. In such
7752 * cases clear the parent information. The parent
7753 * information may/will be re-set later if the
7754 * API client wants to adjust a complete medium
7755 * hierarchy one by one. */
7756 hrc = S_OK;
7757 alock.acquire();
7758 RTUuidClear(&parentId);
7759 vrc = VDSetParentUuid(hdd, 0, &parentId);
7760 alock.release();
7761 ComAssertRCThrow(vrc, E_FAIL);
7762 }
7763 else
7764 {
7765 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
7766 &parentId, location.c_str(),
7767 pVirtualBox->i_settingsFilePath().c_str());
7768 throw S_OK;
7769 }
7770 }
7771
7772 /* must drop the caller before taking the tree lock */
7773 autoCaller.release();
7774 /* we set m->pParent & children() */
7775 treeLock.acquire();
7776 autoCaller.add();
7777 if (FAILED(autoCaller.hrc()))
7778 throw autoCaller.hrc();
7779
7780 if (m->pParent)
7781 i_deparent();
7782
7783 if (!pParent.isNull())
7784 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7785 {
7786 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
7787 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7788 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"),
7789 pParent->m->strLocationFull.c_str());
7790 }
7791 i_setParent(pParent);
7792
7793 treeLock.release();
7794 }
7795 else
7796 {
7797 /* must drop the caller before taking the tree lock */
7798 autoCaller.release();
7799 /* we access m->pParent */
7800 treeLock.acquire();
7801 autoCaller.add();
7802 if (FAILED(autoCaller.hrc()))
7803 throw autoCaller.hrc();
7804
7805 /* check that parent UUIDs match. Note that there's no need
7806 * for the parent's AutoCaller (our lifetime is bound to
7807 * it) */
7808
7809 if (m->pParent.isNull())
7810 {
7811 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
7812 * and 3.1.0-3.1.8 there are base images out there
7813 * which have a non-zero parent UUID. No point in
7814 * complaining about them, instead automatically
7815 * repair the problem. Later we can bring back the
7816 * error message, but we should wait until really
7817 * most users have repaired their images, either with
7818 * VBoxFixHdd or this way. */
7819#if 1
7820 fRepairImageZeroParentUuid = true;
7821#else /* 0 */
7822 lastAccessError = Utf8StrFmt(
7823 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
7824 location.c_str(),
7825 pVirtualBox->settingsFilePath().c_str());
7826 treeLock.release();
7827 throw S_OK;
7828#endif /* 0 */
7829 }
7830
7831 {
7832 autoCaller.release();
7833 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
7834 autoCaller.add();
7835 if (FAILED(autoCaller.hrc()))
7836 throw autoCaller.hrc();
7837
7838 if ( !fRepairImageZeroParentUuid
7839 && m->pParent->i_getState() != MediumState_Inaccessible
7840 && m->pParent->i_getId() != parentId)
7841 {
7842 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7843 lastAccessError = Utf8StrFmt(
7844 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
7845 &parentId, location.c_str(),
7846 m->pParent->i_getId().raw(),
7847 pVirtualBox->i_settingsFilePath().c_str());
7848 parentLock.release();
7849 treeLock.release();
7850 throw S_OK;
7851 }
7852 }
7853
7854 /// @todo NEWMEDIA what to do if the parent is not
7855 /// accessible while the diff is? Probably nothing. The
7856 /// real code will detect the mismatch anyway.
7857
7858 treeLock.release();
7859 }
7860 }
7861
7862 mediumSize = VDGetFileSize(hdd, 0);
7863 mediumLogicalSize = VDGetSize(hdd, 0);
7864
7865 success = true;
7866 }
7867 catch (HRESULT hrcXcpt)
7868 {
7869 hrc = hrcXcpt;
7870 }
7871
7872 vrc = VDDestroy(hdd);
7873 if (RT_FAILURE(vrc))
7874 {
7875 lastAccessError.printf(tr("Could not update and close the medium '%s'%s"),
7876 location.c_str(), i_vdError(vrc).c_str());
7877 success = false;
7878 throw S_OK;
7879 }
7880 }
7881 catch (HRESULT hrcXcpt)
7882 {
7883 hrc = hrcXcpt;
7884 }
7885
7886 autoCaller.release();
7887 treeLock.acquire();
7888 autoCaller.add();
7889 if (FAILED(autoCaller.hrc()))
7890 {
7891 m->queryInfoRunning = false;
7892 return autoCaller.hrc();
7893 }
7894 alock.acquire();
7895
7896 if (success)
7897 {
7898 m->size = mediumSize;
7899 m->logicalSize = mediumLogicalSize;
7900 m->strLastAccessError.setNull();
7901 }
7902 else
7903 {
7904 m->strLastAccessError = lastAccessError;
7905 Log1WarningFunc(("'%s' is not accessible (error='%s', hrc=%Rhrc, vrc=%Rrc)\n",
7906 location.c_str(), m->strLastAccessError.c_str(), hrc, vrc));
7907 }
7908
7909 /* Set the proper state according to the result of the check */
7910 if (success)
7911 m->preLockState = MediumState_Created;
7912 else
7913 m->preLockState = MediumState_Inaccessible;
7914
7915 /* unblock anyone waiting for the i_queryInfo results */
7916 qlock.release();
7917 m->queryInfoRunning = false;
7918
7919 pToken->Abandon();
7920 pToken.setNull();
7921
7922 if (FAILED(hrc))
7923 return hrc;
7924
7925 /* If this is a base image which incorrectly has a parent UUID set,
7926 * repair the image now by zeroing the parent UUID. This is only done
7927 * when we have structural information from a config file, on import
7928 * this is not possible. If someone would accidentally call openMedium
7929 * with a diff image before the base is registered this would destroy
7930 * the diff. Not acceptable. */
7931 do
7932 {
7933 if (fRepairImageZeroParentUuid)
7934 {
7935 hrc = LockWrite(pToken.asOutParam());
7936 if (FAILED(hrc))
7937 break;
7938
7939 alock.release();
7940
7941 try
7942 {
7943 PVDISK hdd;
7944 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7945 ComAssertRCThrow(vrc, E_FAIL);
7946
7947 try
7948 {
7949 vrc = VDOpen(hdd,
7950 format.c_str(),
7951 location.c_str(),
7952 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
7953 m->vdImageIfaces);
7954 if (RT_FAILURE(vrc))
7955 throw S_OK;
7956
7957 RTUUID zeroParentUuid;
7958 RTUuidClear(&zeroParentUuid);
7959 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
7960 ComAssertRCThrow(vrc, E_FAIL);
7961 }
7962 catch (HRESULT hrcXcpt)
7963 {
7964 hrc = hrcXcpt;
7965 }
7966
7967 VDDestroy(hdd);
7968 }
7969 catch (HRESULT hrcXcpt)
7970 {
7971 hrc = hrcXcpt;
7972 }
7973
7974 pToken->Abandon();
7975 pToken.setNull();
7976 if (FAILED(hrc))
7977 break;
7978 }
7979 } while(0);
7980
7981 return hrc;
7982}
7983
7984/**
7985 * Performs extra checks if the medium can be closed and returns S_OK in
7986 * this case. Otherwise, returns a respective error message. Called by
7987 * Close() under the medium tree lock and the medium lock.
7988 *
7989 * @note Also reused by Medium::Reset().
7990 *
7991 * @note Caller must hold the media tree write lock!
7992 */
7993HRESULT Medium::i_canClose()
7994{
7995 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7996
7997 if (i_getChildren().size() != 0)
7998 return setError(VBOX_E_OBJECT_IN_USE,
7999 tr("Cannot close medium '%s' because it has %d child media", "", i_getChildren().size()),
8000 m->strLocationFull.c_str(), i_getChildren().size());
8001
8002 return S_OK;
8003}
8004
8005/**
8006 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
8007 *
8008 * @note Caller must have locked the media tree lock for writing!
8009 */
8010HRESULT Medium::i_unregisterWithVirtualBox()
8011{
8012 /* Note that we need to de-associate ourselves from the parent to let
8013 * VirtualBox::i_unregisterMedium() properly save the registry */
8014
8015 /* we modify m->pParent and access children */
8016 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
8017
8018 Medium *pParentBackup = m->pParent;
8019 AssertReturn(i_getChildren().size() == 0, E_FAIL);
8020 if (m->pParent)
8021 i_deparent();
8022
8023 HRESULT hrc = m->pVirtualBox->i_unregisterMedium(this);
8024 if (FAILED(hrc))
8025 {
8026 if (pParentBackup)
8027 {
8028 // re-associate with the parent as we are still relatives in the registry
8029 i_setParent(pParentBackup);
8030 }
8031 }
8032
8033 return hrc;
8034}
8035
8036/**
8037 * Like SetProperty but do not trigger a settings store. Only for internal use!
8038 */
8039HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
8040{
8041 AutoCaller autoCaller(this);
8042 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
8043
8044 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
8045
8046 switch (m->state)
8047 {
8048 case MediumState_Created:
8049 case MediumState_Inaccessible:
8050 break;
8051 default:
8052 return i_setStateError();
8053 }
8054
8055 m->mapProperties[aName] = aValue;
8056
8057 return S_OK;
8058}
8059
8060/**
8061 * Sets the extended error info according to the current media state.
8062 *
8063 * @note Must be called from under this object's write or read lock.
8064 */
8065HRESULT Medium::i_setStateError()
8066{
8067 HRESULT hrc;
8068
8069 switch (m->state)
8070 {
8071 case MediumState_NotCreated:
8072 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8073 tr("Storage for the medium '%s' is not created"),
8074 m->strLocationFull.c_str());
8075 break;
8076 case MediumState_Created:
8077 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8078 tr("Storage for the medium '%s' is already created"),
8079 m->strLocationFull.c_str());
8080 break;
8081 case MediumState_LockedRead:
8082 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8083 tr("Medium '%s' is locked for reading by another task"),
8084 m->strLocationFull.c_str());
8085 break;
8086 case MediumState_LockedWrite:
8087 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8088 tr("Medium '%s' is locked for writing by another task"),
8089 m->strLocationFull.c_str());
8090 break;
8091 case MediumState_Inaccessible:
8092 /* be in sync with Console::powerUpThread() */
8093 if (!m->strLastAccessError.isEmpty())
8094 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8095 tr("Medium '%s' is not accessible. %s"),
8096 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
8097 else
8098 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8099 tr("Medium '%s' is not accessible"),
8100 m->strLocationFull.c_str());
8101 break;
8102 case MediumState_Creating:
8103 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8104 tr("Storage for the medium '%s' is being created"),
8105 m->strLocationFull.c_str());
8106 break;
8107 case MediumState_Deleting:
8108 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8109 tr("Storage for the medium '%s' is being deleted"),
8110 m->strLocationFull.c_str());
8111 break;
8112 default:
8113 AssertFailed();
8114 hrc = E_FAIL;
8115 break;
8116 }
8117
8118 return hrc;
8119}
8120
8121/**
8122 * Sets the value of m->strLocationFull. The given location must be a fully
8123 * qualified path; relative paths are not supported here.
8124 *
8125 * As a special exception, if the specified location is a file path that ends with '/'
8126 * then the file name part will be generated by this method automatically in the format
8127 * '{\<uuid\>}.\<ext\>' where \<uuid\> is a fresh UUID that this method will generate
8128 * and assign to this medium, and \<ext\> is the default extension for this
8129 * medium's storage format. Note that this procedure requires the media state to
8130 * be NotCreated and will return a failure otherwise.
8131 *
8132 * @param aLocation Location of the storage unit. If the location is a FS-path,
8133 * then it can be relative to the VirtualBox home directory.
8134 * @param aFormat Optional fallback format if it is an import and the format
8135 * cannot be determined.
8136 *
8137 * @note Must be called from under this object's write lock.
8138 */
8139HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
8140 const Utf8Str &aFormat /* = Utf8Str::Empty */)
8141{
8142 AssertReturn(!aLocation.isEmpty(), E_FAIL);
8143
8144 AutoCaller autoCaller(this);
8145 AssertComRCReturnRC(autoCaller.hrc());
8146
8147 /* formatObj may be null only when initializing from an existing path and
8148 * no format is known yet */
8149 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
8150 || ( getObjectState().getState() == ObjectState::InInit
8151 && m->state != MediumState_NotCreated
8152 && m->id.isZero()
8153 && m->strFormat.isEmpty()
8154 && m->formatObj.isNull()),
8155 E_FAIL);
8156
8157 /* are we dealing with a new medium constructed using the existing
8158 * location? */
8159 bool isImport = m->strFormat.isEmpty();
8160
8161 if ( isImport
8162 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
8163 && !m->hostDrive))
8164 {
8165 Guid id;
8166
8167 Utf8Str locationFull(aLocation);
8168
8169 if (m->state == MediumState_NotCreated)
8170 {
8171 /* must be a file (formatObj must be already known) */
8172 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
8173
8174 if (RTPathFilename(aLocation.c_str()) == NULL)
8175 {
8176 /* no file name is given (either an empty string or ends with a
8177 * slash), generate a new UUID + file name if the state allows
8178 * this */
8179
8180 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
8181 (tr("Must be at least one extension if it is MediumFormatCapabilities_File\n")),
8182 E_FAIL);
8183
8184 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
8185 ComAssertMsgRet(!strExt.isEmpty(),
8186 (tr("Default extension must not be empty\n")),
8187 E_FAIL);
8188
8189 id.create();
8190
8191 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
8192 aLocation.c_str(), id.raw(), strExt.c_str());
8193 }
8194 }
8195
8196 // we must always have full paths now (if it refers to a file)
8197 if ( ( m->formatObj.isNull()
8198 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
8199 && !RTPathStartsWithRoot(locationFull.c_str()))
8200 return setError(VBOX_E_FILE_ERROR,
8201 tr("The given path '%s' is not fully qualified"),
8202 locationFull.c_str());
8203
8204 /* detect the backend from the storage unit if importing */
8205 if (isImport)
8206 {
8207 VDTYPE const enmDesiredType = i_convertDeviceType();
8208 VDTYPE enmType = VDTYPE_INVALID;
8209 char *backendName = NULL;
8210
8211 /* is it a file? */
8212 RTFILE hFile;
8213 int vrc = RTFileOpen(&hFile, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
8214 if (RT_SUCCESS(vrc))
8215 {
8216 RTFileClose(hFile);
8217 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
8218 locationFull.c_str(), enmDesiredType, &backendName, &enmType);
8219 }
8220 else if ( vrc != VERR_FILE_NOT_FOUND
8221 && vrc != VERR_PATH_NOT_FOUND
8222 && vrc != VERR_ACCESS_DENIED
8223 && locationFull != aLocation)
8224 {
8225 /* assume it's not a file, restore the original location */
8226 locationFull = aLocation;
8227 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
8228 locationFull.c_str(), enmDesiredType, &backendName, &enmType);
8229 }
8230
8231 if (RT_FAILURE(vrc))
8232 {
8233 if (vrc == VERR_ACCESS_DENIED)
8234 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8235 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
8236 locationFull.c_str(), vrc);
8237 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
8238 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8239 tr("Could not find file for the medium '%s' (%Rrc)"),
8240 locationFull.c_str(), vrc);
8241 if (aFormat.isEmpty())
8242 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8243 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
8244 locationFull.c_str(), vrc);
8245 HRESULT hrc = i_setFormat(aFormat);
8246 /* setFormat() must not fail since we've just used the backend so
8247 * the format object must be there */
8248 AssertComRCReturnRC(hrc);
8249 }
8250 else if ( enmType == VDTYPE_INVALID
8251 || m->devType != i_convertToDeviceType(enmType))
8252 {
8253 /*
8254 * The user tried to use a image as a device which is not supported
8255 * by the backend.
8256 */
8257 RTStrFree(backendName);
8258 return setError(E_FAIL,
8259 tr("The medium '%s' can't be used as the requested device type (%s, detected %s)"),
8260 locationFull.c_str(), getDeviceTypeName(m->devType), getVDTypeName(enmType));
8261 }
8262 else
8263 {
8264 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
8265
8266 HRESULT hrc = i_setFormat(backendName);
8267 RTStrFree(backendName);
8268
8269 /* setFormat() must not fail since we've just used the backend so
8270 * the format object must be there */
8271 AssertComRCReturnRC(hrc);
8272 }
8273 }
8274
8275 m->strLocationFull = locationFull;
8276
8277 /* is it still a file? */
8278 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
8279 && (m->state == MediumState_NotCreated)
8280 )
8281 /* assign a new UUID (this UUID will be used when calling
8282 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
8283 * also do that if we didn't generate it to make sure it is
8284 * either generated by us or reset to null */
8285 unconst(m->id) = id;
8286 }
8287 else
8288 m->strLocationFull = aLocation;
8289
8290 return S_OK;
8291}
8292
8293/**
8294 * Checks that the format ID is valid and sets it on success.
8295 *
8296 * Note that this method will caller-reference the format object on success!
8297 * This reference must be released somewhere to let the MediumFormat object be
8298 * uninitialized.
8299 *
8300 * @note Must be called from under this object's write lock.
8301 */
8302HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
8303{
8304 /* get the format object first */
8305 {
8306 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
8307 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
8308
8309 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
8310 if (m->formatObj.isNull())
8311 return setError(E_INVALIDARG,
8312 tr("Invalid medium storage format '%s'"),
8313 aFormat.c_str());
8314
8315 /* get properties (preinsert them as keys in the map). Note that the
8316 * map doesn't grow over the object life time since the set of
8317 * properties is meant to be constant. */
8318
8319 Assert(m->mapProperties.empty());
8320
8321 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
8322 it != m->formatObj->i_getProperties().end();
8323 ++it)
8324 {
8325 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
8326 }
8327 }
8328
8329 unconst(m->strFormat) = aFormat;
8330
8331 return S_OK;
8332}
8333
8334/**
8335 * Converts the Medium device type to the VD type.
8336 */
8337VDTYPE Medium::i_convertDeviceType()
8338{
8339 VDTYPE enmType;
8340
8341 switch (m->devType)
8342 {
8343 case DeviceType_HardDisk:
8344 enmType = VDTYPE_HDD;
8345 break;
8346 case DeviceType_DVD:
8347 enmType = VDTYPE_OPTICAL_DISC;
8348 break;
8349 case DeviceType_Floppy:
8350 enmType = VDTYPE_FLOPPY;
8351 break;
8352 default:
8353 ComAssertFailedRet(VDTYPE_INVALID);
8354 }
8355
8356 return enmType;
8357}
8358
8359/**
8360 * Converts from the VD type to the medium type.
8361 */
8362DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
8363{
8364 DeviceType_T devType;
8365
8366 switch (enmType)
8367 {
8368 case VDTYPE_HDD:
8369 devType = DeviceType_HardDisk;
8370 break;
8371 case VDTYPE_OPTICAL_DISC:
8372 devType = DeviceType_DVD;
8373 break;
8374 case VDTYPE_FLOPPY:
8375 devType = DeviceType_Floppy;
8376 break;
8377 default:
8378 ComAssertFailedRet(DeviceType_Null);
8379 }
8380
8381 return devType;
8382}
8383
8384/**
8385 * Internal method which checks whether a property name is for a filter plugin.
8386 */
8387bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
8388{
8389 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
8390 size_t offSlash;
8391 if ((offSlash = aName.find("/", 0)) != aName.npos)
8392 {
8393 com::Utf8Str strFilter;
8394 com::Utf8Str strKey;
8395
8396 HRESULT hrc = strFilter.assignEx(aName, 0, offSlash);
8397 if (FAILED(hrc))
8398 return false;
8399
8400 hrc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
8401 if (FAILED(hrc))
8402 return false;
8403
8404 VDFILTERINFO FilterInfo;
8405 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
8406 if (RT_SUCCESS(vrc))
8407 {
8408 /* Check that the property exists. */
8409 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
8410 while (paConfig->pszKey)
8411 {
8412 if (strKey.equals(paConfig->pszKey))
8413 return true;
8414 paConfig++;
8415 }
8416 }
8417 }
8418
8419 return false;
8420}
8421
8422/**
8423 * Returns the last error message collected by the i_vdErrorCall callback and
8424 * resets it.
8425 *
8426 * The error message is returned prepended with a dot and a space, like this:
8427 * <code>
8428 * ". <error_text> (%Rrc)"
8429 * </code>
8430 * to make it easily appendable to a more general error message. The @c %Rrc
8431 * format string is given @a aVRC as an argument.
8432 *
8433 * If there is no last error message collected by i_vdErrorCall or if it is a
8434 * null or empty string, then this function returns the following text:
8435 * <code>
8436 * " (%Rrc)"
8437 * </code>
8438 *
8439 * @note Doesn't do any object locking; it is assumed that the caller makes sure
8440 * the callback isn't called by more than one thread at a time.
8441 *
8442 * @param aVRC VBox error code to use when no error message is provided.
8443 */
8444Utf8Str Medium::i_vdError(int aVRC)
8445{
8446 Utf8Str error;
8447
8448 if (m->vdError.isEmpty())
8449 error.printf(" (%Rrc)", aVRC);
8450 else
8451 error.printf(".\n%s", m->vdError.c_str());
8452
8453 m->vdError.setNull();
8454
8455 return error;
8456}
8457
8458/**
8459 * Error message callback.
8460 *
8461 * Puts the reported error message to the m->vdError field.
8462 *
8463 * @note Doesn't do any object locking; it is assumed that the caller makes sure
8464 * the callback isn't called by more than one thread at a time.
8465 *
8466 * @param pvUser The opaque data passed on container creation.
8467 * @param vrc The VBox error code.
8468 * @param SRC_POS Use RT_SRC_POS.
8469 * @param pszFormat Error message format string.
8470 * @param va Error message arguments.
8471 */
8472/*static*/
8473DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int vrc, RT_SRC_POS_DECL,
8474 const char *pszFormat, va_list va)
8475{
8476 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
8477
8478 Medium *that = static_cast<Medium*>(pvUser);
8479 AssertReturnVoid(that != NULL);
8480
8481 va_list vaCopy; /* For gcc */
8482 va_copy(vaCopy, va);
8483 if (that->m->vdError.isEmpty())
8484 that->m->vdError.printf("%N (%Rrc)", pszFormat, &vaCopy, vrc);
8485 else
8486 that->m->vdError.appendPrintf(".\n%N (%Rrc)", pszFormat, &vaCopy, vrc);
8487 va_end(vaCopy);
8488}
8489
8490/* static */
8491DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
8492 const char * /* pszzValid */)
8493{
8494 Medium *that = static_cast<Medium*>(pvUser);
8495 AssertReturn(that != NULL, false);
8496
8497 /* we always return true since the only keys we have are those found in
8498 * VDBACKENDINFO */
8499 return true;
8500}
8501
8502/* static */
8503DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
8504 const char *pszName,
8505 size_t *pcbValue)
8506{
8507 AssertPtrReturn(pcbValue, VERR_INVALID_POINTER);
8508
8509 Medium *that = static_cast<Medium*>(pvUser);
8510 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
8511
8512 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
8513 if (it == that->m->mapProperties.end())
8514 return VERR_CFGM_VALUE_NOT_FOUND;
8515
8516 /* we interpret null values as "no value" in Medium */
8517 if (it->second.isEmpty())
8518 return VERR_CFGM_VALUE_NOT_FOUND;
8519
8520 *pcbValue = it->second.length() + 1 /* include terminator */;
8521
8522 return VINF_SUCCESS;
8523}
8524
8525/* static */
8526DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
8527 const char *pszName,
8528 char *pszValue,
8529 size_t cchValue)
8530{
8531 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
8532
8533 Medium *that = static_cast<Medium*>(pvUser);
8534 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
8535
8536 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
8537 if (it == that->m->mapProperties.end())
8538 return VERR_CFGM_VALUE_NOT_FOUND;
8539
8540 /* we interpret null values as "no value" in Medium */
8541 if (it->second.isEmpty())
8542 return VERR_CFGM_VALUE_NOT_FOUND;
8543
8544 const Utf8Str &value = it->second;
8545 if (value.length() >= cchValue)
8546 return VERR_CFGM_NOT_ENOUGH_SPACE;
8547
8548 memcpy(pszValue, value.c_str(), value.length() + 1);
8549
8550 return VINF_SUCCESS;
8551}
8552
8553DECLCALLBACK(bool) Medium::i_vdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid)
8554{
8555 /* Just return always true here. */
8556 NOREF(pvUser);
8557 NOREF(pszzValid);
8558 return true;
8559}
8560
8561DECLCALLBACK(int) Medium::i_vdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
8562{
8563 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8564 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8565 AssertPtrReturn(pcbValue, VERR_INVALID_POINTER);
8566
8567 size_t cbValue = 0;
8568 if (!strcmp(pszName, "Algorithm"))
8569 cbValue = strlen(pSettings->pszCipher) + 1;
8570 else if (!strcmp(pszName, "KeyId"))
8571 cbValue = sizeof("irrelevant");
8572 else if (!strcmp(pszName, "KeyStore"))
8573 {
8574 if (!pSettings->pszKeyStoreLoad)
8575 return VERR_CFGM_VALUE_NOT_FOUND;
8576 cbValue = strlen(pSettings->pszKeyStoreLoad) + 1;
8577 }
8578 else if (!strcmp(pszName, "CreateKeyStore"))
8579 cbValue = 2; /* Single digit + terminator. */
8580 else
8581 return VERR_CFGM_VALUE_NOT_FOUND;
8582
8583 *pcbValue = cbValue + 1 /* include terminator */;
8584
8585 return VINF_SUCCESS;
8586}
8587
8588DECLCALLBACK(int) Medium::i_vdCryptoConfigQuery(void *pvUser, const char *pszName,
8589 char *pszValue, size_t cchValue)
8590{
8591 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8592 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8593 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
8594
8595 const char *psz = NULL;
8596 if (!strcmp(pszName, "Algorithm"))
8597 psz = pSettings->pszCipher;
8598 else if (!strcmp(pszName, "KeyId"))
8599 psz = "irrelevant";
8600 else if (!strcmp(pszName, "KeyStore"))
8601 psz = pSettings->pszKeyStoreLoad;
8602 else if (!strcmp(pszName, "CreateKeyStore"))
8603 {
8604 if (pSettings->fCreateKeyStore)
8605 psz = "1";
8606 else
8607 psz = "0";
8608 }
8609 else
8610 return VERR_CFGM_VALUE_NOT_FOUND;
8611
8612 size_t cch = strlen(psz);
8613 if (cch >= cchValue)
8614 return VERR_CFGM_NOT_ENOUGH_SPACE;
8615
8616 memcpy(pszValue, psz, cch + 1);
8617 return VINF_SUCCESS;
8618}
8619
8620DECLCALLBACK(int) Medium::i_vdConfigUpdate(void *pvUser,
8621 bool fCreate,
8622 const char *pszName,
8623 const char *pszValue)
8624{
8625 Medium *that = (Medium *)pvUser;
8626
8627 // Detect if this runs inside i_queryInfo() on the current thread.
8628 // Skip if not. Check does not need synchronization.
8629 if (!that->m || !that->m->queryInfoRunning || !that->m->queryInfoSem.isWriteLockOnCurrentThread())
8630 return VINF_SUCCESS;
8631
8632 // It's guaranteed that this code is executing inside Medium::i_queryInfo,
8633 // can assume it took care of synchronization.
8634 int rv = VINF_SUCCESS;
8635 Utf8Str strName(pszName);
8636 settings::StringsMap::const_iterator it = that->m->mapProperties.find(strName);
8637 if (it == that->m->mapProperties.end() && !fCreate)
8638 rv = VERR_CFGM_VALUE_NOT_FOUND;
8639 else
8640 that->m->mapProperties[strName] = Utf8Str(pszValue);
8641 return rv;
8642}
8643
8644DECLCALLBACK(int) Medium::i_vdCryptoKeyRetain(void *pvUser, const char *pszId,
8645 const uint8_t **ppbKey, size_t *pcbKey)
8646{
8647 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8648 NOREF(pszId);
8649 NOREF(ppbKey);
8650 NOREF(pcbKey);
8651 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8652 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8653}
8654
8655DECLCALLBACK(int) Medium::i_vdCryptoKeyRelease(void *pvUser, const char *pszId)
8656{
8657 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8658 NOREF(pszId);
8659 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8660 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8661}
8662
8663DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
8664{
8665 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8666 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8667
8668 NOREF(pszId);
8669 *ppszPassword = pSettings->pszPassword;
8670 return VINF_SUCCESS;
8671}
8672
8673DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
8674{
8675 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8676 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8677 NOREF(pszId);
8678 return VINF_SUCCESS;
8679}
8680
8681DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore)
8682{
8683 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8684 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8685
8686 pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore);
8687 if (!pSettings->pszKeyStore)
8688 return VERR_NO_MEMORY;
8689
8690 memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore);
8691 return VINF_SUCCESS;
8692}
8693
8694DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher,
8695 const uint8_t *pbDek, size_t cbDek)
8696{
8697 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8698 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8699
8700 pSettings->pszCipherReturned = RTStrDup(pszCipher);
8701 pSettings->pbDek = pbDek;
8702 pSettings->cbDek = cbDek;
8703
8704 return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY;
8705}
8706
8707/**
8708 * Creates a VDISK instance for this medium.
8709 *
8710 * @note Caller should not hold any medium related locks as this method will
8711 * acquire the medium lock for writing and others (VirtualBox).
8712 *
8713 * @returns COM status code.
8714 * @param fWritable Whether to return a writable VDISK instance
8715 * (true) or a read-only one (false).
8716 * @param pKeyStore The key store.
8717 * @param ppHdd Where to return the pointer to the VDISK on
8718 * success.
8719 * @param pMediumLockList The lock list to populate and lock. Caller
8720 * is responsible for calling the destructor or
8721 * MediumLockList::Clear() after destroying
8722 * @a *ppHdd
8723 * @param pCryptoSettings The crypto settings to use for setting up
8724 * decryption/encryption of the VDISK. This object
8725 * must be alive until the VDISK is destroyed!
8726 */
8727HRESULT Medium::i_openForIO(bool fWritable, SecretKeyStore *pKeyStore, PVDISK *ppHdd, MediumLockList *pMediumLockList,
8728 MediumCryptoFilterSettings *pCryptoSettings)
8729{
8730 /*
8731 * Create the media lock list and lock the media.
8732 */
8733 HRESULT hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
8734 fWritable ? this : NULL /* pToLockWrite */,
8735 false /* fMediumLockWriteAll */,
8736 NULL,
8737 *pMediumLockList);
8738 if (SUCCEEDED(hrc))
8739 hrc = pMediumLockList->Lock();
8740 if (FAILED(hrc))
8741 return hrc;
8742
8743 /*
8744 * Get the base medium before write locking this medium.
8745 */
8746 ComObjPtr<Medium> pBase = i_getBase();
8747 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8748
8749 /*
8750 * Create the VDISK instance.
8751 */
8752 PVDISK pHdd;
8753 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pHdd);
8754 AssertRCReturn(vrc, E_FAIL);
8755
8756 /*
8757 * Goto avoidance using try/catch/throw(HRESULT).
8758 */
8759 try
8760 {
8761 settings::StringsMap::iterator itKeyStore = pBase->m->mapProperties.find("CRYPT/KeyStore");
8762 if (itKeyStore != pBase->m->mapProperties.end())
8763 {
8764#ifdef VBOX_WITH_EXTPACK
8765 settings::StringsMap::iterator itKeyId = pBase->m->mapProperties.find("CRYPT/KeyId");
8766
8767 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
8768 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
8769 {
8770 /* Load the plugin */
8771 Utf8Str strPlugin;
8772 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
8773 if (SUCCEEDED(hrc))
8774 {
8775 vrc = VDPluginLoadFromFilename(strPlugin.c_str());
8776 if (RT_FAILURE(vrc))
8777 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
8778 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
8779 i_vdError(vrc).c_str());
8780 }
8781 else
8782 throw setError(VBOX_E_NOT_SUPPORTED,
8783 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
8784 ORACLE_PUEL_EXTPACK_NAME);
8785 }
8786 else
8787 throw setError(VBOX_E_NOT_SUPPORTED,
8788 tr("Encryption is not supported because the extension pack '%s' is missing"),
8789 ORACLE_PUEL_EXTPACK_NAME);
8790
8791 if (itKeyId == pBase->m->mapProperties.end())
8792 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8793 tr("Image '%s' is configured for encryption but doesn't has a key identifier set"),
8794 pBase->m->strLocationFull.c_str());
8795
8796 /* Find the proper secret key in the key store. */
8797 if (!pKeyStore)
8798 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8799 tr("Image '%s' is configured for encryption but there is no key store to retrieve the password from"),
8800 pBase->m->strLocationFull.c_str());
8801
8802 SecretKey *pKey = NULL;
8803 vrc = pKeyStore->retainSecretKey(itKeyId->second, &pKey);
8804 if (RT_FAILURE(vrc))
8805 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
8806 tr("Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)"),
8807 itKeyId->second.c_str(), vrc);
8808
8809 i_taskEncryptSettingsSetup(pCryptoSettings, NULL, itKeyStore->second.c_str(), (const char *)pKey->getKeyBuffer(),
8810 false /* fCreateKeyStore */);
8811 vrc = VDFilterAdd(pHdd, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pCryptoSettings->vdFilterIfaces);
8812 pKeyStore->releaseSecretKey(itKeyId->second);
8813 if (vrc == VERR_VD_PASSWORD_INCORRECT)
8814 throw setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc, tr("The password to decrypt the image is incorrect"));
8815 if (RT_FAILURE(vrc))
8816 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc, tr("Failed to load the decryption filter: %s"),
8817 i_vdError(vrc).c_str());
8818#else
8819 RT_NOREF(pKeyStore, pCryptoSettings);
8820 throw setError(VBOX_E_NOT_SUPPORTED,
8821 tr("Encryption is not supported because extension pack support is not built in"));
8822#endif /* VBOX_WITH_EXTPACK */
8823 }
8824
8825 /*
8826 * Open all media in the source chain.
8827 */
8828 MediumLockList::Base::const_iterator sourceListBegin = pMediumLockList->GetBegin();
8829 MediumLockList::Base::const_iterator sourceListEnd = pMediumLockList->GetEnd();
8830 MediumLockList::Base::const_iterator mediumListLast = sourceListEnd;
8831 --mediumListLast;
8832
8833 for (MediumLockList::Base::const_iterator it = sourceListBegin; it != sourceListEnd; ++it)
8834 {
8835 const MediumLock &mediumLock = *it;
8836 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8837 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8838
8839 /* sanity check */
8840 Assert(pMedium->m->state == (fWritable && it == mediumListLast ? MediumState_LockedWrite : MediumState_LockedRead));
8841
8842 /* Open all media in read-only mode. */
8843 vrc = VDOpen(pHdd,
8844 pMedium->m->strFormat.c_str(),
8845 pMedium->m->strLocationFull.c_str(),
8846 m->uOpenFlagsDef | (fWritable && it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
8847 pMedium->m->vdImageIfaces);
8848 if (RT_FAILURE(vrc))
8849 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8850 tr("Could not open the medium storage unit '%s'%s"),
8851 pMedium->m->strLocationFull.c_str(),
8852 i_vdError(vrc).c_str());
8853 }
8854
8855 Assert(m->state == (fWritable ? MediumState_LockedWrite : MediumState_LockedRead));
8856
8857 /*
8858 * Done!
8859 */
8860 *ppHdd = pHdd;
8861 return S_OK;
8862 }
8863 catch (HRESULT hrc2)
8864 {
8865 hrc = hrc2;
8866 }
8867
8868 VDDestroy(pHdd);
8869 return hrc;
8870
8871}
8872
8873/**
8874 * Implementation code for the "create base" task.
8875 *
8876 * This only gets started from Medium::CreateBaseStorage() and always runs
8877 * asynchronously. As a result, we always save the VirtualBox.xml file when
8878 * we're done here.
8879 *
8880 * @param task
8881 * @return
8882 */
8883HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
8884{
8885 /** @todo r=klaus The code below needs to be double checked with regard
8886 * to lock order violations, it probably causes lock order issues related
8887 * to the AutoCaller usage. */
8888 HRESULT hrc = S_OK;
8889
8890 /* these parameters we need after creation */
8891 uint64_t size = 0, logicalSize = 0;
8892 MediumVariant_T variant = MediumVariant_Standard;
8893 bool fGenerateUuid = false;
8894
8895 try
8896 {
8897 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8898
8899 /* The object may request a specific UUID (through a special form of
8900 * the moveTo() argument). Otherwise we have to generate it */
8901 Guid id = m->id;
8902
8903 fGenerateUuid = id.isZero();
8904 if (fGenerateUuid)
8905 {
8906 id.create();
8907 /* VirtualBox::i_registerMedium() will need UUID */
8908 unconst(m->id) = id;
8909 }
8910
8911 Utf8Str format(m->strFormat);
8912 Utf8Str location(m->strLocationFull);
8913 uint64_t capabilities = m->formatObj->i_getCapabilities();
8914 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
8915 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
8916 Assert(m->state == MediumState_Creating);
8917
8918 PVDISK hdd;
8919 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8920 ComAssertRCThrow(vrc, E_FAIL);
8921
8922 /* unlock before the potentially lengthy operation */
8923 thisLock.release();
8924
8925 try
8926 {
8927 /* ensure the directory exists */
8928 if (capabilities & MediumFormatCapabilities_File)
8929 {
8930 hrc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8931 if (FAILED(hrc))
8932 throw hrc;
8933 }
8934
8935 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
8936
8937 vrc = VDCreateBase(hdd,
8938 format.c_str(),
8939 location.c_str(),
8940 task.mSize,
8941 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
8942 NULL,
8943 &geo,
8944 &geo,
8945 id.raw(),
8946 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8947 m->vdImageIfaces,
8948 task.mVDOperationIfaces);
8949 if (RT_FAILURE(vrc))
8950 {
8951 if (vrc == VERR_VD_INVALID_TYPE)
8952 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8953 tr("Parameters for creating the medium storage unit '%s' are invalid%s"),
8954 location.c_str(), i_vdError(vrc).c_str());
8955 else
8956 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8957 tr("Could not create the medium storage unit '%s'%s"),
8958 location.c_str(), i_vdError(vrc).c_str());
8959 }
8960
8961 if (task.mVariant & MediumVariant_Formatted)
8962 {
8963 RTVFSFILE hVfsFile;
8964 vrc = VDCreateVfsFileFromDisk(hdd, 0 /*fFlags*/, &hVfsFile);
8965 if (RT_FAILURE(vrc))
8966 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Opening medium storage unit '%s' failed%s"),
8967 location.c_str(), i_vdError(vrc).c_str());
8968 RTERRINFOSTATIC ErrInfo;
8969 vrc = RTFsFatVolFormat(hVfsFile, 0 /* offVol */, 0 /* cbVol */, RTFSFATVOL_FMT_F_FULL,
8970 0 /* cbSector */, 0 /* cbSectorPerCluster */, RTFSFATTYPE_INVALID,
8971 0 /* cHeads */, 0 /* cSectorsPerTrack*/, 0 /* bMedia */,
8972 0 /* cRootDirEntries */, 0 /* cHiddenSectors */,
8973 RTErrInfoInitStatic(&ErrInfo));
8974 RTVfsFileRelease(hVfsFile);
8975 if (RT_FAILURE(vrc) && RTErrInfoIsSet(&ErrInfo.Core))
8976 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed: %s"),
8977 location.c_str(), ErrInfo.Core.pszMsg);
8978 if (RT_FAILURE(vrc))
8979 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed%s"),
8980 location.c_str(), i_vdError(vrc).c_str());
8981 }
8982
8983 size = VDGetFileSize(hdd, 0);
8984 logicalSize = VDGetSize(hdd, 0);
8985 unsigned uImageFlags;
8986 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8987 if (RT_SUCCESS(vrc))
8988 variant = (MediumVariant_T)uImageFlags;
8989 }
8990 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
8991
8992 VDDestroy(hdd);
8993 }
8994 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
8995
8996 if (SUCCEEDED(hrc))
8997 {
8998 /* register with mVirtualBox as the last step and move to
8999 * Created state only on success (leaving an orphan file is
9000 * better than breaking media registry consistency) */
9001 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9002 ComObjPtr<Medium> pMedium;
9003 hrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
9004 Assert(pMedium == NULL || this == pMedium);
9005 }
9006
9007 // re-acquire the lock before changing state
9008 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9009
9010 if (SUCCEEDED(hrc))
9011 {
9012 m->state = MediumState_Created;
9013
9014 m->size = size;
9015 m->logicalSize = logicalSize;
9016 m->variant = variant;
9017
9018 thisLock.release();
9019 i_markRegistriesModified();
9020 if (task.isAsync())
9021 {
9022 // in asynchronous mode, save settings now
9023 m->pVirtualBox->i_saveModifiedRegistries();
9024 }
9025 }
9026 else
9027 {
9028 /* back to NotCreated on failure */
9029 m->state = MediumState_NotCreated;
9030
9031 /* reset UUID to prevent it from being reused next time */
9032 if (fGenerateUuid)
9033 unconst(m->id).clear();
9034 }
9035
9036 if (task.NotifyAboutChanges() && SUCCEEDED(hrc))
9037 {
9038 m->pVirtualBox->i_onMediumConfigChanged(this);
9039 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
9040 }
9041
9042 return hrc;
9043}
9044
9045/**
9046 * Implementation code for the "create diff" task.
9047 *
9048 * This task always gets started from Medium::createDiffStorage() and can run
9049 * synchronously or asynchronously depending on the "wait" parameter passed to
9050 * that function. If we run synchronously, the caller expects the medium
9051 * registry modification to be set before returning; otherwise (in asynchronous
9052 * mode), we save the settings ourselves.
9053 *
9054 * @param task
9055 * @return
9056 */
9057HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
9058{
9059 /** @todo r=klaus The code below needs to be double checked with regard
9060 * to lock order violations, it probably causes lock order issues related
9061 * to the AutoCaller usage. */
9062 HRESULT hrcTmp = S_OK;
9063
9064 const ComObjPtr<Medium> &pTarget = task.mTarget;
9065
9066 uint64_t size = 0, logicalSize = 0;
9067 MediumVariant_T variant = MediumVariant_Standard;
9068 bool fGenerateUuid = false;
9069
9070 try
9071 {
9072 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9073 {
9074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9075 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9076 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"),
9077 m->strLocationFull.c_str());
9078 }
9079
9080 /* Lock both in {parent,child} order. */
9081 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
9082
9083 /* The object may request a specific UUID (through a special form of
9084 * the moveTo() argument). Otherwise we have to generate it */
9085 Guid targetId = pTarget->m->id;
9086
9087 fGenerateUuid = targetId.isZero();
9088 if (fGenerateUuid)
9089 {
9090 targetId.create();
9091 /* VirtualBox::i_registerMedium() will need UUID */
9092 unconst(pTarget->m->id) = targetId;
9093 }
9094
9095 Guid id = m->id;
9096
9097 Utf8Str targetFormat(pTarget->m->strFormat);
9098 Utf8Str targetLocation(pTarget->m->strLocationFull);
9099 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
9100 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
9101
9102 Assert(pTarget->m->state == MediumState_Creating);
9103 Assert(m->state == MediumState_LockedRead);
9104
9105 PVDISK hdd;
9106 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9107 ComAssertRCThrow(vrc, E_FAIL);
9108
9109 /* the two media are now protected by their non-default states;
9110 * unlock the media before the potentially lengthy operation */
9111 mediaLock.release();
9112
9113 try
9114 {
9115 /* Open all media in the target chain but the last. */
9116 MediumLockList::Base::const_iterator targetListBegin =
9117 task.mpMediumLockList->GetBegin();
9118 MediumLockList::Base::const_iterator targetListEnd =
9119 task.mpMediumLockList->GetEnd();
9120 for (MediumLockList::Base::const_iterator it = targetListBegin;
9121 it != targetListEnd;
9122 ++it)
9123 {
9124 const MediumLock &mediumLock = *it;
9125 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9126
9127 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9128
9129 /* Skip over the target diff medium */
9130 if (pMedium->m->state == MediumState_Creating)
9131 continue;
9132
9133 /* sanity check */
9134 Assert(pMedium->m->state == MediumState_LockedRead);
9135
9136 /* Open all media in appropriate mode. */
9137 vrc = VDOpen(hdd,
9138 pMedium->m->strFormat.c_str(),
9139 pMedium->m->strLocationFull.c_str(),
9140 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9141 pMedium->m->vdImageIfaces);
9142 if (RT_FAILURE(vrc))
9143 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9144 tr("Could not open the medium storage unit '%s'%s"),
9145 pMedium->m->strLocationFull.c_str(),
9146 i_vdError(vrc).c_str());
9147 }
9148
9149 /* ensure the target directory exists */
9150 if (capabilities & MediumFormatCapabilities_File)
9151 {
9152 HRESULT hrc = VirtualBox::i_ensureFilePathExists(targetLocation,
9153 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9154 if (FAILED(hrc))
9155 throw hrc;
9156 }
9157
9158 vrc = VDCreateDiff(hdd,
9159 targetFormat.c_str(),
9160 targetLocation.c_str(),
9161 (task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk))
9162 | VD_IMAGE_FLAGS_DIFF,
9163 NULL,
9164 targetId.raw(),
9165 id.raw(),
9166 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9167 pTarget->m->vdImageIfaces,
9168 task.mVDOperationIfaces);
9169 if (RT_FAILURE(vrc))
9170 {
9171 if (vrc == VERR_VD_INVALID_TYPE)
9172 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9173 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
9174 targetLocation.c_str(), i_vdError(vrc).c_str());
9175 else
9176 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9177 tr("Could not create the differencing medium storage unit '%s'%s"),
9178 targetLocation.c_str(), i_vdError(vrc).c_str());
9179 }
9180
9181 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9182 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9183 unsigned uImageFlags;
9184 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9185 if (RT_SUCCESS(vrc))
9186 variant = (MediumVariant_T)uImageFlags;
9187 }
9188 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9189
9190 VDDestroy(hdd);
9191 }
9192 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9193
9194 MultiResult mrc(hrcTmp);
9195
9196 if (SUCCEEDED(mrc))
9197 {
9198 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9199
9200 Assert(pTarget->m->pParent.isNull());
9201
9202 /* associate child with the parent, maximum depth was checked above */
9203 pTarget->i_setParent(this);
9204
9205 /* diffs for immutable media are auto-reset by default */
9206 bool fAutoReset;
9207 {
9208 ComObjPtr<Medium> pBase = i_getBase();
9209 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
9210 fAutoReset = (pBase->m->type == MediumType_Immutable);
9211 }
9212 {
9213 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
9214 pTarget->m->autoReset = fAutoReset;
9215 }
9216
9217 /* register with mVirtualBox as the last step and move to
9218 * Created state only on success (leaving an orphan file is
9219 * better than breaking media registry consistency) */
9220 ComObjPtr<Medium> pMedium;
9221 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
9222 Assert(pTarget == pMedium);
9223
9224 if (FAILED(mrc))
9225 /* break the parent association on failure to register */
9226 i_deparent();
9227 }
9228
9229 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
9230
9231 if (SUCCEEDED(mrc))
9232 {
9233 pTarget->m->state = MediumState_Created;
9234
9235 pTarget->m->size = size;
9236 pTarget->m->logicalSize = logicalSize;
9237 pTarget->m->variant = variant;
9238 }
9239 else
9240 {
9241 /* back to NotCreated on failure */
9242 pTarget->m->state = MediumState_NotCreated;
9243
9244 pTarget->m->autoReset = false;
9245
9246 /* reset UUID to prevent it from being reused next time */
9247 if (fGenerateUuid)
9248 unconst(pTarget->m->id).clear();
9249 }
9250
9251 // deregister the task registered in createDiffStorage()
9252 Assert(m->numCreateDiffTasks != 0);
9253 --m->numCreateDiffTasks;
9254
9255 mediaLock.release();
9256 i_markRegistriesModified();
9257 if (task.isAsync())
9258 {
9259 // in asynchronous mode, save settings now
9260 m->pVirtualBox->i_saveModifiedRegistries();
9261 }
9262
9263 /* Note that in sync mode, it's the caller's responsibility to
9264 * unlock the medium. */
9265
9266 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
9267 {
9268 m->pVirtualBox->i_onMediumConfigChanged(this);
9269 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
9270 }
9271
9272 return mrc;
9273}
9274
9275/**
9276 * Implementation code for the "merge" task.
9277 *
9278 * This task always gets started from Medium::mergeTo() and can run
9279 * synchronously or asynchronously depending on the "wait" parameter passed to
9280 * that function. If we run synchronously, the caller expects the medium
9281 * registry modification to be set before returning; otherwise (in asynchronous
9282 * mode), we save the settings ourselves.
9283 *
9284 * @param task
9285 * @return
9286 */
9287HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
9288{
9289 /** @todo r=klaus The code below needs to be double checked with regard
9290 * to lock order violations, it probably causes lock order issues related
9291 * to the AutoCaller usage. */
9292 HRESULT hrcTmp = S_OK;
9293
9294 const ComObjPtr<Medium> &pTarget = task.mTarget;
9295
9296 try
9297 {
9298 if (!task.mParentForTarget.isNull())
9299 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9300 {
9301 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
9302 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9303 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9304 task.mParentForTarget->m->strLocationFull.c_str());
9305 }
9306
9307 // Resize target to source size, if possible. Otherwise throw an error.
9308 // It's offline resizing. Online resizing will be called in the
9309 // SessionMachine::onlineMergeMedium.
9310
9311 uint64_t sourceSize = 0;
9312 Utf8Str sourceName;
9313 {
9314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9315 sourceSize = i_getLogicalSize();
9316 sourceName = i_getName();
9317 }
9318 uint64_t targetSize = 0;
9319 Utf8Str targetName;
9320 {
9321 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
9322 targetSize = pTarget->i_getLogicalSize();
9323 targetName = pTarget->i_getName();
9324 }
9325
9326 //reducing vm disks are not implemented yet
9327 if (sourceSize > targetSize)
9328 {
9329 if (i_isMediumFormatFile())
9330 {
9331 /// @todo r=klaus Can this use the standard code for creating a medium lock list?
9332 // Have to make own lock list, because "resize" method resizes the last image
9333 // in the lock chain only. The lock chain is already in the task.mpMediumLockList,
9334 // so just make new lock list based on it, with the right last medium. The own
9335 // lock list skips double locking and therefore does not affect the general lock
9336 // state after the "resize" method.
9337 MediumLockList* pMediumLockListForResize = new MediumLockList();
9338
9339 for (MediumLockList::Base::iterator it = task.mpMediumLockList->GetBegin();
9340 it != task.mpMediumLockList->GetEnd();
9341 ++it)
9342 {
9343 ComObjPtr<Medium> pMedium = it->GetMedium();
9344 pMediumLockListForResize->Append(pMedium, pMedium->m->state == MediumState_LockedWrite);
9345 if (pMedium == pTarget)
9346 break;
9347 }
9348
9349 // just to switch internal state of the lock list to avoid errors during list deletion,
9350 // because all media in the list already locked by task.mpMediumLockList
9351 HRESULT hrc = pMediumLockListForResize->Lock(true /* fSkipOverLockedMedia */);
9352 if (FAILED(hrc))
9353 {
9354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9355 delete pMediumLockListForResize;
9356 throw setError(hrc,
9357 tr("Failed to lock the medium '%s' to resize before merge"),
9358 targetName.c_str());
9359 }
9360
9361 ComObjPtr<Progress> pProgress(task.GetProgressObject());
9362 hrc = pTarget->i_resize(sourceSize, pMediumLockListForResize, &pProgress, true, false);
9363 if (FAILED(hrc))
9364 {
9365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9366 delete pMediumLockListForResize;
9367 throw setError(hrc,
9368 tr("Failed to set size of '%s' to size of '%s'"),
9369 targetName.c_str(), sourceName.c_str());
9370 }
9371 delete pMediumLockListForResize;
9372 }
9373 else
9374 {
9375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9376 throw setError(VBOX_E_NOT_SUPPORTED,
9377 tr("Sizes of '%s' and '%s' are different and medium format does not support resing"),
9378 sourceName.c_str(), targetName.c_str());
9379 }
9380 }
9381
9382 task.GetProgressObject()->SetNextOperation(BstrFmt(tr("Merging medium '%s' to '%s'"),
9383 i_getName().c_str(),
9384 targetName.c_str()).raw(),
9385 1);
9386
9387 PVDISK hdd;
9388 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9389 ComAssertRCThrow(vrc, E_FAIL);
9390
9391 try
9392 {
9393 // Similar code appears in SessionMachine::onlineMergeMedium, so
9394 // if you make any changes below check whether they are applicable
9395 // in that context as well.
9396
9397 unsigned uTargetIdx = VD_LAST_IMAGE;
9398 unsigned uSourceIdx = VD_LAST_IMAGE;
9399 /* Open all media in the chain. */
9400 MediumLockList::Base::iterator lockListBegin =
9401 task.mpMediumLockList->GetBegin();
9402 MediumLockList::Base::iterator lockListEnd =
9403 task.mpMediumLockList->GetEnd();
9404 unsigned i = 0;
9405 for (MediumLockList::Base::iterator it = lockListBegin;
9406 it != lockListEnd;
9407 ++it)
9408 {
9409 MediumLock &mediumLock = *it;
9410 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9411
9412 if (pMedium == this)
9413 uSourceIdx = i;
9414 else if (pMedium == pTarget)
9415 uTargetIdx = i;
9416
9417 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9418
9419 /*
9420 * complex sanity (sane complexity)
9421 *
9422 * The current medium must be in the Deleting (medium is merged)
9423 * or LockedRead (parent medium) state if it is not the target.
9424 * If it is the target it must be in the LockedWrite state.
9425 */
9426 Assert( ( pMedium != pTarget
9427 && ( pMedium->m->state == MediumState_Deleting
9428 || pMedium->m->state == MediumState_LockedRead))
9429 || ( pMedium == pTarget
9430 && pMedium->m->state == MediumState_LockedWrite));
9431 /*
9432 * Medium must be the target, in the LockedRead state
9433 * or Deleting state where it is not allowed to be attached
9434 * to a virtual machine.
9435 */
9436 Assert( pMedium == pTarget
9437 || pMedium->m->state == MediumState_LockedRead
9438 || ( pMedium->m->backRefs.size() == 0
9439 && pMedium->m->state == MediumState_Deleting));
9440 /* The source medium must be in Deleting state. */
9441 Assert( pMedium != this
9442 || pMedium->m->state == MediumState_Deleting);
9443
9444 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9445
9446 if ( pMedium->m->state == MediumState_LockedRead
9447 || pMedium->m->state == MediumState_Deleting)
9448 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9449 if (pMedium->m->type == MediumType_Shareable)
9450 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9451
9452 /* Open the medium */
9453 vrc = VDOpen(hdd,
9454 pMedium->m->strFormat.c_str(),
9455 pMedium->m->strLocationFull.c_str(),
9456 uOpenFlags | m->uOpenFlagsDef,
9457 pMedium->m->vdImageIfaces);
9458 if (RT_FAILURE(vrc))
9459 throw vrc;
9460
9461 i++;
9462 }
9463
9464 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
9465 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
9466
9467 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
9468 task.mVDOperationIfaces);
9469 if (RT_FAILURE(vrc))
9470 throw vrc;
9471
9472 /* update parent UUIDs */
9473 if (!task.mfMergeForward)
9474 {
9475 /* we need to update UUIDs of all source's children
9476 * which cannot be part of the container at once so
9477 * add each one in there individually */
9478 if (task.mpChildrenToReparent)
9479 {
9480 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9481 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9482 for (MediumLockList::Base::iterator it = childrenBegin;
9483 it != childrenEnd;
9484 ++it)
9485 {
9486 Medium *pMedium = it->GetMedium();
9487 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
9488 vrc = VDOpen(hdd,
9489 pMedium->m->strFormat.c_str(),
9490 pMedium->m->strLocationFull.c_str(),
9491 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9492 pMedium->m->vdImageIfaces);
9493 if (RT_FAILURE(vrc))
9494 throw vrc;
9495
9496 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
9497 pTarget->m->id.raw());
9498 if (RT_FAILURE(vrc))
9499 throw vrc;
9500
9501 vrc = VDClose(hdd, false /* fDelete */);
9502 if (RT_FAILURE(vrc))
9503 throw vrc;
9504 }
9505 }
9506 }
9507 }
9508 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9509 catch (int aVRC)
9510 {
9511 hrcTmp = setErrorBoth(VBOX_E_FILE_ERROR, aVRC,
9512 tr("Could not merge the medium '%s' to '%s'%s"),
9513 m->strLocationFull.c_str(),
9514 pTarget->m->strLocationFull.c_str(),
9515 i_vdError(aVRC).c_str());
9516 }
9517
9518 VDDestroy(hdd);
9519 }
9520 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9521
9522 ErrorInfoKeeper eik;
9523 MultiResult mrc(hrcTmp);
9524 HRESULT hrc2;
9525
9526 std::set<ComObjPtr<Medium> > pMediaForNotify;
9527 std::map<Guid, DeviceType_T> uIdsForNotify;
9528
9529 if (SUCCEEDED(mrc))
9530 {
9531 /* all media but the target were successfully deleted by
9532 * VDMerge; reparent the last one and uninitialize deleted media. */
9533
9534 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9535
9536 if (task.mfMergeForward)
9537 {
9538 /* first, unregister the target since it may become a base
9539 * medium which needs re-registration */
9540 hrc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
9541 AssertComRC(hrc2);
9542
9543 /* then, reparent it and disconnect the deleted branch at both ends
9544 * (chain->parent() is source's parent). Depth check above. */
9545 pTarget->i_deparent();
9546 pTarget->i_setParent(task.mParentForTarget);
9547 if (task.mParentForTarget)
9548 {
9549 i_deparent();
9550 if (task.NotifyAboutChanges())
9551 pMediaForNotify.insert(task.mParentForTarget);
9552 }
9553
9554 /* then, register again */
9555 ComObjPtr<Medium> pMedium;
9556 hrc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
9557 AssertComRC(hrc2);
9558 }
9559 else
9560 {
9561 Assert(pTarget->i_getChildren().size() == 1);
9562 Medium *targetChild = pTarget->i_getChildren().front();
9563
9564 /* disconnect the deleted branch at the elder end */
9565 targetChild->i_deparent();
9566
9567 /* reparent source's children and disconnect the deleted
9568 * branch at the younger end */
9569 if (task.mpChildrenToReparent)
9570 {
9571 /* obey {parent,child} lock order */
9572 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
9573
9574 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9575 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9576 for (MediumLockList::Base::iterator it = childrenBegin;
9577 it != childrenEnd;
9578 ++it)
9579 {
9580 Medium *pMedium = it->GetMedium();
9581 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
9582
9583 pMedium->i_deparent(); // removes pMedium from source
9584 // no depth check, reduces depth
9585 pMedium->i_setParent(pTarget);
9586
9587 if (task.NotifyAboutChanges())
9588 pMediaForNotify.insert(pMedium);
9589 }
9590 }
9591 pMediaForNotify.insert(pTarget);
9592 }
9593
9594 /* unregister and uninitialize all media removed by the merge */
9595 MediumLockList::Base::iterator lockListBegin =
9596 task.mpMediumLockList->GetBegin();
9597 MediumLockList::Base::iterator lockListEnd =
9598 task.mpMediumLockList->GetEnd();
9599 for (MediumLockList::Base::iterator it = lockListBegin;
9600 it != lockListEnd;
9601 )
9602 {
9603 MediumLock &mediumLock = *it;
9604 /* Create a real copy of the medium pointer, as the medium
9605 * lock deletion below would invalidate the referenced object. */
9606 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
9607
9608 /* The target and all media not merged (readonly) are skipped */
9609 if ( pMedium == pTarget
9610 || pMedium->m->state == MediumState_LockedRead)
9611 {
9612 ++it;
9613 continue;
9614 }
9615
9616 uIdsForNotify[pMedium->i_getId()] = pMedium->i_getDeviceType();
9617 hrc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
9618 AssertComRC(hrc2);
9619
9620 /* now, uninitialize the deleted medium (note that
9621 * due to the Deleting state, uninit() will not touch
9622 * the parent-child relationship so we need to
9623 * uninitialize each disk individually) */
9624
9625 /* note that the operation initiator medium (which is
9626 * normally also the source medium) is a special case
9627 * -- there is one more caller added by Task to it which
9628 * we must release. Also, if we are in sync mode, the
9629 * caller may still hold an AutoCaller instance for it
9630 * and therefore we cannot uninit() it (it's therefore
9631 * the caller's responsibility) */
9632 if (pMedium == this)
9633 {
9634 Assert(i_getChildren().size() == 0);
9635 Assert(m->backRefs.size() == 0);
9636 task.mMediumCaller.release();
9637 }
9638
9639 /* Delete the medium lock list entry, which also releases the
9640 * caller added by MergeChain before uninit() and updates the
9641 * iterator to point to the right place. */
9642 hrc2 = task.mpMediumLockList->RemoveByIterator(it);
9643 AssertComRC(hrc2);
9644
9645 if (task.isAsync() || pMedium != this)
9646 {
9647 treeLock.release();
9648 pMedium->uninit();
9649 treeLock.acquire();
9650 }
9651 }
9652 }
9653
9654 i_markRegistriesModified();
9655 if (task.isAsync())
9656 {
9657 // in asynchronous mode, save settings now
9658 eik.restore();
9659 m->pVirtualBox->i_saveModifiedRegistries();
9660 eik.fetch();
9661 }
9662
9663 if (FAILED(mrc))
9664 {
9665 /* Here we come if either VDMerge() failed (in which case we
9666 * assume that it tried to do everything to make a further
9667 * retry possible -- e.g. not deleted intermediate media
9668 * and so on) or VirtualBox::saveRegistries() failed (where we
9669 * should have the original tree but with intermediate storage
9670 * units deleted by VDMerge()). We have to only restore states
9671 * (through the MergeChain dtor) unless we are run synchronously
9672 * in which case it's the responsibility of the caller as stated
9673 * in the mergeTo() docs. The latter also implies that we
9674 * don't own the merge chain, so release it in this case. */
9675 if (task.isAsync())
9676 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
9677 }
9678 else if (task.NotifyAboutChanges())
9679 {
9680 for (std::set<ComObjPtr<Medium> >::const_iterator it = pMediaForNotify.begin();
9681 it != pMediaForNotify.end();
9682 ++it)
9683 {
9684 if (it->isNotNull())
9685 m->pVirtualBox->i_onMediumConfigChanged(*it);
9686 }
9687 for (std::map<Guid, DeviceType_T>::const_iterator it = uIdsForNotify.begin();
9688 it != uIdsForNotify.end();
9689 ++it)
9690 {
9691 m->pVirtualBox->i_onMediumRegistered(it->first, it->second, FALSE);
9692 }
9693 }
9694
9695 return mrc;
9696}
9697
9698/**
9699 * Implementation code for the "clone" task.
9700 *
9701 * This only gets started from Medium::CloneTo() and always runs asynchronously.
9702 * As a result, we always save the VirtualBox.xml file when we're done here.
9703 *
9704 * @param task
9705 * @return
9706 */
9707HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
9708{
9709 /** @todo r=klaus The code below needs to be double checked with regard
9710 * to lock order violations, it probably causes lock order issues related
9711 * to the AutoCaller usage. */
9712 HRESULT hrcTmp = S_OK;
9713
9714 const ComObjPtr<Medium> &pTarget = task.mTarget;
9715 const ComObjPtr<Medium> &pParent = task.mParent;
9716
9717 bool fCreatingTarget = false;
9718
9719 uint64_t size = 0, logicalSize = 0;
9720 MediumVariant_T variant = MediumVariant_Standard;
9721 bool fGenerateUuid = false;
9722
9723 try
9724 {
9725 if (!pParent.isNull())
9726 {
9727
9728 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9729 {
9730 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
9731 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9732 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9733 pParent->m->strLocationFull.c_str());
9734 }
9735 }
9736
9737 /* Lock all in {parent,child} order. The lock is also used as a
9738 * signal from the task initiator (which releases it only after
9739 * RTThreadCreate()) that we can start the job. */
9740 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
9741
9742 fCreatingTarget = pTarget->m->state == MediumState_Creating;
9743
9744 /* The object may request a specific UUID (through a special form of
9745 * the moveTo() argument). Otherwise we have to generate it */
9746 Guid targetId = pTarget->m->id;
9747
9748 fGenerateUuid = targetId.isZero();
9749 if (fGenerateUuid)
9750 {
9751 targetId.create();
9752 /* VirtualBox::registerMedium() will need UUID */
9753 unconst(pTarget->m->id) = targetId;
9754 }
9755
9756 PVDISK hdd;
9757 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9758 ComAssertRCThrow(vrc, E_FAIL);
9759
9760 try
9761 {
9762 /* Open all media in the source chain. */
9763 MediumLockList::Base::const_iterator sourceListBegin =
9764 task.mpSourceMediumLockList->GetBegin();
9765 MediumLockList::Base::const_iterator sourceListEnd =
9766 task.mpSourceMediumLockList->GetEnd();
9767 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9768 it != sourceListEnd;
9769 ++it)
9770 {
9771 const MediumLock &mediumLock = *it;
9772 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9773 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9774
9775 /* sanity check */
9776 Assert(pMedium->m->state == MediumState_LockedRead);
9777
9778 /** Open all media in read-only mode. */
9779 vrc = VDOpen(hdd,
9780 pMedium->m->strFormat.c_str(),
9781 pMedium->m->strLocationFull.c_str(),
9782 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9783 pMedium->m->vdImageIfaces);
9784 if (RT_FAILURE(vrc))
9785 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9786 tr("Could not open the medium storage unit '%s'%s"),
9787 pMedium->m->strLocationFull.c_str(),
9788 i_vdError(vrc).c_str());
9789 }
9790
9791 Utf8Str targetFormat(pTarget->m->strFormat);
9792 Utf8Str targetLocation(pTarget->m->strLocationFull);
9793 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
9794
9795 Assert( pTarget->m->state == MediumState_Creating
9796 || pTarget->m->state == MediumState_LockedWrite);
9797 Assert(m->state == MediumState_LockedRead);
9798 Assert( pParent.isNull()
9799 || pParent->m->state == MediumState_LockedRead);
9800
9801 /* unlock before the potentially lengthy operation */
9802 thisLock.release();
9803
9804 /* ensure the target directory exists */
9805 if (capabilities & MediumFormatCapabilities_File)
9806 {
9807 HRESULT hrc = VirtualBox::i_ensureFilePathExists(targetLocation,
9808 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9809 if (FAILED(hrc))
9810 throw hrc;
9811 }
9812
9813 PVDISK targetHdd;
9814 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9815 ComAssertRCThrow(vrc, E_FAIL);
9816
9817 try
9818 {
9819 /* Open all media in the target chain. */
9820 MediumLockList::Base::const_iterator targetListBegin =
9821 task.mpTargetMediumLockList->GetBegin();
9822 MediumLockList::Base::const_iterator targetListEnd =
9823 task.mpTargetMediumLockList->GetEnd();
9824 for (MediumLockList::Base::const_iterator it = targetListBegin;
9825 it != targetListEnd;
9826 ++it)
9827 {
9828 const MediumLock &mediumLock = *it;
9829 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9830
9831 /* If the target medium is not created yet there's no
9832 * reason to open it. */
9833 if (pMedium == pTarget && fCreatingTarget)
9834 continue;
9835
9836 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9837
9838 /* sanity check */
9839 Assert( pMedium->m->state == MediumState_LockedRead
9840 || pMedium->m->state == MediumState_LockedWrite);
9841
9842 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9843 if (pMedium->m->state != MediumState_LockedWrite)
9844 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9845 if (pMedium->m->type == MediumType_Shareable)
9846 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9847
9848 /* Open all media in appropriate mode. */
9849 vrc = VDOpen(targetHdd,
9850 pMedium->m->strFormat.c_str(),
9851 pMedium->m->strLocationFull.c_str(),
9852 uOpenFlags | m->uOpenFlagsDef,
9853 pMedium->m->vdImageIfaces);
9854 if (RT_FAILURE(vrc))
9855 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9856 tr("Could not open the medium storage unit '%s'%s"),
9857 pMedium->m->strLocationFull.c_str(),
9858 i_vdError(vrc).c_str());
9859 }
9860
9861 /* target isn't locked, but no changing data is accessed */
9862 if (task.midxSrcImageSame == UINT32_MAX)
9863 {
9864 vrc = VDCopy(hdd,
9865 VD_LAST_IMAGE,
9866 targetHdd,
9867 targetFormat.c_str(),
9868 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9869 false /* fMoveByRename */,
9870 task.mTargetLogicalSize /* cbSize */,
9871 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk),
9872 targetId.raw(),
9873 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9874 NULL /* pVDIfsOperation */,
9875 pTarget->m->vdImageIfaces,
9876 task.mVDOperationIfaces);
9877 }
9878 else
9879 {
9880 vrc = VDCopyEx(hdd,
9881 VD_LAST_IMAGE,
9882 targetHdd,
9883 targetFormat.c_str(),
9884 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9885 false /* fMoveByRename */,
9886 task.mTargetLogicalSize /* cbSize */,
9887 task.midxSrcImageSame,
9888 task.midxDstImageSame,
9889 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk),
9890 targetId.raw(),
9891 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9892 NULL /* pVDIfsOperation */,
9893 pTarget->m->vdImageIfaces,
9894 task.mVDOperationIfaces);
9895 }
9896 if (RT_FAILURE(vrc))
9897 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9898 tr("Could not create the clone medium '%s'%s"),
9899 targetLocation.c_str(), i_vdError(vrc).c_str());
9900
9901 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
9902 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
9903 unsigned uImageFlags;
9904 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
9905 if (RT_SUCCESS(vrc))
9906 variant = (MediumVariant_T)uImageFlags;
9907 }
9908 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9909
9910 VDDestroy(targetHdd);
9911 }
9912 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9913
9914 VDDestroy(hdd);
9915 }
9916 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9917
9918 ErrorInfoKeeper eik;
9919 MultiResult mrc(hrcTmp);
9920
9921 /* Only do the parent changes for newly created media. */
9922 if (SUCCEEDED(mrc) && fCreatingTarget)
9923 {
9924 /* we set m->pParent & children() */
9925 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9926
9927 Assert(pTarget->m->pParent.isNull());
9928
9929 if (pParent)
9930 {
9931 /* Associate the clone with the parent and deassociate
9932 * from VirtualBox. Depth check above. */
9933 pTarget->i_setParent(pParent);
9934
9935 /* register with mVirtualBox as the last step and move to
9936 * Created state only on success (leaving an orphan file is
9937 * better than breaking media registry consistency) */
9938 eik.restore();
9939 ComObjPtr<Medium> pMedium;
9940 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9941 treeLock);
9942 Assert( FAILED(mrc)
9943 || pTarget == pMedium);
9944 eik.fetch();
9945
9946 if (FAILED(mrc))
9947 /* break parent association on failure to register */
9948 pTarget->i_deparent(); // removes target from parent
9949 }
9950 else
9951 {
9952 /* just register */
9953 eik.restore();
9954 ComObjPtr<Medium> pMedium;
9955 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9956 treeLock);
9957 Assert( FAILED(mrc)
9958 || pTarget == pMedium);
9959 eik.fetch();
9960 }
9961 }
9962
9963 if (fCreatingTarget)
9964 {
9965 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
9966
9967 if (SUCCEEDED(mrc))
9968 {
9969 pTarget->m->state = MediumState_Created;
9970
9971 pTarget->m->size = size;
9972 pTarget->m->logicalSize = logicalSize;
9973 pTarget->m->variant = variant;
9974 }
9975 else
9976 {
9977 /* back to NotCreated on failure */
9978 pTarget->m->state = MediumState_NotCreated;
9979
9980 /* reset UUID to prevent it from being reused next time */
9981 if (fGenerateUuid)
9982 unconst(pTarget->m->id).clear();
9983 }
9984 }
9985
9986 /* Copy any filter related settings over to the target. */
9987 if (SUCCEEDED(mrc))
9988 {
9989 /* Copy any filter related settings over. */
9990 ComObjPtr<Medium> pBase = i_getBase();
9991 ComObjPtr<Medium> pTargetBase = pTarget->i_getBase();
9992 std::vector<com::Utf8Str> aFilterPropNames;
9993 std::vector<com::Utf8Str> aFilterPropValues;
9994 mrc = pBase->i_getFilterProperties(aFilterPropNames, aFilterPropValues);
9995 if (SUCCEEDED(mrc))
9996 {
9997 /* Go through the properties and add them to the target medium. */
9998 for (unsigned idx = 0; idx < aFilterPropNames.size(); idx++)
9999 {
10000 mrc = pTargetBase->i_setPropertyDirect(aFilterPropNames[idx], aFilterPropValues[idx]);
10001 if (FAILED(mrc)) break;
10002 }
10003
10004 // now, at the end of this task (always asynchronous), save the settings
10005 if (SUCCEEDED(mrc))
10006 {
10007 // save the settings
10008 i_markRegistriesModified();
10009 /* collect multiple errors */
10010 eik.restore();
10011 m->pVirtualBox->i_saveModifiedRegistries();
10012 eik.fetch();
10013
10014 if (task.NotifyAboutChanges())
10015 {
10016 if (!fCreatingTarget)
10017 {
10018 if (!aFilterPropNames.empty())
10019 m->pVirtualBox->i_onMediumConfigChanged(pTargetBase);
10020 if (pParent)
10021 m->pVirtualBox->i_onMediumConfigChanged(pParent);
10022 }
10023 else
10024 {
10025 m->pVirtualBox->i_onMediumRegistered(pTarget->i_getId(), pTarget->i_getDeviceType(), TRUE);
10026 }
10027 }
10028 }
10029 }
10030 }
10031
10032 /* Everything is explicitly unlocked when the task exits,
10033 * as the task destruction also destroys the source chain. */
10034
10035 /* Make sure the source chain is released early. It could happen
10036 * that we get a deadlock in Appliance::Import when Medium::Close
10037 * is called & the source chain is released at the same time. */
10038 task.mpSourceMediumLockList->Clear();
10039
10040 return mrc;
10041}
10042
10043/**
10044 * Implementation code for the "move" task.
10045 *
10046 * This only gets started from Medium::MoveTo() and always
10047 * runs asynchronously.
10048 *
10049 * @param task
10050 * @return
10051 */
10052HRESULT Medium::i_taskMoveHandler(Medium::MoveTask &task)
10053{
10054 LogFlowFuncEnter();
10055 HRESULT hrcOut = S_OK;
10056
10057 /* pTarget is equal "this" in our case */
10058 const ComObjPtr<Medium> &pTarget = task.mMedium;
10059
10060 uint64_t size = 0; NOREF(size);
10061 uint64_t logicalSize = 0; NOREF(logicalSize);
10062 MediumVariant_T variant = MediumVariant_Standard; NOREF(variant);
10063
10064 /*
10065 * it's exactly moving, not cloning
10066 */
10067 if (!i_isMoveOperation(pTarget))
10068 {
10069 LogFlowFunc(("LEAVE: hrc=VBOX_E_FILE_ERROR (early)\n"));
10070 return setError(VBOX_E_FILE_ERROR,
10071 tr("Wrong preconditions for moving the medium %s"),
10072 pTarget->m->strLocationFull.c_str());
10073 }
10074
10075 try
10076 {
10077 /* Lock all in {parent,child} order. The lock is also used as a
10078 * signal from the task initiator (which releases it only after
10079 * RTThreadCreate()) that we can start the job. */
10080
10081 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10082
10083 PVDISK hdd;
10084 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10085 ComAssertRCThrow(vrc, E_FAIL);
10086
10087 try
10088 {
10089 /* Open all media in the source chain. */
10090 MediumLockList::Base::const_iterator sourceListBegin =
10091 task.mpMediumLockList->GetBegin();
10092 MediumLockList::Base::const_iterator sourceListEnd =
10093 task.mpMediumLockList->GetEnd();
10094 for (MediumLockList::Base::const_iterator it = sourceListBegin;
10095 it != sourceListEnd;
10096 ++it)
10097 {
10098 const MediumLock &mediumLock = *it;
10099 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10100 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10101
10102 /* sanity check */
10103 Assert(pMedium->m->state == MediumState_LockedWrite);
10104
10105 vrc = VDOpen(hdd,
10106 pMedium->m->strFormat.c_str(),
10107 pMedium->m->strLocationFull.c_str(),
10108 VD_OPEN_FLAGS_NORMAL,
10109 pMedium->m->vdImageIfaces);
10110 if (RT_FAILURE(vrc))
10111 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10112 tr("Could not open the medium storage unit '%s'%s"),
10113 pMedium->m->strLocationFull.c_str(),
10114 i_vdError(vrc).c_str());
10115 }
10116
10117 /* we can directly use pTarget->m->"variables" but for better reading we use local copies */
10118 Guid targetId = pTarget->m->id;
10119 Utf8Str targetFormat(pTarget->m->strFormat);
10120 uint64_t targetCapabilities = pTarget->m->formatObj->i_getCapabilities();
10121
10122 /*
10123 * change target location
10124 * m->strNewLocationFull has been set already together with m->fMoveThisMedium in
10125 * i_preparationForMoving()
10126 */
10127 Utf8Str targetLocation = i_getNewLocationForMoving();
10128
10129 /* unlock before the potentially lengthy operation */
10130 thisLock.release();
10131
10132 /* ensure the target directory exists */
10133 if (targetCapabilities & MediumFormatCapabilities_File)
10134 {
10135 HRESULT hrc = VirtualBox::i_ensureFilePathExists(targetLocation,
10136 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
10137 if (FAILED(hrc))
10138 throw hrc;
10139 }
10140
10141 try
10142 {
10143 vrc = VDCopy(hdd,
10144 VD_LAST_IMAGE,
10145 hdd,
10146 targetFormat.c_str(),
10147 targetLocation.c_str(),
10148 true /* fMoveByRename */,
10149 0 /* cbSize */,
10150 VD_IMAGE_FLAGS_NONE,
10151 targetId.raw(),
10152 VD_OPEN_FLAGS_NORMAL,
10153 NULL /* pVDIfsOperation */,
10154 pTarget->m->vdImageIfaces,
10155 task.mVDOperationIfaces);
10156 if (RT_FAILURE(vrc))
10157 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10158 tr("Could not move medium '%s'%s"),
10159 targetLocation.c_str(), i_vdError(vrc).c_str());
10160 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10161 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10162 unsigned uImageFlags;
10163 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
10164 if (RT_SUCCESS(vrc))
10165 variant = (MediumVariant_T)uImageFlags;
10166
10167 /*
10168 * set current location, because VDCopy\VDCopyEx doesn't do it.
10169 * also reset moving flag
10170 */
10171 i_resetMoveOperationData();
10172 m->strLocationFull = targetLocation;
10173
10174 }
10175 catch (HRESULT hrcXcpt) { hrcOut = hrcXcpt; }
10176
10177 }
10178 catch (HRESULT hrcXcpt) { hrcOut = hrcXcpt; }
10179
10180 VDDestroy(hdd);
10181 }
10182 catch (HRESULT hrcXcpt) { hrcOut = hrcXcpt; }
10183
10184 ErrorInfoKeeper eik;
10185 MultiResult mrc(hrcOut);
10186
10187 // now, at the end of this task (always asynchronous), save the settings
10188 if (SUCCEEDED(mrc))
10189 {
10190 // save the settings
10191 i_markRegistriesModified();
10192 /* collect multiple errors */
10193 eik.restore();
10194 m->pVirtualBox->i_saveModifiedRegistries();
10195 eik.fetch();
10196 }
10197
10198 /* Everything is explicitly unlocked when the task exits,
10199 * as the task destruction also destroys the source chain. */
10200
10201 task.mpMediumLockList->Clear();
10202
10203 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
10204 m->pVirtualBox->i_onMediumConfigChanged(this);
10205
10206 LogFlowFunc(("LEAVE: mrc=%Rhrc\n", (HRESULT)mrc));
10207 return mrc;
10208}
10209
10210/**
10211 * Implementation code for the "delete" task.
10212 *
10213 * This task always gets started from Medium::deleteStorage() and can run
10214 * synchronously or asynchronously depending on the "wait" parameter passed to
10215 * that function.
10216 *
10217 * @param task
10218 * @return
10219 */
10220HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
10221{
10222 NOREF(task);
10223 HRESULT hrc = S_OK;
10224
10225 try
10226 {
10227 /* The lock is also used as a signal from the task initiator (which
10228 * releases it only after RTThreadCreate()) that we can start the job */
10229 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10230
10231 PVDISK hdd;
10232 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10233 ComAssertRCThrow(vrc, E_FAIL);
10234
10235 Utf8Str format(m->strFormat);
10236 Utf8Str location(m->strLocationFull);
10237
10238 /* unlock before the potentially lengthy operation */
10239 Assert(m->state == MediumState_Deleting);
10240 thisLock.release();
10241
10242 try
10243 {
10244 vrc = VDOpen(hdd,
10245 format.c_str(),
10246 location.c_str(),
10247 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
10248 m->vdImageIfaces);
10249 if (RT_SUCCESS(vrc))
10250 vrc = VDClose(hdd, true /* fDelete */);
10251
10252 if (RT_FAILURE(vrc) && vrc != VERR_FILE_NOT_FOUND)
10253 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10254 tr("Could not delete the medium storage unit '%s'%s"),
10255 location.c_str(), i_vdError(vrc).c_str());
10256
10257 }
10258 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10259
10260 VDDestroy(hdd);
10261 }
10262 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10263
10264 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10265
10266 /* go to the NotCreated state even on failure since the storage
10267 * may have been already partially deleted and cannot be used any
10268 * more. One will be able to manually re-open the storage if really
10269 * needed to re-register it. */
10270 m->state = MediumState_NotCreated;
10271
10272 /* Reset UUID to prevent Create* from reusing it again */
10273 com::Guid uOldId = m->id;
10274 unconst(m->id).clear();
10275
10276 if (task.NotifyAboutChanges() && SUCCEEDED(hrc))
10277 {
10278 if (m->pParent.isNotNull())
10279 m->pVirtualBox->i_onMediumConfigChanged(m->pParent);
10280 m->pVirtualBox->i_onMediumRegistered(uOldId, m->devType, FALSE);
10281 }
10282
10283 return hrc;
10284}
10285
10286/**
10287 * Implementation code for the "reset" task.
10288 *
10289 * This always gets started asynchronously from Medium::Reset().
10290 *
10291 * @param task
10292 * @return
10293 */
10294HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
10295{
10296 HRESULT hrc = S_OK;
10297
10298 uint64_t size = 0, logicalSize = 0;
10299 MediumVariant_T variant = MediumVariant_Standard;
10300
10301 try
10302 {
10303 /* The lock is also used as a signal from the task initiator (which
10304 * releases it only after RTThreadCreate()) that we can start the job */
10305 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10306
10307 /// @todo Below we use a pair of delete/create operations to reset
10308 /// the diff contents but the most efficient way will of course be
10309 /// to add a VDResetDiff() API call
10310
10311 PVDISK hdd;
10312 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10313 ComAssertRCThrow(vrc, E_FAIL);
10314
10315 Guid id = m->id;
10316 Utf8Str format(m->strFormat);
10317 Utf8Str location(m->strLocationFull);
10318
10319 Medium *pParent = m->pParent;
10320 Guid parentId = pParent->m->id;
10321 Utf8Str parentFormat(pParent->m->strFormat);
10322 Utf8Str parentLocation(pParent->m->strLocationFull);
10323
10324 Assert(m->state == MediumState_LockedWrite);
10325
10326 /* unlock before the potentially lengthy operation */
10327 thisLock.release();
10328
10329 try
10330 {
10331 /* Open all media in the target chain but the last. */
10332 MediumLockList::Base::const_iterator targetListBegin =
10333 task.mpMediumLockList->GetBegin();
10334 MediumLockList::Base::const_iterator targetListEnd =
10335 task.mpMediumLockList->GetEnd();
10336 for (MediumLockList::Base::const_iterator it = targetListBegin;
10337 it != targetListEnd;
10338 ++it)
10339 {
10340 const MediumLock &mediumLock = *it;
10341 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10342
10343 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10344
10345 /* sanity check, "this" is checked above */
10346 Assert( pMedium == this
10347 || pMedium->m->state == MediumState_LockedRead);
10348
10349 /* Open all media in appropriate mode. */
10350 vrc = VDOpen(hdd,
10351 pMedium->m->strFormat.c_str(),
10352 pMedium->m->strLocationFull.c_str(),
10353 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
10354 pMedium->m->vdImageIfaces);
10355 if (RT_FAILURE(vrc))
10356 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10357 tr("Could not open the medium storage unit '%s'%s"),
10358 pMedium->m->strLocationFull.c_str(),
10359 i_vdError(vrc).c_str());
10360
10361 /* Done when we hit the media which should be reset */
10362 if (pMedium == this)
10363 break;
10364 }
10365
10366 /* first, delete the storage unit */
10367 vrc = VDClose(hdd, true /* fDelete */);
10368 if (RT_FAILURE(vrc))
10369 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10370 tr("Could not delete the medium storage unit '%s'%s"),
10371 location.c_str(), i_vdError(vrc).c_str());
10372
10373 /* next, create it again */
10374 vrc = VDOpen(hdd,
10375 parentFormat.c_str(),
10376 parentLocation.c_str(),
10377 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
10378 m->vdImageIfaces);
10379 if (RT_FAILURE(vrc))
10380 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10381 tr("Could not open the medium storage unit '%s'%s"),
10382 parentLocation.c_str(), i_vdError(vrc).c_str());
10383
10384 vrc = VDCreateDiff(hdd,
10385 format.c_str(),
10386 location.c_str(),
10387 /// @todo use the same medium variant as before
10388 VD_IMAGE_FLAGS_NONE,
10389 NULL,
10390 id.raw(),
10391 parentId.raw(),
10392 VD_OPEN_FLAGS_NORMAL,
10393 m->vdImageIfaces,
10394 task.mVDOperationIfaces);
10395 if (RT_FAILURE(vrc))
10396 {
10397 if (vrc == VERR_VD_INVALID_TYPE)
10398 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10399 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
10400 location.c_str(), i_vdError(vrc).c_str());
10401 else
10402 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10403 tr("Could not create the differencing medium storage unit '%s'%s"),
10404 location.c_str(), i_vdError(vrc).c_str());
10405 }
10406
10407 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10408 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10409 unsigned uImageFlags;
10410 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
10411 if (RT_SUCCESS(vrc))
10412 variant = (MediumVariant_T)uImageFlags;
10413 }
10414 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10415
10416 VDDestroy(hdd);
10417 }
10418 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10419
10420 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10421
10422 m->size = size;
10423 m->logicalSize = logicalSize;
10424 m->variant = variant;
10425
10426 if (task.NotifyAboutChanges() && SUCCEEDED(hrc))
10427 m->pVirtualBox->i_onMediumConfigChanged(this);
10428
10429 /* Everything is explicitly unlocked when the task exits,
10430 * as the task destruction also destroys the media chain. */
10431
10432 return hrc;
10433}
10434
10435/**
10436 * Implementation code for the "compact" task.
10437 *
10438 * @param task
10439 * @return
10440 */
10441HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
10442{
10443 HRESULT hrc = S_OK;
10444
10445 /* Lock all in {parent,child} order. The lock is also used as a
10446 * signal from the task initiator (which releases it only after
10447 * RTThreadCreate()) that we can start the job. */
10448 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10449
10450 try
10451 {
10452 PVDISK hdd;
10453 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10454 ComAssertRCThrow(vrc, E_FAIL);
10455
10456 try
10457 {
10458 /* Open all media in the chain. */
10459 MediumLockList::Base::const_iterator mediumListBegin =
10460 task.mpMediumLockList->GetBegin();
10461 MediumLockList::Base::const_iterator mediumListEnd =
10462 task.mpMediumLockList->GetEnd();
10463 MediumLockList::Base::const_iterator mediumListLast =
10464 mediumListEnd;
10465 --mediumListLast;
10466 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10467 it != mediumListEnd;
10468 ++it)
10469 {
10470 const MediumLock &mediumLock = *it;
10471 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10472 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10473
10474 /* sanity check */
10475 if (it == mediumListLast)
10476 Assert(pMedium->m->state == MediumState_LockedWrite);
10477 else
10478 Assert(pMedium->m->state == MediumState_LockedRead);
10479
10480 /* Open all media but last in read-only mode. Do not handle
10481 * shareable media, as compaction and sharing are mutually
10482 * exclusive. */
10483 vrc = VDOpen(hdd,
10484 pMedium->m->strFormat.c_str(),
10485 pMedium->m->strLocationFull.c_str(),
10486 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10487 pMedium->m->vdImageIfaces);
10488 if (RT_FAILURE(vrc))
10489 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10490 tr("Could not open the medium storage unit '%s'%s"),
10491 pMedium->m->strLocationFull.c_str(),
10492 i_vdError(vrc).c_str());
10493 }
10494
10495 Assert(m->state == MediumState_LockedWrite);
10496
10497 Utf8Str location(m->strLocationFull);
10498
10499 /* unlock before the potentially lengthy operation */
10500 thisLock.release();
10501
10502 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
10503 if (RT_FAILURE(vrc))
10504 {
10505 if (vrc == VERR_NOT_SUPPORTED)
10506 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10507 tr("Compacting is not yet supported for medium '%s'"),
10508 location.c_str());
10509 else if (vrc == VERR_NOT_IMPLEMENTED)
10510 throw setErrorBoth(E_NOTIMPL, vrc,
10511 tr("Compacting is not implemented, medium '%s'"),
10512 location.c_str());
10513 else
10514 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10515 tr("Could not compact medium '%s'%s"),
10516 location.c_str(),
10517 i_vdError(vrc).c_str());
10518 }
10519 }
10520 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10521
10522 VDDestroy(hdd);
10523 }
10524 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10525
10526 if (task.NotifyAboutChanges() && SUCCEEDED(hrc))
10527 m->pVirtualBox->i_onMediumConfigChanged(this);
10528
10529 /* Everything is explicitly unlocked when the task exits,
10530 * as the task destruction also destroys the media chain. */
10531
10532 return hrc;
10533}
10534
10535/**
10536 * Implementation code for the "resize" task.
10537 *
10538 * @param task
10539 * @return
10540 */
10541HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
10542{
10543 HRESULT hrc = S_OK;
10544
10545 uint64_t size = 0, logicalSize = 0;
10546
10547 try
10548 {
10549 /* The lock is also used as a signal from the task initiator (which
10550 * releases it only after RTThreadCreate()) that we can start the job */
10551 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10552
10553 PVDISK hdd;
10554 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10555 ComAssertRCThrow(vrc, E_FAIL);
10556
10557 try
10558 {
10559 /* Open all media in the chain. */
10560 MediumLockList::Base::const_iterator mediumListBegin =
10561 task.mpMediumLockList->GetBegin();
10562 MediumLockList::Base::const_iterator mediumListEnd =
10563 task.mpMediumLockList->GetEnd();
10564 MediumLockList::Base::const_iterator mediumListLast =
10565 mediumListEnd;
10566 --mediumListLast;
10567 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10568 it != mediumListEnd;
10569 ++it)
10570 {
10571 const MediumLock &mediumLock = *it;
10572 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10573 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10574
10575 /* sanity check */
10576 if (it == mediumListLast)
10577 Assert(pMedium->m->state == MediumState_LockedWrite);
10578 else
10579 Assert(pMedium->m->state == MediumState_LockedRead ||
10580 // Allow resize the target image during mergeTo in case
10581 // of direction from parent to child because all intermediate
10582 // images are marked to MediumState_Deleting and will be
10583 // destroyed after successful merge
10584 pMedium->m->state == MediumState_Deleting);
10585
10586 /* Open all media but last in read-only mode. Do not handle
10587 * shareable media, as compaction and sharing are mutually
10588 * exclusive. */
10589 vrc = VDOpen(hdd,
10590 pMedium->m->strFormat.c_str(),
10591 pMedium->m->strLocationFull.c_str(),
10592 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10593 pMedium->m->vdImageIfaces);
10594 if (RT_FAILURE(vrc))
10595 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10596 tr("Could not open the medium storage unit '%s'%s"),
10597 pMedium->m->strLocationFull.c_str(),
10598 i_vdError(vrc).c_str());
10599 }
10600
10601 Assert(m->state == MediumState_LockedWrite);
10602
10603 Utf8Str location(m->strLocationFull);
10604
10605 /* unlock before the potentially lengthy operation */
10606 thisLock.release();
10607
10608 VDGEOMETRY geo = {0, 0, 0}; /* auto */
10609 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
10610 if (RT_FAILURE(vrc))
10611 {
10612 if (vrc == VERR_VD_SHRINK_NOT_SUPPORTED)
10613 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10614 tr("Shrinking is not yet supported for medium '%s'"),
10615 location.c_str());
10616 if (vrc == VERR_NOT_SUPPORTED)
10617 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10618 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
10619 task.mSize, location.c_str());
10620 else if (vrc == VERR_NOT_IMPLEMENTED)
10621 throw setErrorBoth(E_NOTIMPL, vrc,
10622 tr("Resizing is not implemented, medium '%s'"),
10623 location.c_str());
10624 else
10625 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10626 tr("Could not resize medium '%s'%s"),
10627 location.c_str(),
10628 i_vdError(vrc).c_str());
10629 }
10630 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10631 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10632 }
10633 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10634
10635 VDDestroy(hdd);
10636 }
10637 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10638
10639 if (SUCCEEDED(hrc))
10640 {
10641 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10642 m->size = size;
10643 m->logicalSize = logicalSize;
10644
10645 if (task.NotifyAboutChanges())
10646 m->pVirtualBox->i_onMediumConfigChanged(this);
10647 }
10648
10649 /* Everything is explicitly unlocked when the task exits,
10650 * as the task destruction also destroys the media chain. */
10651
10652 return hrc;
10653}
10654
10655/**
10656 * Implementation code for the "import" task.
10657 *
10658 * This only gets started from Medium::importFile() and always runs
10659 * asynchronously. It potentially touches the media registry, so we
10660 * always save the VirtualBox.xml file when we're done here.
10661 *
10662 * @param task
10663 * @return
10664 */
10665HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
10666{
10667 /** @todo r=klaus The code below needs to be double checked with regard
10668 * to lock order violations, it probably causes lock order issues related
10669 * to the AutoCaller usage. */
10670 HRESULT hrcTmp = S_OK;
10671
10672 const ComObjPtr<Medium> &pParent = task.mParent;
10673
10674 bool fCreatingTarget = false;
10675
10676 uint64_t size = 0, logicalSize = 0;
10677 MediumVariant_T variant = MediumVariant_Standard;
10678 bool fGenerateUuid = false;
10679
10680 try
10681 {
10682 if (!pParent.isNull())
10683 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
10684 {
10685 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
10686 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10687 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
10688 pParent->m->strLocationFull.c_str());
10689 }
10690
10691 /* Lock all in {parent,child} order. The lock is also used as a
10692 * signal from the task initiator (which releases it only after
10693 * RTThreadCreate()) that we can start the job. */
10694 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
10695
10696 fCreatingTarget = m->state == MediumState_Creating;
10697
10698 /* The object may request a specific UUID (through a special form of
10699 * the moveTo() argument). Otherwise we have to generate it */
10700 Guid targetId = m->id;
10701
10702 fGenerateUuid = targetId.isZero();
10703 if (fGenerateUuid)
10704 {
10705 targetId.create();
10706 /* VirtualBox::i_registerMedium() will need UUID */
10707 unconst(m->id) = targetId;
10708 }
10709
10710
10711 PVDISK hdd;
10712 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10713 ComAssertRCThrow(vrc, E_FAIL);
10714
10715 try
10716 {
10717 /* Open source medium. */
10718 vrc = VDOpen(hdd,
10719 task.mFormat->i_getId().c_str(),
10720 task.mFilename.c_str(),
10721 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
10722 task.mVDImageIfaces);
10723 if (RT_FAILURE(vrc))
10724 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10725 tr("Could not open the medium storage unit '%s'%s"),
10726 task.mFilename.c_str(),
10727 i_vdError(vrc).c_str());
10728
10729 Utf8Str targetFormat(m->strFormat);
10730 Utf8Str targetLocation(m->strLocationFull);
10731 uint64_t capabilities = task.mFormat->i_getCapabilities();
10732
10733 Assert( m->state == MediumState_Creating
10734 || m->state == MediumState_LockedWrite);
10735 Assert( pParent.isNull()
10736 || pParent->m->state == MediumState_LockedRead);
10737
10738 /* unlock before the potentially lengthy operation */
10739 thisLock.release();
10740
10741 /* ensure the target directory exists */
10742 if (capabilities & MediumFormatCapabilities_File)
10743 {
10744 HRESULT hrc = VirtualBox::i_ensureFilePathExists(targetLocation,
10745 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
10746 if (FAILED(hrc))
10747 throw hrc;
10748 }
10749
10750 PVDISK targetHdd;
10751 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
10752 ComAssertRCThrow(vrc, E_FAIL);
10753
10754 try
10755 {
10756 /* Open all media in the target chain. */
10757 MediumLockList::Base::const_iterator targetListBegin =
10758 task.mpTargetMediumLockList->GetBegin();
10759 MediumLockList::Base::const_iterator targetListEnd =
10760 task.mpTargetMediumLockList->GetEnd();
10761 for (MediumLockList::Base::const_iterator it = targetListBegin;
10762 it != targetListEnd;
10763 ++it)
10764 {
10765 const MediumLock &mediumLock = *it;
10766 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10767
10768 /* If the target medium is not created yet there's no
10769 * reason to open it. */
10770 if (pMedium == this && fCreatingTarget)
10771 continue;
10772
10773 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10774
10775 /* sanity check */
10776 Assert( pMedium->m->state == MediumState_LockedRead
10777 || pMedium->m->state == MediumState_LockedWrite);
10778
10779 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
10780 if (pMedium->m->state != MediumState_LockedWrite)
10781 uOpenFlags = VD_OPEN_FLAGS_READONLY;
10782 if (pMedium->m->type == MediumType_Shareable)
10783 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
10784
10785 /* Open all media in appropriate mode. */
10786 vrc = VDOpen(targetHdd,
10787 pMedium->m->strFormat.c_str(),
10788 pMedium->m->strLocationFull.c_str(),
10789 uOpenFlags | m->uOpenFlagsDef,
10790 pMedium->m->vdImageIfaces);
10791 if (RT_FAILURE(vrc))
10792 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10793 tr("Could not open the medium storage unit '%s'%s"),
10794 pMedium->m->strLocationFull.c_str(),
10795 i_vdError(vrc).c_str());
10796 }
10797
10798 vrc = VDCopy(hdd,
10799 VD_LAST_IMAGE,
10800 targetHdd,
10801 targetFormat.c_str(),
10802 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
10803 false /* fMoveByRename */,
10804 0 /* cbSize */,
10805 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk),
10806 targetId.raw(),
10807 VD_OPEN_FLAGS_NORMAL,
10808 NULL /* pVDIfsOperation */,
10809 m->vdImageIfaces,
10810 task.mVDOperationIfaces);
10811 if (RT_FAILURE(vrc))
10812 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10813 tr("Could not create the imported medium '%s'%s"),
10814 targetLocation.c_str(), i_vdError(vrc).c_str());
10815
10816 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
10817 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
10818 unsigned uImageFlags;
10819 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
10820 if (RT_SUCCESS(vrc))
10821 variant = (MediumVariant_T)uImageFlags;
10822 }
10823 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
10824
10825 VDDestroy(targetHdd);
10826 }
10827 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
10828
10829 VDDestroy(hdd);
10830 }
10831 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
10832
10833 ErrorInfoKeeper eik;
10834 MultiResult mrc(hrcTmp);
10835
10836 /* Only do the parent changes for newly created media. */
10837 if (SUCCEEDED(mrc) && fCreatingTarget)
10838 {
10839 /* we set m->pParent & children() */
10840 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10841
10842 Assert(m->pParent.isNull());
10843
10844 if (pParent)
10845 {
10846 /* Associate the imported medium with the parent and deassociate
10847 * from VirtualBox. Depth check above. */
10848 i_setParent(pParent);
10849
10850 /* register with mVirtualBox as the last step and move to
10851 * Created state only on success (leaving an orphan file is
10852 * better than breaking media registry consistency) */
10853 eik.restore();
10854 ComObjPtr<Medium> pMedium;
10855 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
10856 treeLock);
10857 Assert(this == pMedium);
10858 eik.fetch();
10859
10860 if (FAILED(mrc))
10861 /* break parent association on failure to register */
10862 this->i_deparent(); // removes target from parent
10863 }
10864 else
10865 {
10866 /* just register */
10867 eik.restore();
10868 ComObjPtr<Medium> pMedium;
10869 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
10870 Assert(this == pMedium);
10871 eik.fetch();
10872 }
10873 }
10874
10875 if (fCreatingTarget)
10876 {
10877 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
10878
10879 if (SUCCEEDED(mrc))
10880 {
10881 m->state = MediumState_Created;
10882
10883 m->size = size;
10884 m->logicalSize = logicalSize;
10885 m->variant = variant;
10886 }
10887 else
10888 {
10889 /* back to NotCreated on failure */
10890 m->state = MediumState_NotCreated;
10891
10892 /* reset UUID to prevent it from being reused next time */
10893 if (fGenerateUuid)
10894 unconst(m->id).clear();
10895 }
10896 }
10897
10898 // now, at the end of this task (always asynchronous), save the settings
10899 {
10900 // save the settings
10901 i_markRegistriesModified();
10902 /* collect multiple errors */
10903 eik.restore();
10904 m->pVirtualBox->i_saveModifiedRegistries();
10905 eik.fetch();
10906 }
10907
10908 /* Everything is explicitly unlocked when the task exits,
10909 * as the task destruction also destroys the target chain. */
10910
10911 /* Make sure the target chain is released early, otherwise it can
10912 * lead to deadlocks with concurrent IAppliance activities. */
10913 task.mpTargetMediumLockList->Clear();
10914
10915 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
10916 {
10917 if (pParent)
10918 m->pVirtualBox->i_onMediumConfigChanged(pParent);
10919 if (fCreatingTarget)
10920 m->pVirtualBox->i_onMediumConfigChanged(this);
10921 else
10922 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
10923 }
10924
10925 return mrc;
10926}
10927
10928/**
10929 * Sets up the encryption settings for a filter.
10930 */
10931void Medium::i_taskEncryptSettingsSetup(MediumCryptoFilterSettings *pSettings, const char *pszCipher,
10932 const char *pszKeyStore, const char *pszPassword,
10933 bool fCreateKeyStore)
10934{
10935 pSettings->pszCipher = pszCipher;
10936 pSettings->pszPassword = pszPassword;
10937 pSettings->pszKeyStoreLoad = pszKeyStore;
10938 pSettings->fCreateKeyStore = fCreateKeyStore;
10939 pSettings->pbDek = NULL;
10940 pSettings->cbDek = 0;
10941 pSettings->vdFilterIfaces = NULL;
10942
10943 pSettings->vdIfCfg.pfnAreKeysValid = i_vdCryptoConfigAreKeysValid;
10944 pSettings->vdIfCfg.pfnQuerySize = i_vdCryptoConfigQuerySize;
10945 pSettings->vdIfCfg.pfnQuery = i_vdCryptoConfigQuery;
10946 pSettings->vdIfCfg.pfnQueryBytes = NULL;
10947
10948 pSettings->vdIfCrypto.pfnKeyRetain = i_vdCryptoKeyRetain;
10949 pSettings->vdIfCrypto.pfnKeyRelease = i_vdCryptoKeyRelease;
10950 pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = i_vdCryptoKeyStorePasswordRetain;
10951 pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = i_vdCryptoKeyStorePasswordRelease;
10952 pSettings->vdIfCrypto.pfnKeyStoreSave = i_vdCryptoKeyStoreSave;
10953 pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = i_vdCryptoKeyStoreReturnParameters;
10954
10955 int vrc = VDInterfaceAdd(&pSettings->vdIfCfg.Core,
10956 "Medium::vdInterfaceCfgCrypto",
10957 VDINTERFACETYPE_CONFIG, pSettings,
10958 sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces);
10959 AssertRC(vrc);
10960
10961 vrc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core,
10962 "Medium::vdInterfaceCrypto",
10963 VDINTERFACETYPE_CRYPTO, pSettings,
10964 sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces);
10965 AssertRC(vrc);
10966}
10967
10968/**
10969 * Implementation code for the "encrypt" task.
10970 *
10971 * @param task
10972 * @return
10973 */
10974HRESULT Medium::i_taskEncryptHandler(Medium::EncryptTask &task)
10975{
10976# ifndef VBOX_WITH_EXTPACK
10977 RT_NOREF(task);
10978# endif
10979 HRESULT hrc = S_OK;
10980
10981 /* Lock all in {parent,child} order. The lock is also used as a
10982 * signal from the task initiator (which releases it only after
10983 * RTThreadCreate()) that we can start the job. */
10984 ComObjPtr<Medium> pBase = i_getBase();
10985 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10986
10987 try
10988 {
10989# ifdef VBOX_WITH_EXTPACK
10990 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
10991 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
10992 {
10993 /* Load the plugin */
10994 Utf8Str strPlugin;
10995 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
10996 if (SUCCEEDED(hrc))
10997 {
10998 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
10999 if (RT_FAILURE(vrc))
11000 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
11001 tr("Encrypting the image failed because the encryption plugin could not be loaded (%s)"),
11002 i_vdError(vrc).c_str());
11003 }
11004 else
11005 throw setError(VBOX_E_NOT_SUPPORTED,
11006 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
11007 ORACLE_PUEL_EXTPACK_NAME);
11008 }
11009 else
11010 throw setError(VBOX_E_NOT_SUPPORTED,
11011 tr("Encryption is not supported because the extension pack '%s' is missing"),
11012 ORACLE_PUEL_EXTPACK_NAME);
11013
11014 PVDISK pDisk = NULL;
11015 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
11016 ComAssertRCThrow(vrc, E_FAIL);
11017
11018 MediumCryptoFilterSettings CryptoSettingsRead;
11019 MediumCryptoFilterSettings CryptoSettingsWrite;
11020
11021 void *pvBuf = NULL;
11022 const char *pszPasswordNew = NULL;
11023 try
11024 {
11025 /* Set up disk encryption filters. */
11026 if (task.mstrCurrentPassword.isEmpty())
11027 {
11028 /*
11029 * Query whether the medium property indicating that encryption is
11030 * configured is existing.
11031 */
11032 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
11033 if (it != pBase->m->mapProperties.end())
11034 throw setError(VBOX_E_PASSWORD_INCORRECT,
11035 tr("The password given for the encrypted image is incorrect"));
11036 }
11037 else
11038 {
11039 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
11040 if (it == pBase->m->mapProperties.end())
11041 throw setError(VBOX_E_INVALID_OBJECT_STATE,
11042 tr("The image is not configured for encryption"));
11043
11044 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, it->second.c_str(), task.mstrCurrentPassword.c_str(),
11045 false /* fCreateKeyStore */);
11046 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
11047 if (vrc == VERR_VD_PASSWORD_INCORRECT)
11048 throw setError(VBOX_E_PASSWORD_INCORRECT,
11049 tr("The password to decrypt the image is incorrect"));
11050 else if (RT_FAILURE(vrc))
11051 throw setError(VBOX_E_INVALID_OBJECT_STATE,
11052 tr("Failed to load the decryption filter: %s"),
11053 i_vdError(vrc).c_str());
11054 }
11055
11056 if (task.mstrCipher.isNotEmpty())
11057 {
11058 if ( task.mstrNewPassword.isEmpty()
11059 && task.mstrNewPasswordId.isEmpty()
11060 && task.mstrCurrentPassword.isNotEmpty())
11061 {
11062 /* An empty password and password ID will default to the current password. */
11063 pszPasswordNew = task.mstrCurrentPassword.c_str();
11064 }
11065 else if (task.mstrNewPassword.isEmpty())
11066 throw setError(VBOX_E_OBJECT_NOT_FOUND,
11067 tr("A password must be given for the image encryption"));
11068 else if (task.mstrNewPasswordId.isEmpty())
11069 throw setError(VBOX_E_INVALID_OBJECT_STATE,
11070 tr("A valid identifier for the password must be given"));
11071 else
11072 pszPasswordNew = task.mstrNewPassword.c_str();
11073
11074 i_taskEncryptSettingsSetup(&CryptoSettingsWrite, task.mstrCipher.c_str(), NULL,
11075 pszPasswordNew, true /* fCreateKeyStore */);
11076 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_WRITE, CryptoSettingsWrite.vdFilterIfaces);
11077 if (RT_FAILURE(vrc))
11078 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
11079 tr("Failed to load the encryption filter: %s"),
11080 i_vdError(vrc).c_str());
11081 }
11082 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
11083 throw setError(VBOX_E_INVALID_OBJECT_STATE,
11084 tr("The password and password identifier must be empty if the output should be unencrypted"));
11085
11086 /* Open all media in the chain. */
11087 MediumLockList::Base::const_iterator mediumListBegin =
11088 task.mpMediumLockList->GetBegin();
11089 MediumLockList::Base::const_iterator mediumListEnd =
11090 task.mpMediumLockList->GetEnd();
11091 MediumLockList::Base::const_iterator mediumListLast =
11092 mediumListEnd;
11093 --mediumListLast;
11094 for (MediumLockList::Base::const_iterator it = mediumListBegin;
11095 it != mediumListEnd;
11096 ++it)
11097 {
11098 const MediumLock &mediumLock = *it;
11099 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
11100 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
11101
11102 Assert(pMedium->m->state == MediumState_LockedWrite);
11103
11104 /* Open all media but last in read-only mode. Do not handle
11105 * shareable media, as compaction and sharing are mutually
11106 * exclusive. */
11107 vrc = VDOpen(pDisk,
11108 pMedium->m->strFormat.c_str(),
11109 pMedium->m->strLocationFull.c_str(),
11110 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
11111 pMedium->m->vdImageIfaces);
11112 if (RT_FAILURE(vrc))
11113 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
11114 tr("Could not open the medium storage unit '%s'%s"),
11115 pMedium->m->strLocationFull.c_str(),
11116 i_vdError(vrc).c_str());
11117 }
11118
11119 Assert(m->state == MediumState_LockedWrite);
11120
11121 Utf8Str location(m->strLocationFull);
11122
11123 /* unlock before the potentially lengthy operation */
11124 thisLock.release();
11125
11126 vrc = VDPrepareWithFilters(pDisk, task.mVDOperationIfaces);
11127 if (RT_FAILURE(vrc))
11128 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
11129 tr("Could not prepare disk images for encryption (%Rrc): %s"),
11130 vrc, i_vdError(vrc).c_str());
11131
11132 thisLock.acquire();
11133 /* If everything went well set the new key store. */
11134 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
11135 if (it != pBase->m->mapProperties.end())
11136 pBase->m->mapProperties.erase(it);
11137
11138 /* Delete KeyId if encryption is removed or the password did change. */
11139 if ( task.mstrNewPasswordId.isNotEmpty()
11140 || task.mstrCipher.isEmpty())
11141 {
11142 it = pBase->m->mapProperties.find("CRYPT/KeyId");
11143 if (it != pBase->m->mapProperties.end())
11144 pBase->m->mapProperties.erase(it);
11145 }
11146
11147 if (CryptoSettingsWrite.pszKeyStore)
11148 {
11149 pBase->m->mapProperties["CRYPT/KeyStore"] = Utf8Str(CryptoSettingsWrite.pszKeyStore);
11150 if (task.mstrNewPasswordId.isNotEmpty())
11151 pBase->m->mapProperties["CRYPT/KeyId"] = task.mstrNewPasswordId;
11152 }
11153
11154 if (CryptoSettingsRead.pszCipherReturned)
11155 RTStrFree(CryptoSettingsRead.pszCipherReturned);
11156
11157 if (CryptoSettingsWrite.pszCipherReturned)
11158 RTStrFree(CryptoSettingsWrite.pszCipherReturned);
11159
11160 thisLock.release();
11161 pBase->i_markRegistriesModified();
11162 m->pVirtualBox->i_saveModifiedRegistries();
11163 }
11164 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
11165
11166 if (pvBuf)
11167 RTMemFree(pvBuf);
11168
11169 VDDestroy(pDisk);
11170# else
11171 throw setError(VBOX_E_NOT_SUPPORTED,
11172 tr("Encryption is not supported because extension pack support is not built in"));
11173# endif
11174 }
11175 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
11176
11177 /* Everything is explicitly unlocked when the task exits,
11178 * as the task destruction also destroys the media chain. */
11179
11180 return hrc;
11181}
11182
11183/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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