VirtualBox

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

Last change on this file since 98262 was 98262, checked in by vboxsync, 22 months ago

Main: rc() -> hrc()/vrc(). bugref:10223

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

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