VirtualBox

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

Last change on this file since 107437 was 107262, checked in by vboxsync, 6 weeks ago

bugref:10806. Set the idle time for Medium and Machine tracked objects to 2 hours.

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

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