VirtualBox

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

Last change on this file since 108566 was 108566, checked in by vboxsync, 5 weeks ago

bugref:10806. Excluded Machine, Medium, Session support. Only Progress support.

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

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