VirtualBox

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

Last change on this file since 100841 was 100078, checked in by vboxsync, 19 months ago

Main/src-server and Storage: Immutable media handling flexibility added bugref:5995

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