VirtualBox

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

Last change on this file since 107597 was 107488, checked in by vboxsync, 3 weeks ago

Main/src-server/MediumImpl.cpp: Fix unused assignment and missing field initialiser parfait warnings, bugref:3409

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