VirtualBox

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

Last change on this file since 106832 was 106832, checked in by vboxsync, 5 months ago

Main/{Machine,Medium,Snapshot}: Address broken reference counting of
medium back references when snapshots are involved which could cause
back references to get removed while the medium was still in use. Also
resolved some locking issues for various failure scenarios when creating
or deleting snapshots.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 399.5 KB
Line 
1/* $Id: MediumImpl.cpp 106832 2024-11-05 11:35:04Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2008-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MEDIUM
29#include "MediumImpl.h"
30#include "MediumIOImpl.h"
31#include "TokenImpl.h"
32#include "ProgressImpl.h"
33#include "SystemPropertiesImpl.h"
34#include "VirtualBoxImpl.h"
35#include "ExtPackManagerImpl.h"
36
37#include "AutoCaller.h"
38#include "Global.h"
39#include "LoggingNew.h"
40#include "ThreadTask.h"
41#include "VBox/com/MultiResult.h"
42#include "VBox/com/ErrorInfo.h"
43
44#include <VBox/err.h>
45#include <VBox/settings.h>
46
47#include <iprt/param.h>
48#include <iprt/path.h>
49#include <iprt/file.h>
50#include <iprt/cpp/utils.h>
51#include <iprt/memsafer.h>
52#include <iprt/base64.h>
53#include <iprt/vfs.h>
54#include <iprt/fsvfs.h>
55
56#include <VBox/vd.h>
57
58#include <algorithm>
59#include <list>
60#include <set>
61#include <map>
62
63
64typedef std::list<Guid> GuidList;
65
66
67#ifdef VBOX_WITH_EXTPACK
68static const char g_szVDPlugin[] = "VDPluginCrypt";
69#endif
70
71
72////////////////////////////////////////////////////////////////////////////////
73//
74// Medium data definition
75//
76////////////////////////////////////////////////////////////////////////////////
77#if __cplusplus < 201700 && RT_GNUC_PREREQ(11,0) /* gcc/libstdc++ 12.1.1 errors out here because unary_function is deprecated */
78# pragma GCC diagnostic push
79# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
80#endif
81
82struct SnapshotRef
83{
84 /** Equality predicate for stdc++. */
85 struct EqualsTo
86#if __cplusplus < 201700 /* deprecated in C++11, removed in C++17. */
87 : public std::unary_function <SnapshotRef, bool>
88#endif
89 {
90 explicit EqualsTo(const Guid &aSnapshotId) : snapshotId(aSnapshotId) {}
91
92#if __cplusplus < 201700 /* deprecated in C++11, removed in C++17. */
93 bool operator()(const argument_type &aThat) const
94#else
95 bool operator()(const SnapshotRef &aThat) const
96#endif
97 {
98 return aThat.snapshotId == snapshotId;
99 }
100
101 const Guid snapshotId;
102 };
103
104 SnapshotRef(const Guid &aSnapshotId,
105 const int &aRefCnt = 1)
106 : snapshotId(aSnapshotId),
107 iRefCnt(aRefCnt) {}
108
109 Guid snapshotId;
110 /*
111 * The number of attachments of the medium in the same snapshot.
112 * Used for MediumType_Readonly. It is always equal to 1 for other types.
113 * Usual int is used because any changes in the BackRef are guarded by
114 * AutoWriteLock.
115 */
116 int iRefCnt;
117};
118
119/** Describes how a machine refers to this medium. */
120struct BackRef
121{
122 /** Equality predicate for stdc++. */
123 struct EqualsTo
124#if __cplusplus < 201700 /* deprecated in C++11, removed in C++17. */
125 : public std::unary_function <BackRef, bool>
126#endif
127 {
128 explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
129
130#if __cplusplus < 201700 /* deprecated in C++11, removed in C++17. */
131 bool operator()(const argument_type &aThat) const
132#else
133 bool operator()(const BackRef &aThat) const
134#endif
135 {
136 return aThat.machineId == machineId;
137 }
138
139 const Guid machineId;
140 };
141
142 BackRef(const Guid &aMachineId,
143 const Guid &aSnapshotId = Guid::Empty)
144 : machineId(aMachineId),
145 iRefCnt(1),
146 fInCurState(aSnapshotId.isZero())
147 {
148 if (aSnapshotId.isValid() && !aSnapshotId.isZero())
149 llSnapshotIds.push_back(SnapshotRef(aSnapshotId));
150 }
151
152 Guid machineId;
153 /*
154 * The number of attachments of the medium in the same machine.
155 * Used for MediumType_Readonly. It is always equal to 1 for other types.
156 * Usual int is used because any changes in the BackRef are guarded by
157 * AutoWriteLock.
158 */
159 int iRefCnt;
160 bool fInCurState : 1;
161 std::list<SnapshotRef> llSnapshotIds;
162};
163
164typedef std::list<BackRef> BackRefList;
165
166#if __cplusplus < 201700 && RT_GNUC_PREREQ(11,0)
167# pragma GCC diagnostic pop
168#endif
169
170
171struct Medium::Data
172{
173 Data()
174 : pVirtualBox(NULL),
175 state(MediumState_NotCreated),
176 variant(MediumVariant_Standard),
177 size(0),
178 readers(0),
179 preLockState(MediumState_NotCreated),
180 queryInfoSem(LOCKCLASS_MEDIUMQUERY, "queryInfoSem"),
181 queryInfoRunning(false),
182 type(MediumType_Normal),
183 devType(DeviceType_HardDisk),
184 logicalSize(0),
185 hddOpenMode(OpenReadWrite),
186 autoReset(false),
187 hostDrive(false),
188 implicit(false),
189 fClosing(false),
190 uOpenFlagsDef(VD_OPEN_FLAGS_IGNORE_FLUSH),
191 numCreateDiffTasks(0),
192 vdDiskIfaces(NULL),
193 vdImageIfaces(NULL),
194 fMoveThisMedium(false)
195 { }
196
197 /** weak VirtualBox parent */
198 VirtualBox * const pVirtualBox;
199
200 // pParent and llChildren are protected by VirtualBox::i_getMediaTreeLockHandle()
201 ComObjPtr<Medium> pParent;
202 MediaList llChildren; // to add a child, just call push_back; to remove
203 // a child, call child->deparent() which does a lookup
204
205 GuidList llRegistryIDs; // media registries in which this medium is listed
206
207 const Guid id;
208 Utf8Str strDescription;
209 MediumState_T state;
210 MediumVariant_T variant;
211 Utf8Str strLocationFull;
212 uint64_t size;
213 Utf8Str strLastAccessError;
214
215 BackRefList backRefs;
216
217 size_t readers;
218 MediumState_T preLockState;
219
220 /** Special synchronization for operations which must wait for
221 * Medium::i_queryInfo in another thread to complete. Using a SemRW is
222 * not quite ideal, but at least it is subject to the lock validator,
223 * unlike the SemEventMulti which we had here for many years. Catching
224 * possible deadlocks is more important than a tiny bit of efficiency. */
225 RWLockHandle queryInfoSem;
226 bool queryInfoRunning : 1;
227
228 const Utf8Str strFormat;
229 ComObjPtr<MediumFormat> formatObj;
230
231 MediumType_T type;
232 DeviceType_T devType;
233 uint64_t logicalSize;
234
235 HDDOpenMode hddOpenMode;
236
237 bool autoReset : 1;
238
239 /** New UUID to be set on the next Medium::i_queryInfo call. */
240 const Guid uuidImage;
241 /** New parent UUID to be set on the next Medium::i_queryInfo call. */
242 const Guid uuidParentImage;
243
244/** @todo r=bird: the boolean bitfields are pointless if they're not grouped! */
245 bool hostDrive : 1;
246
247 settings::StringsMap mapProperties;
248
249 bool implicit : 1;
250 /** Flag whether the medium is in the process of being closed. */
251 bool fClosing: 1;
252
253 /** Default flags passed to VDOpen(). */
254 unsigned uOpenFlagsDef;
255
256 uint32_t numCreateDiffTasks;
257
258 Utf8Str vdError; /*< Error remembered by the VD error callback. */
259
260 VDINTERFACEERROR vdIfError;
261
262 VDINTERFACECONFIG vdIfConfig;
263
264 /** The handle to the default VD TCP/IP interface. */
265 VDIFINST hTcpNetInst;
266
267 PVDINTERFACE vdDiskIfaces;
268 PVDINTERFACE vdImageIfaces;
269
270 /** Flag if the medium is going to move to a new
271 * location. */
272 bool fMoveThisMedium;
273 /** new location path */
274 Utf8Str strNewLocationFull;
275};
276
277typedef struct VDSOCKETINT
278{
279 /** Socket handle. */
280 RTSOCKET hSocket;
281} VDSOCKETINT, *PVDSOCKETINT;
282
283////////////////////////////////////////////////////////////////////////////////
284//
285// Globals
286//
287////////////////////////////////////////////////////////////////////////////////
288
289/**
290 * Medium::Task class for asynchronous operations.
291 *
292 * @note Instances of this class must be created using new() because the
293 * task thread function will delete them when the task is complete.
294 *
295 * @note The constructor of this class adds a caller on the managed Medium
296 * object which is automatically released upon destruction.
297 */
298class Medium::Task : public ThreadTask
299{
300public:
301 Task(Medium *aMedium, Progress *aProgress, bool fNotifyAboutChanges = true)
302 : ThreadTask("Medium::Task"),
303 mVDOperationIfaces(NULL),
304 mMedium(aMedium),
305 mMediumCaller(aMedium),
306 mProgress(aProgress),
307 mVirtualBoxCaller(NULL),
308 mNotifyAboutChanges(fNotifyAboutChanges)
309 {
310 AssertReturnVoidStmt(aMedium, mHrc = E_FAIL);
311 mHrc = mMediumCaller.hrc();
312 if (FAILED(mHrc))
313 return;
314
315 /* Get strong VirtualBox reference, see below. */
316 VirtualBox *pVirtualBox = aMedium->m->pVirtualBox;
317 mVirtualBox = pVirtualBox;
318 mVirtualBoxCaller.attach(pVirtualBox);
319 mHrc = mVirtualBoxCaller.hrc();
320 if (FAILED(mHrc))
321 return;
322
323 /* Set up a per-operation progress interface, can be used freely (for
324 * binary operations you can use it either on the source or target). */
325 if (mProgress)
326 {
327 mVDIfProgress.pfnProgress = aProgress->i_vdProgressCallback;
328 int vrc = VDInterfaceAdd(&mVDIfProgress.Core,
329 "Medium::Task::vdInterfaceProgress",
330 VDINTERFACETYPE_PROGRESS,
331 mProgress,
332 sizeof(mVDIfProgress),
333 &mVDOperationIfaces);
334 AssertRC(vrc);
335 if (RT_FAILURE(vrc))
336 mHrc = E_FAIL;
337 }
338 }
339
340 // Make all destructors virtual. Just in case.
341 virtual ~Task()
342 {
343 /* send the notification of completion.*/
344 if ( isAsync()
345 && !mProgress.isNull())
346 mProgress->i_notifyComplete(mHrc);
347 }
348
349 HRESULT hrc() const { return mHrc; }
350 bool isOk() const { return SUCCEEDED(hrc()); }
351 bool NotifyAboutChanges() const { return mNotifyAboutChanges; }
352
353 const ComPtr<Progress>& GetProgressObject() const {return mProgress;}
354
355 /**
356 * Runs Medium::Task::executeTask() on the current thread
357 * instead of creating a new one.
358 */
359 HRESULT runNow()
360 {
361 LogFlowFuncEnter();
362
363 mHrc = executeTask();
364
365 LogFlowFunc(("hrc=%Rhrc\n", mHrc));
366 LogFlowFuncLeave();
367 return mHrc;
368 }
369
370 /**
371 * Implementation code for the "create base" task.
372 * Used as function for execution from a standalone thread.
373 */
374 void handler()
375 {
376 LogFlowFuncEnter();
377 try
378 {
379 mHrc = executeTask(); /* (destructor picks up mHrc, see above) */
380 LogFlowFunc(("hrc=%Rhrc\n", mHrc));
381 }
382 catch (...)
383 {
384 LogRel(("Some exception in the function Medium::Task:handler()\n"));
385 }
386
387 LogFlowFuncLeave();
388 }
389
390 PVDINTERFACE mVDOperationIfaces;
391
392 const ComObjPtr<Medium> mMedium;
393 AutoCaller mMediumCaller;
394
395protected:
396 HRESULT mHrc;
397
398private:
399 virtual HRESULT executeTask() = 0;
400
401 const ComObjPtr<Progress> mProgress;
402
403 VDINTERFACEPROGRESS mVDIfProgress;
404
405 /* Must have a strong VirtualBox reference during a task otherwise the
406 * reference count might drop to 0 while a task is still running. This
407 * would result in weird behavior, including deadlocks due to uninit and
408 * locking order issues. The deadlock often is not detectable because the
409 * uninit uses event semaphores which sabotages deadlock detection. */
410 ComObjPtr<VirtualBox> mVirtualBox;
411 AutoCaller mVirtualBoxCaller;
412 bool mNotifyAboutChanges;
413};
414
415class Medium::CreateBaseTask : public Medium::Task
416{
417public:
418 CreateBaseTask(Medium *aMedium,
419 Progress *aProgress,
420 uint64_t aSize,
421 MediumVariant_T aVariant,
422 bool fNotifyAboutChanges = true)
423 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
424 mSize(aSize),
425 mVariant(aVariant)
426 {
427 m_strTaskName = "createBase";
428 }
429
430 uint64_t mSize;
431 MediumVariant_T mVariant;
432
433private:
434 HRESULT executeTask()
435 {
436 return mMedium->i_taskCreateBaseHandler(*this);
437 }
438};
439
440class Medium::CreateDiffTask : public Medium::Task
441{
442public:
443 CreateDiffTask(Medium *aMedium,
444 Progress *aProgress,
445 Medium *aTarget,
446 MediumVariant_T aVariant,
447 MediumLockList *aMediumLockList,
448 bool fKeepMediumLockList = false,
449 bool fNotifyAboutChanges = true)
450 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
451 mpMediumLockList(aMediumLockList),
452 mTarget(aTarget),
453 mVariant(aVariant),
454 mTargetCaller(aTarget),
455 mfKeepMediumLockList(fKeepMediumLockList)
456 {
457 AssertReturnVoidStmt(aTarget != NULL, mHrc = E_FAIL);
458 mHrc = mTargetCaller.hrc();
459 if (FAILED(mHrc))
460 return;
461 m_strTaskName = "createDiff";
462 }
463
464 ~CreateDiffTask()
465 {
466 if (!mfKeepMediumLockList && mpMediumLockList)
467 delete mpMediumLockList;
468 }
469
470 MediumLockList *mpMediumLockList;
471
472 const ComObjPtr<Medium> mTarget;
473 MediumVariant_T mVariant;
474
475private:
476 HRESULT executeTask()
477 {
478 return mMedium->i_taskCreateDiffHandler(*this);
479 }
480
481 AutoCaller mTargetCaller;
482 bool mfKeepMediumLockList;
483};
484
485class Medium::CloneTask : public Medium::Task
486{
487public:
488 CloneTask(Medium *aMedium,
489 Progress *aProgress,
490 Medium *aTarget,
491 MediumVariant_T aVariant,
492 Medium *aParent,
493 uint32_t idxSrcImageSame,
494 uint32_t idxDstImageSame,
495 MediumLockList *aSourceMediumLockList,
496 MediumLockList *aTargetMediumLockList,
497 bool fKeepSourceMediumLockList = false,
498 bool fKeepTargetMediumLockList = false,
499 bool fNotifyAboutChanges = true,
500 uint64_t aTargetLogicalSize = 0)
501 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
502 mTarget(aTarget),
503 mParent(aParent),
504 mTargetLogicalSize(aTargetLogicalSize),
505 mpSourceMediumLockList(aSourceMediumLockList),
506 mpTargetMediumLockList(aTargetMediumLockList),
507 mVariant(aVariant),
508 midxSrcImageSame(idxSrcImageSame),
509 midxDstImageSame(idxDstImageSame),
510 mTargetCaller(aTarget),
511 mParentCaller(aParent),
512 mfKeepSourceMediumLockList(fKeepSourceMediumLockList),
513 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
514 {
515 AssertReturnVoidStmt(aTarget != NULL, mHrc = E_FAIL);
516 mHrc = mTargetCaller.hrc();
517 if (FAILED(mHrc))
518 return;
519 /* aParent may be NULL */
520 mHrc = mParentCaller.hrc();
521 if (FAILED(mHrc))
522 return;
523 AssertReturnVoidStmt(aSourceMediumLockList != NULL, mHrc = E_FAIL);
524 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mHrc = E_FAIL);
525 m_strTaskName = "createClone";
526 }
527
528 ~CloneTask()
529 {
530 if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
531 delete mpSourceMediumLockList;
532 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
533 delete mpTargetMediumLockList;
534 }
535
536 const ComObjPtr<Medium> mTarget;
537 const ComObjPtr<Medium> mParent;
538 uint64_t mTargetLogicalSize;
539 MediumLockList *mpSourceMediumLockList;
540 MediumLockList *mpTargetMediumLockList;
541 MediumVariant_T mVariant;
542 uint32_t midxSrcImageSame;
543 uint32_t midxDstImageSame;
544
545private:
546 HRESULT executeTask()
547 {
548 return mMedium->i_taskCloneHandler(*this);
549 }
550
551 AutoCaller mTargetCaller;
552 AutoCaller mParentCaller;
553 bool mfKeepSourceMediumLockList;
554 bool mfKeepTargetMediumLockList;
555};
556
557class Medium::MoveTask : public Medium::Task
558{
559public:
560 MoveTask(Medium *aMedium,
561 Progress *aProgress,
562 MediumVariant_T aVariant,
563 MediumLockList *aMediumLockList,
564 bool fKeepMediumLockList = false,
565 bool fNotifyAboutChanges = true)
566 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
567 mpMediumLockList(aMediumLockList),
568 mVariant(aVariant),
569 mfKeepMediumLockList(fKeepMediumLockList)
570 {
571 AssertReturnVoidStmt(aMediumLockList != NULL, mHrc = E_FAIL);
572 m_strTaskName = "createMove";
573 }
574
575 ~MoveTask()
576 {
577 if (!mfKeepMediumLockList && mpMediumLockList)
578 delete mpMediumLockList;
579 }
580
581 MediumLockList *mpMediumLockList;
582 MediumVariant_T mVariant;
583
584private:
585 HRESULT executeTask()
586 {
587 return mMedium->i_taskMoveHandler(*this);
588 }
589
590 bool mfKeepMediumLockList;
591};
592
593class Medium::CompactTask : public Medium::Task
594{
595public:
596 CompactTask(Medium *aMedium,
597 Progress *aProgress,
598 MediumLockList *aMediumLockList,
599 bool fKeepMediumLockList = false,
600 bool fNotifyAboutChanges = true)
601 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
602 mpMediumLockList(aMediumLockList),
603 mfKeepMediumLockList(fKeepMediumLockList)
604 {
605 AssertReturnVoidStmt(aMediumLockList != NULL, mHrc = E_FAIL);
606 m_strTaskName = "createCompact";
607 }
608
609 ~CompactTask()
610 {
611 if (!mfKeepMediumLockList && mpMediumLockList)
612 delete mpMediumLockList;
613 }
614
615 MediumLockList *mpMediumLockList;
616
617private:
618 HRESULT executeTask()
619 {
620 return mMedium->i_taskCompactHandler(*this);
621 }
622
623 bool mfKeepMediumLockList;
624};
625
626class Medium::ResizeTask : public Medium::Task
627{
628public:
629 ResizeTask(Medium *aMedium,
630 uint64_t aSize,
631 Progress *aProgress,
632 MediumLockList *aMediumLockList,
633 bool fKeepMediumLockList = false,
634 bool fNotifyAboutChanges = true)
635 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
636 mSize(aSize),
637 mpMediumLockList(aMediumLockList),
638 mfKeepMediumLockList(fKeepMediumLockList)
639 {
640 AssertReturnVoidStmt(aMediumLockList != NULL, mHrc = E_FAIL);
641 m_strTaskName = "createResize";
642 }
643
644 ~ResizeTask()
645 {
646 if (!mfKeepMediumLockList && mpMediumLockList)
647 delete mpMediumLockList;
648 }
649
650 uint64_t mSize;
651 MediumLockList *mpMediumLockList;
652
653private:
654 HRESULT executeTask()
655 {
656 return mMedium->i_taskResizeHandler(*this);
657 }
658
659 bool mfKeepMediumLockList;
660};
661
662class Medium::ResetTask : public Medium::Task
663{
664public:
665 ResetTask(Medium *aMedium,
666 Progress *aProgress,
667 MediumLockList *aMediumLockList,
668 bool fKeepMediumLockList = false,
669 bool fNotifyAboutChanges = true)
670 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
671 mpMediumLockList(aMediumLockList),
672 mfKeepMediumLockList(fKeepMediumLockList)
673 {
674 m_strTaskName = "createReset";
675 }
676
677 ~ResetTask()
678 {
679 if (!mfKeepMediumLockList && mpMediumLockList)
680 delete mpMediumLockList;
681 }
682
683 MediumLockList *mpMediumLockList;
684
685private:
686 HRESULT executeTask()
687 {
688 return mMedium->i_taskResetHandler(*this);
689 }
690
691 bool mfKeepMediumLockList;
692};
693
694class Medium::DeleteTask : public Medium::Task
695{
696public:
697 DeleteTask(Medium *aMedium,
698 Progress *aProgress,
699 MediumLockList *aMediumLockList,
700 bool fKeepMediumLockList = false,
701 bool fNotifyAboutChanges = true)
702 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
703 mpMediumLockList(aMediumLockList),
704 mfKeepMediumLockList(fKeepMediumLockList)
705 {
706 m_strTaskName = "createDelete";
707 }
708
709 ~DeleteTask()
710 {
711 if (!mfKeepMediumLockList && mpMediumLockList)
712 delete mpMediumLockList;
713 }
714
715 MediumLockList *mpMediumLockList;
716
717private:
718 HRESULT executeTask()
719 {
720 return mMedium->i_taskDeleteHandler(*this);
721 }
722
723 bool mfKeepMediumLockList;
724};
725
726class Medium::MergeTask : public Medium::Task
727{
728public:
729 MergeTask(Medium *aMedium,
730 Medium *aTarget,
731 bool fMergeForward,
732 Medium *aParentForTarget,
733 MediumLockList *aChildrenToReparent,
734 Progress *aProgress,
735 MediumLockList *aMediumLockList,
736 bool fKeepMediumLockList = false,
737 bool fNotifyAboutChanges = true)
738 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
739 mTarget(aTarget),
740 mfMergeForward(fMergeForward),
741 mParentForTarget(aParentForTarget),
742 mpChildrenToReparent(aChildrenToReparent),
743 mpMediumLockList(aMediumLockList),
744 mTargetCaller(aTarget),
745 mParentForTargetCaller(aParentForTarget),
746 mfKeepMediumLockList(fKeepMediumLockList)
747 {
748 AssertReturnVoidStmt(aMediumLockList != NULL, mHrc = E_FAIL);
749 m_strTaskName = "createMerge";
750 }
751
752 ~MergeTask()
753 {
754 if (!mfKeepMediumLockList && mpMediumLockList)
755 delete mpMediumLockList;
756 if (mpChildrenToReparent)
757 delete mpChildrenToReparent;
758 }
759
760 const ComObjPtr<Medium> mTarget;
761 bool mfMergeForward;
762 /* When mpChildrenToReparent is null then mParentForTarget is non-null and
763 * vice versa. In other words: they are used in different cases. */
764 const ComObjPtr<Medium> mParentForTarget;
765 MediumLockList *mpChildrenToReparent;
766 MediumLockList *mpMediumLockList;
767
768private:
769 HRESULT executeTask()
770 {
771 return mMedium->i_taskMergeHandler(*this);
772 }
773
774 AutoCaller mTargetCaller;
775 AutoCaller mParentForTargetCaller;
776 bool mfKeepMediumLockList;
777};
778
779class Medium::ImportTask : public Medium::Task
780{
781public:
782 ImportTask(Medium *aMedium,
783 Progress *aProgress,
784 const char *aFilename,
785 MediumFormat *aFormat,
786 MediumVariant_T aVariant,
787 RTVFSIOSTREAM aVfsIosSrc,
788 Medium *aParent,
789 MediumLockList *aTargetMediumLockList,
790 bool fKeepTargetMediumLockList = false,
791 bool fNotifyAboutChanges = true)
792 : Medium::Task(aMedium, aProgress, fNotifyAboutChanges),
793 mFilename(aFilename),
794 mFormat(aFormat),
795 mVariant(aVariant),
796 mParent(aParent),
797 mpTargetMediumLockList(aTargetMediumLockList),
798 mpVfsIoIf(NULL),
799 mParentCaller(aParent),
800 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
801 {
802 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mHrc = E_FAIL);
803 /* aParent may be NULL */
804 mHrc = mParentCaller.hrc();
805 if (FAILED(mHrc))
806 return;
807
808 mVDImageIfaces = aMedium->m->vdImageIfaces;
809
810 int vrc = VDIfCreateFromVfsStream(aVfsIosSrc, RTFILE_O_READ, &mpVfsIoIf);
811 AssertRCReturnVoidStmt(vrc, mHrc = E_FAIL);
812
813 vrc = VDInterfaceAdd(&mpVfsIoIf->Core, "Medium::ImportTaskVfsIos",
814 VDINTERFACETYPE_IO, mpVfsIoIf,
815 sizeof(VDINTERFACEIO), &mVDImageIfaces);
816 AssertRCReturnVoidStmt(vrc, mHrc = E_FAIL);
817 m_strTaskName = "createImport";
818 }
819
820 ~ImportTask()
821 {
822 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
823 delete mpTargetMediumLockList;
824 if (mpVfsIoIf)
825 {
826 VDIfDestroyFromVfsStream(mpVfsIoIf);
827 mpVfsIoIf = NULL;
828 }
829 }
830
831 Utf8Str mFilename;
832 ComObjPtr<MediumFormat> mFormat;
833 MediumVariant_T mVariant;
834 const ComObjPtr<Medium> mParent;
835 MediumLockList *mpTargetMediumLockList;
836 PVDINTERFACE mVDImageIfaces;
837 PVDINTERFACEIO mpVfsIoIf; /**< Pointer to the VFS I/O stream to VD I/O interface wrapper. */
838
839private:
840 HRESULT executeTask()
841 {
842 return mMedium->i_taskImportHandler(*this);
843 }
844
845 AutoCaller mParentCaller;
846 bool mfKeepTargetMediumLockList;
847};
848
849class Medium::EncryptTask : public Medium::Task
850{
851public:
852 EncryptTask(Medium *aMedium,
853 const com::Utf8Str &strNewPassword,
854 const com::Utf8Str &strCurrentPassword,
855 const com::Utf8Str &strCipher,
856 const com::Utf8Str &strNewPasswordId,
857 Progress *aProgress,
858 MediumLockList *aMediumLockList)
859 : Medium::Task(aMedium, aProgress, false),
860 mstrNewPassword(strNewPassword),
861 mstrCurrentPassword(strCurrentPassword),
862 mstrCipher(strCipher),
863 mstrNewPasswordId(strNewPasswordId),
864 mpMediumLockList(aMediumLockList)
865 {
866 AssertReturnVoidStmt(aMediumLockList != NULL, mHrc = E_FAIL);
867 /* aParent may be NULL */
868 mHrc = mParentCaller.hrc();
869 if (FAILED(mHrc))
870 return;
871
872 mVDImageIfaces = aMedium->m->vdImageIfaces;
873 m_strTaskName = "createEncrypt";
874 }
875
876 ~EncryptTask()
877 {
878 if (mstrNewPassword.length())
879 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
880 if (mstrCurrentPassword.length())
881 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
882
883 /* Keep any errors which might be set when deleting the lock list. */
884 ErrorInfoKeeper eik;
885 delete mpMediumLockList;
886 }
887
888 Utf8Str mstrNewPassword;
889 Utf8Str mstrCurrentPassword;
890 Utf8Str mstrCipher;
891 Utf8Str mstrNewPasswordId;
892 MediumLockList *mpMediumLockList;
893 PVDINTERFACE mVDImageIfaces;
894
895private:
896 HRESULT executeTask()
897 {
898 return mMedium->i_taskEncryptHandler(*this);
899 }
900
901 AutoCaller mParentCaller;
902};
903
904
905
906/**
907 * Converts the Medium device type to the VD type.
908 */
909static const char *getVDTypeName(VDTYPE enmType)
910{
911 switch (enmType)
912 {
913 case VDTYPE_HDD: return "HDD";
914 case VDTYPE_OPTICAL_DISC: return "DVD";
915 case VDTYPE_FLOPPY: return "floppy";
916 case VDTYPE_INVALID: return "invalid";
917 default:
918 AssertFailedReturn("unknown");
919 }
920}
921
922/**
923 * Converts the Medium device type to the VD type.
924 */
925static const char *getDeviceTypeName(DeviceType_T enmType)
926{
927 switch (enmType)
928 {
929 case DeviceType_HardDisk: return "HDD";
930 case DeviceType_DVD: return "DVD";
931 case DeviceType_Floppy: return "floppy";
932 case DeviceType_Null: return "null";
933 case DeviceType_Network: return "network";
934 case DeviceType_USB: return "USB";
935 case DeviceType_SharedFolder: return "shared folder";
936 case DeviceType_Graphics3D: return "graphics 3d";
937 default:
938 AssertFailedReturn("unknown");
939 }
940}
941
942
943
944////////////////////////////////////////////////////////////////////////////////
945//
946// Medium constructor / destructor
947//
948////////////////////////////////////////////////////////////////////////////////
949
950DEFINE_EMPTY_CTOR_DTOR(Medium)
951
952HRESULT Medium::FinalConstruct()
953{
954 m = new Data;
955
956 /* Initialize the callbacks of the VD error interface */
957 m->vdIfError.pfnError = i_vdErrorCall;
958 m->vdIfError.pfnMessage = NULL;
959
960 /* Initialize the callbacks of the VD config interface */
961 m->vdIfConfig.pfnAreKeysValid = i_vdConfigAreKeysValid;
962 m->vdIfConfig.pfnQuerySize = i_vdConfigQuerySize;
963 m->vdIfConfig.pfnQuery = i_vdConfigQuery;
964 m->vdIfConfig.pfnUpdate = i_vdConfigUpdate;
965 m->vdIfConfig.pfnQueryBytes = NULL;
966
967 /* Initialize the per-disk interface chain (could be done more globally,
968 * but it's not wasting much time or space so it's not worth it). */
969 int vrc;
970 vrc = VDInterfaceAdd(&m->vdIfError.Core,
971 "Medium::vdInterfaceError",
972 VDINTERFACETYPE_ERROR, this,
973 sizeof(VDINTERFACEERROR), &m->vdDiskIfaces);
974 AssertRCReturn(vrc, E_FAIL);
975
976 /* Initialize the per-image interface chain */
977 vrc = VDInterfaceAdd(&m->vdIfConfig.Core,
978 "Medium::vdInterfaceConfig",
979 VDINTERFACETYPE_CONFIG, this,
980 sizeof(VDINTERFACECONFIG), &m->vdImageIfaces);
981 AssertRCReturn(vrc, E_FAIL);
982
983 /* Initialize the callbacks of the VD TCP interface (we always use the host
984 * IP stack for now) */
985 vrc = VDIfTcpNetInstDefaultCreate(&m->hTcpNetInst, &m->vdImageIfaces);
986 AssertRCReturn(vrc, E_FAIL);
987
988 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 AutoCaller autoCaller(this);
4620 AssertComRCReturnRC(autoCaller.hrc());
4621
4622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4623
4624 LogFlowThisFunc(("ENTER: medium: '%s' aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n",
4625 i_getLocationFull().c_str(), aMachineId.raw(), aSnapshotId.raw()));
4626
4627 switch (m->state)
4628 {
4629 case MediumState_Created:
4630 case MediumState_Inaccessible:
4631 case MediumState_LockedRead:
4632 case MediumState_LockedWrite:
4633 break;
4634
4635 default:
4636 return i_setStateError();
4637 }
4638
4639 if (m->numCreateDiffTasks > 0)
4640 return setError(VBOX_E_OBJECT_IN_USE,
4641 tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created", "",
4642 m->numCreateDiffTasks),
4643 m->strLocationFull.c_str(),
4644 m->id.raw(),
4645 m->numCreateDiffTasks);
4646
4647 BackRefList::iterator it = std::find_if(m->backRefs.begin(),
4648 m->backRefs.end(),
4649 BackRef::EqualsTo(aMachineId));
4650 if (it == m->backRefs.end())
4651 {
4652 BackRef ref(aMachineId, aSnapshotId);
4653 m->backRefs.push_back(ref);
4654
4655 return S_OK;
4656 }
4657 bool fDvd = false;
4658 {
4659 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
4660 /*
4661 * Check the medium is DVD and readonly. It's for the case if DVD
4662 * will be able to be writable sometime in the future.
4663 */
4664 fDvd = m->type == MediumType_Readonly && m->devType == DeviceType_DVD;
4665 }
4666
4667 // if the caller has not supplied a snapshot ID, then we're attaching
4668 // to a machine a medium which represents the machine's current state,
4669 // so set the flag
4670
4671 if (aSnapshotId.isZero())
4672 {
4673 // Allow DVD having MediumType_Readonly to be attached twice.
4674 // (the medium already had been added to back reference)
4675 if (fDvd)
4676 {
4677 it->iRefCnt++;
4678 return S_OK;
4679 }
4680
4681 /* sanity: no duplicate attachments */
4682 if (it->fInCurState)
4683 return setError(VBOX_E_OBJECT_IN_USE,
4684 tr("Cannot attach medium '%s' {%RTuuid}: medium is already associated with the current state of machine uuid {%RTuuid}!"),
4685 m->strLocationFull.c_str(),
4686 m->id.raw(),
4687 aMachineId.raw());
4688 it->fInCurState = true;
4689
4690 return S_OK;
4691 }
4692
4693 // otherwise: a snapshot medium is being attached
4694
4695 /* sanity: no duplicate attachments */
4696 for (std::list<SnapshotRef>::iterator jt = it->llSnapshotIds.begin();
4697 jt != it->llSnapshotIds.end();
4698 ++jt)
4699 {
4700 const Guid &idOldSnapshot = jt->snapshotId;
4701
4702 if (idOldSnapshot == aSnapshotId)
4703 {
4704 if (fDvd)
4705 {
4706 jt->iRefCnt++;
4707 return S_OK;
4708 }
4709#ifdef DEBUG
4710 i_dumpBackRefs();
4711#endif
4712 return setError(VBOX_E_OBJECT_IN_USE,
4713 tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
4714 m->strLocationFull.c_str(),
4715 m->id.raw(),
4716 aSnapshotId.raw());
4717 }
4718 }
4719
4720 it->llSnapshotIds.push_back(SnapshotRef(aSnapshotId));
4721 // Do not touch fInCurState, as the image may be attached to the current
4722 // state *and* a snapshot, otherwise we lose the current state association!
4723
4724 LogFlowThisFuncLeave();
4725
4726 return S_OK;
4727}
4728
4729/**
4730 * Removes the given machine and optionally the snapshot from the list of the
4731 * objects this medium is attached to.
4732 *
4733 * @param aMachineId Machine ID.
4734 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
4735 * attachment.
4736 */
4737HRESULT Medium::i_removeBackReference(const Guid &aMachineId,
4738 const Guid &aSnapshotId /*= Guid::Empty*/)
4739{
4740 AssertReturn(aMachineId.isValid(), E_FAIL);
4741
4742 AutoCaller autoCaller(this);
4743 AssertComRCReturnRC(autoCaller.hrc());
4744
4745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4746
4747 LogFlowThisFunc(("ENTER: medium: '%s' aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n",
4748 i_getLocationFull().c_str(), aMachineId.raw(), aSnapshotId.raw()));
4749
4750 BackRefList::iterator it =
4751 std::find_if(m->backRefs.begin(), m->backRefs.end(),
4752 BackRef::EqualsTo(aMachineId));
4753 AssertReturn(it != m->backRefs.end(), E_FAIL);
4754
4755 bool fDvd = false;
4756 {
4757 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
4758 /*
4759 * Check the medium is DVD and readonly. It's for the case if DVD
4760 * will be able to be writable sometime in the future.
4761 */
4762 fDvd = m->type == MediumType_Readonly && m->devType == DeviceType_DVD;
4763 }
4764
4765 if (aSnapshotId.isZero())
4766 {
4767 /* Attached removable media which are part of a snapshot can be added to a
4768 * back reference more than once (as described above in i_addBackReference())
4769 * so we need to balance out the reference counting for DVDs here. */
4770 if (fDvd && it->iRefCnt > 1)
4771 it->iRefCnt--;
4772 it->iRefCnt--;
4773 if (it->iRefCnt > 0)
4774 return S_OK;
4775
4776 /* Mark the medium as no longer in use by the current machine although it
4777 * may still be included in one or more snapshots (it->llSnapshotIds). */
4778 it->fInCurState = false;
4779 }
4780 else
4781 {
4782 /* remove the snapshot attachment */
4783 std::list<SnapshotRef>::iterator jt =
4784 std::find_if(it->llSnapshotIds.begin(),
4785 it->llSnapshotIds.end(),
4786 SnapshotRef::EqualsTo(aSnapshotId));
4787
4788 AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
4789
4790 jt->iRefCnt--;
4791 if (jt->iRefCnt > 0)
4792 return S_OK;
4793
4794 it->llSnapshotIds.erase(jt);
4795
4796 /* The BackRef() constructor in i_addBackReference() creates back references
4797 * for attached hard disk mediums associated with a snapshot with the reference
4798 * count set to '1' and fInCurState=false before adding the snapshot to
4799 * it->llSnapshotIds. When removing that snapshot reference here we need to
4800 * decrement the reference count since there won't be a separate
4801 * i_removeBackReference() call with aSnapshotId.isZero() since this hard disk
4802 * medium is only associated with this snapshot. Removable media like DVDs
4803 * associated with a snapshot on the other hand can be attached twice, once for
4804 * the snapshot and once for the machine (aSnapshotId.isZero()=true) and their
4805 * reference counting is balanced out in the machine case above. */
4806 if (!fDvd && it->fInCurState == false && it->iRefCnt == 1 && it->llSnapshotIds.size() == 0)
4807 it->iRefCnt--;
4808 }
4809
4810 /* if the backref becomes empty, remove it */
4811 if (it->fInCurState == false && it->iRefCnt == 0 && it->llSnapshotIds.size() == 0)
4812 m->backRefs.erase(it);
4813
4814 return S_OK;
4815}
4816
4817/**
4818 * Internal method to return the medium's list of backrefs. Must have caller + locking!
4819 * @return
4820 */
4821const Guid* Medium::i_getFirstMachineBackrefId() const
4822{
4823 if (!m->backRefs.size())
4824 return NULL;
4825
4826 return &m->backRefs.front().machineId;
4827}
4828
4829/**
4830 * Internal method which returns a machine that either this medium or one of its children
4831 * is attached to. This is used for finding a replacement media registry when an existing
4832 * media registry is about to be deleted in VirtualBox::unregisterMachine().
4833 *
4834 * Must have caller + locking, *and* caller must hold the media tree lock!
4835 * @param aId Id to ignore when looking for backrefs.
4836 * @return
4837 */
4838const Guid* Medium::i_getAnyMachineBackref(const Guid &aId) const
4839{
4840 std::list<const Medium *> llMediaTodo;
4841 llMediaTodo.push_back(this);
4842
4843 while (!llMediaTodo.empty())
4844 {
4845 const Medium *pMedium = llMediaTodo.front();
4846 llMediaTodo.pop_front();
4847
4848 if (pMedium->m->backRefs.size())
4849 {
4850 if (pMedium->m->backRefs.front().machineId != aId)
4851 return &pMedium->m->backRefs.front().machineId;
4852 if (pMedium->m->backRefs.size() > 1)
4853 {
4854 BackRefList::const_iterator it = pMedium->m->backRefs.begin();
4855 ++it;
4856 return &it->machineId;
4857 }
4858 }
4859
4860 MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
4861 MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
4862 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
4863 llMediaTodo.push_back(*it);
4864 }
4865
4866 return NULL;
4867}
4868
4869const Guid* Medium::i_getFirstMachineBackrefSnapshotId() const
4870{
4871 if (!m->backRefs.size())
4872 return NULL;
4873
4874 const BackRef &ref = m->backRefs.front();
4875 if (ref.llSnapshotIds.empty())
4876 return NULL;
4877
4878 return &ref.llSnapshotIds.front().snapshotId;
4879}
4880
4881size_t Medium::i_getMachineBackRefCount() const
4882{
4883 return m->backRefs.size();
4884}
4885
4886#ifdef DEBUG
4887/**
4888 * Debugging helper that gets called after VirtualBox initialization that writes all
4889 * machine backreferences to the debug log.
4890 */
4891void Medium::i_dumpBackRefs()
4892{
4893 AutoCaller autoCaller(this);
4894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4895
4896 LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
4897
4898 for (BackRefList::iterator it2 = m->backRefs.begin();
4899 it2 != m->backRefs.end();
4900 ++it2)
4901 {
4902 const BackRef &ref = *it2;
4903 LogFlowThisFunc((" Backref from machine={%RTuuid} (fInCurState=%d, iRefCnt=%d)\n", ref.machineId.raw(), ref.fInCurState, ref.iRefCnt));
4904
4905 for (std::list<SnapshotRef>::const_iterator jt2 = it2->llSnapshotIds.begin();
4906 jt2 != it2->llSnapshotIds.end();
4907 ++jt2)
4908 {
4909 const Guid &id = jt2->snapshotId;
4910 LogFlowThisFunc((" Backref from snapshot={%RTuuid} (iRefCnt=%d)\n", id.raw(), jt2->iRefCnt));
4911 }
4912 }
4913}
4914#endif
4915
4916/**
4917 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
4918 * of this media and updates it if necessary to reflect the new location.
4919 *
4920 * @param strOldPath Old path (full).
4921 * @param strNewPath New path (full).
4922 *
4923 * @note Locks this object for writing.
4924 */
4925HRESULT Medium::i_updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
4926{
4927 AssertReturn(!strOldPath.isEmpty(), E_FAIL);
4928 AssertReturn(!strNewPath.isEmpty(), E_FAIL);
4929
4930 AutoCaller autoCaller(this);
4931 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
4932
4933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4934
4935 LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
4936
4937 const char *pcszMediumPath = m->strLocationFull.c_str();
4938
4939 if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
4940 {
4941 Utf8Str newPath(strNewPath);
4942 newPath.append(pcszMediumPath + strOldPath.length());
4943 unconst(m->strLocationFull) = newPath;
4944
4945 m->pVirtualBox->i_onMediumConfigChanged(this);
4946
4947 LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
4948 // we changed something
4949 return S_OK;
4950 }
4951
4952 // no change was necessary, signal error which the caller needs to interpret
4953 return VBOX_E_FILE_ERROR;
4954}
4955
4956/**
4957 * Returns the base medium of the media chain this medium is part of.
4958 *
4959 * The base medium is found by walking up the parent-child relationship axis.
4960 * If the medium doesn't have a parent (i.e. it's a base medium), it
4961 * returns itself in response to this method.
4962 *
4963 * @param aLevel Where to store the number of ancestors of this medium
4964 * (zero for the base), may be @c NULL.
4965 *
4966 * @note Locks medium tree for reading.
4967 */
4968ComObjPtr<Medium> Medium::i_getBase(uint32_t *aLevel /*= NULL*/)
4969{
4970 ComObjPtr<Medium> pBase;
4971
4972 /* it is possible that some previous/concurrent uninit has already cleared
4973 * the pVirtualBox reference, and in this case we don't need to continue */
4974 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4975 if (!pVirtualBox)
4976 return pBase;
4977
4978 /* we access m->pParent */
4979 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4980
4981 AutoCaller autoCaller(this);
4982 AssertReturn(autoCaller.isOk(), pBase);
4983
4984 pBase = this;
4985 uint32_t level = 0;
4986
4987 if (m->pParent)
4988 {
4989 for (;;)
4990 {
4991 AutoCaller baseCaller(pBase);
4992 AssertReturn(baseCaller.isOk(), pBase);
4993
4994 if (pBase->m->pParent.isNull())
4995 break;
4996
4997 pBase = pBase->m->pParent;
4998 ++level;
4999 }
5000 }
5001
5002 if (aLevel != NULL)
5003 *aLevel = level;
5004
5005 return pBase;
5006}
5007
5008/**
5009 * Returns the depth of this medium in the media chain.
5010 *
5011 * @note Locks medium tree for reading.
5012 */
5013uint32_t Medium::i_getDepth()
5014{
5015 /* it is possible that some previous/concurrent uninit has already cleared
5016 * the pVirtualBox reference, and in this case we don't need to continue */
5017 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
5018 if (!pVirtualBox)
5019 return 1;
5020
5021 /* we access m->pParent */
5022 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5023
5024 uint32_t cDepth = 0;
5025 ComObjPtr<Medium> pMedium(this);
5026 while (!pMedium.isNull())
5027 {
5028 AutoCaller autoCaller(this);
5029 AssertReturn(autoCaller.isOk(), cDepth + 1);
5030
5031 pMedium = pMedium->m->pParent;
5032 cDepth++;
5033 }
5034
5035 return cDepth;
5036}
5037
5038/**
5039 * Returns @c true if this medium cannot be modified because it has
5040 * dependents (children) or is part of the snapshot. Related to the medium
5041 * type and posterity, not to the current media state.
5042 *
5043 * @note Locks this object and medium tree for reading.
5044 */
5045bool Medium::i_isReadOnly()
5046{
5047 /* it is possible that some previous/concurrent uninit has already cleared
5048 * the pVirtualBox reference, and in this case we don't need to continue */
5049 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
5050 if (!pVirtualBox)
5051 return false;
5052
5053 /* we access children */
5054 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5055
5056 AutoCaller autoCaller(this);
5057 AssertComRCReturn(autoCaller.hrc(), false);
5058
5059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5060
5061 switch (m->type)
5062 {
5063 case MediumType_Normal:
5064 {
5065 if (i_getChildren().size() != 0)
5066 return true;
5067
5068 for (BackRefList::const_iterator it = m->backRefs.begin();
5069 it != m->backRefs.end(); ++it)
5070 if (it->llSnapshotIds.size() != 0)
5071 return true;
5072
5073 if (m->variant & MediumVariant_VmdkStreamOptimized)
5074 return true;
5075
5076 return false;
5077 }
5078 case MediumType_Immutable:
5079 case MediumType_MultiAttach:
5080 return true;
5081 case MediumType_Writethrough:
5082 case MediumType_Shareable:
5083 case MediumType_Readonly: /* explicit readonly media has no diffs */
5084 return false;
5085 default:
5086 break;
5087 }
5088
5089 AssertFailedReturn(false);
5090}
5091
5092/**
5093 * Internal method to update the medium's id. Must have caller + locking!
5094 */
5095void Medium::i_updateId(const Guid &id)
5096{
5097 unconst(m->id) = id;
5098}
5099
5100/**
5101 * Saves the settings of one medium.
5102 *
5103 * @note Caller MUST take care of the medium tree lock and caller.
5104 *
5105 * @param data Settings struct to be updated.
5106 * @param strHardDiskFolder Folder for which paths should be relative.
5107 */
5108void Medium::i_saveSettingsOne(settings::Medium &data, const Utf8Str &strHardDiskFolder)
5109{
5110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5111
5112 data.uuid = m->id;
5113
5114 // make path relative if needed
5115 if ( !strHardDiskFolder.isEmpty()
5116 && RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
5117 )
5118 data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
5119 else
5120 data.strLocation = m->strLocationFull;
5121 data.strFormat = m->strFormat;
5122
5123 /* optional, only for diffs, default is false */
5124 if (m->pParent)
5125 data.fAutoReset = m->autoReset;
5126 else
5127 data.fAutoReset = false;
5128
5129 /* optional */
5130 data.strDescription = m->strDescription;
5131
5132 /* optional properties */
5133 data.properties.clear();
5134
5135 /* handle iSCSI initiator secrets transparently */
5136 bool fHaveInitiatorSecretEncrypted = false;
5137 Utf8Str strCiphertext;
5138 settings::StringsMap::const_iterator itPln = m->mapProperties.find("InitiatorSecret");
5139 if ( itPln != m->mapProperties.end()
5140 && !itPln->second.isEmpty())
5141 {
5142 /* Encrypt the plain secret. If that does not work (i.e. no or wrong settings key
5143 * specified), just use the encrypted secret (if there is any). */
5144 int vrc = m->pVirtualBox->i_encryptSetting(itPln->second, &strCiphertext);
5145 if (RT_SUCCESS(vrc))
5146 fHaveInitiatorSecretEncrypted = true;
5147 }
5148 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
5149 it != m->mapProperties.end();
5150 ++it)
5151 {
5152 /* only save properties that have non-default values */
5153 if (!it->second.isEmpty())
5154 {
5155 const Utf8Str &name = it->first;
5156 const Utf8Str &value = it->second;
5157 bool fCreateOnly = false;
5158 for (MediumFormat::PropertyArray::const_iterator itf = m->formatObj->i_getProperties().begin();
5159 itf != m->formatObj->i_getProperties().end();
5160 ++itf)
5161 {
5162 if ( itf->strName.equals(name)
5163 && (itf->flags & VD_CFGKEY_CREATEONLY))
5164 {
5165 fCreateOnly = true;
5166 break;
5167 }
5168 }
5169 if (!fCreateOnly)
5170 /* do NOT store the plain InitiatorSecret */
5171 if ( !fHaveInitiatorSecretEncrypted
5172 || !name.equals("InitiatorSecret"))
5173 data.properties[name] = value;
5174 }
5175 }
5176 if (fHaveInitiatorSecretEncrypted)
5177 data.properties["InitiatorSecretEncrypted"] = strCiphertext;
5178
5179 /* only for base media */
5180 if (m->pParent.isNull())
5181 data.hdType = m->type;
5182}
5183
5184/**
5185 * Saves medium data by putting it into the provided data structure.
5186 * The settings of all children is saved, too.
5187 *
5188 * @param data Settings struct to be updated.
5189 * @param strHardDiskFolder Folder for which paths should be relative.
5190 *
5191 * @note Locks this object, medium tree and children for reading.
5192 */
5193HRESULT Medium::i_saveSettings(settings::Medium &data,
5194 const Utf8Str &strHardDiskFolder)
5195{
5196 /* we access m->pParent */
5197 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5198
5199 AutoCaller autoCaller(this);
5200 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5201
5202 MediaList llMediaTodo;
5203 llMediaTodo.push_back(this);
5204 std::list<settings::Medium *> llSettingsTodo;
5205 llSettingsTodo.push_back(&data);
5206
5207 while (!llMediaTodo.empty())
5208 {
5209 ComObjPtr<Medium> pMedium = llMediaTodo.front();
5210 llMediaTodo.pop_front();
5211 settings::Medium *current = llSettingsTodo.front();
5212 llSettingsTodo.pop_front();
5213
5214 AutoCaller mediumCaller(pMedium);
5215 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
5216
5217 pMedium->i_saveSettingsOne(*current, strHardDiskFolder);
5218
5219 /* save all children */
5220 MediaList::const_iterator itBegin = pMedium->i_getChildren().begin();
5221 MediaList::const_iterator itEnd = pMedium->i_getChildren().end();
5222 for (MediaList::const_iterator it = itBegin; it != itEnd; ++it)
5223 {
5224 llMediaTodo.push_back(*it);
5225 current->llChildren.push_back(settings::Medium::Empty);
5226 llSettingsTodo.push_back(&current->llChildren.back());
5227 }
5228 }
5229
5230 return S_OK;
5231}
5232
5233/**
5234 * Constructs a medium lock list for this medium. The lock is not taken.
5235 *
5236 * @note Caller MUST NOT hold the media tree or medium lock.
5237 *
5238 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
5239 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
5240 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
5241 * @param pToLockWrite If not NULL, associate a write lock with this medium object.
5242 * @param fMediumLockWriteAll Whether to associate a write lock to all other media too.
5243 * @param pToBeParent Medium which will become the parent of this medium.
5244 * @param mediumLockList Where to store the resulting list.
5245 */
5246HRESULT Medium::i_createMediumLockList(bool fFailIfInaccessible,
5247 Medium *pToLockWrite,
5248 bool fMediumLockWriteAll,
5249 Medium *pToBeParent,
5250 MediumLockList &mediumLockList)
5251{
5252 /** @todo r=klaus this needs to be reworked, as the code below uses
5253 * i_getParent without holding the tree lock, and changing this is
5254 * a significant amount of effort. */
5255 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5256 Assert(!isWriteLockOnCurrentThread());
5257
5258 AutoCaller autoCaller(this);
5259 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5260
5261 HRESULT hrc = S_OK;
5262
5263 /* paranoid sanity checking if the medium has a to-be parent medium */
5264 if (pToBeParent)
5265 {
5266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5267 ComAssertRet(i_getParent().isNull(), E_FAIL);
5268 ComAssertRet(i_getChildren().size() == 0, E_FAIL);
5269 }
5270
5271 ErrorInfoKeeper eik;
5272 MultiResult mrc(S_OK);
5273
5274 ComObjPtr<Medium> pMedium = this;
5275 while (!pMedium.isNull())
5276 {
5277 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5278
5279 /* Accessibility check must be first, otherwise locking interferes
5280 * with getting the medium state. Lock lists are not created for
5281 * fun, and thus getting the medium status is no luxury. */
5282 MediumState_T mediumState = pMedium->i_getState();
5283 if (mediumState == MediumState_Inaccessible)
5284 {
5285 alock.release();
5286 hrc = pMedium->i_queryInfo(false /* fSetImageId */, false /* fSetParentId */, autoCaller);
5287 alock.acquire();
5288 if (FAILED(hrc)) return hrc;
5289
5290 mediumState = pMedium->i_getState();
5291 if (mediumState == MediumState_Inaccessible)
5292 {
5293 // ignore inaccessible ISO media and silently return S_OK,
5294 // otherwise VM startup (esp. restore) may fail without good reason
5295 if (!fFailIfInaccessible)
5296 return S_OK;
5297
5298 // otherwise report an error
5299 Bstr error;
5300 hrc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
5301 if (FAILED(hrc)) return hrc;
5302
5303 /* collect multiple errors */
5304 eik.restore();
5305 Assert(!error.isEmpty());
5306 mrc = setError(E_FAIL,
5307 "%ls",
5308 error.raw());
5309 // error message will be something like
5310 // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
5311 eik.fetch();
5312 }
5313 }
5314
5315 if (pMedium == pToLockWrite)
5316 mediumLockList.Prepend(pMedium, true);
5317 else
5318 mediumLockList.Prepend(pMedium, fMediumLockWriteAll);
5319
5320 pMedium = pMedium->i_getParent();
5321 if (pMedium.isNull() && pToBeParent)
5322 {
5323 pMedium = pToBeParent;
5324 pToBeParent = NULL;
5325 }
5326 }
5327
5328 return mrc;
5329}
5330
5331/**
5332 * Creates a new differencing storage unit using the format of the given target
5333 * medium and the location. Note that @c aTarget must be NotCreated.
5334 *
5335 * The @a aMediumLockList parameter contains the associated medium lock list,
5336 * which must be in locked state. If @a aWait is @c true then the caller is
5337 * responsible for unlocking.
5338 *
5339 * If @a aProgress is not NULL but the object it points to is @c null then a
5340 * new progress object will be created and assigned to @a *aProgress on
5341 * success, otherwise the existing progress object is used. If @a aProgress is
5342 * NULL, then no progress object is created/used at all.
5343 *
5344 * When @a aWait is @c false, this method will create a thread to perform the
5345 * create operation asynchronously and will return immediately. Otherwise, it
5346 * will perform the operation on the calling thread and will not return to the
5347 * caller until the operation is completed. Note that @a aProgress cannot be
5348 * NULL when @a aWait is @c false (this method will assert in this case).
5349 *
5350 * @param aTarget Target medium.
5351 * @param aVariant Precise medium variant to create.
5352 * @param aMediumLockList List of media which should be locked.
5353 * @param aProgress Where to find/store a Progress object to track
5354 * operation completion.
5355 * @param aWait @c true if this method should block instead of
5356 * creating an asynchronous thread.
5357 * @param aNotify Notify about media for which metadata is changed
5358 * during execution of the function.
5359 *
5360 * @note Locks this object and @a aTarget for writing.
5361 */
5362HRESULT Medium::i_createDiffStorage(ComObjPtr<Medium> &aTarget,
5363 MediumVariant_T aVariant,
5364 MediumLockList *aMediumLockList,
5365 ComObjPtr<Progress> *aProgress,
5366 bool aWait,
5367 bool aNotify)
5368{
5369 AssertReturn(!aTarget.isNull(), E_FAIL);
5370 AssertReturn(aMediumLockList, E_FAIL);
5371 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5372
5373 AutoCaller autoCaller(this);
5374 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5375
5376 AutoCaller targetCaller(aTarget);
5377 if (FAILED(targetCaller.hrc())) return targetCaller.hrc();
5378
5379 HRESULT hrc = S_OK;
5380 ComObjPtr<Progress> pProgress;
5381 Medium::Task *pTask = NULL;
5382
5383 try
5384 {
5385 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
5386
5387 ComAssertThrow( m->type != MediumType_Writethrough
5388 && m->type != MediumType_Shareable
5389 && m->type != MediumType_Readonly, E_FAIL);
5390 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
5391
5392 if (aTarget->m->state != MediumState_NotCreated)
5393 throw aTarget->i_setStateError();
5394
5395 /* Check that the medium is not attached to the current state of
5396 * any VM referring to it. */
5397 for (BackRefList::const_iterator it = m->backRefs.begin();
5398 it != m->backRefs.end();
5399 ++it)
5400 {
5401 if (it->fInCurState)
5402 {
5403 /* Note: when a VM snapshot is being taken, all normal media
5404 * attached to the VM in the current state will be, as an
5405 * exception, also associated with the snapshot which is about
5406 * to create (see SnapshotMachine::init()) before deassociating
5407 * them from the current state (which takes place only on
5408 * success in Machine::fixupHardDisks()), so that the size of
5409 * snapshotIds will be 1 in this case. The extra condition is
5410 * used to filter out this legal situation. */
5411 if (it->llSnapshotIds.size() == 0)
5412 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5413 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"),
5414 m->strLocationFull.c_str(), it->machineId.raw());
5415
5416 Assert(it->llSnapshotIds.size() == 1);
5417 }
5418 }
5419
5420 if (aProgress != NULL)
5421 {
5422 /* use the existing progress object... */
5423 pProgress = *aProgress;
5424
5425 /* ...but create a new one if it is null */
5426 if (pProgress.isNull())
5427 {
5428 pProgress.createObject();
5429 hrc = pProgress->init(m->pVirtualBox,
5430 static_cast<IMedium*>(this),
5431 BstrFmt(tr("Creating differencing medium storage unit '%s'"),
5432 aTarget->m->strLocationFull.c_str()).raw(),
5433 TRUE /* aCancelable */);
5434 if (FAILED(hrc))
5435 throw hrc;
5436 }
5437 }
5438
5439 /* setup task object to carry out the operation sync/async */
5440 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
5441 aMediumLockList,
5442 aWait /* fKeepMediumLockList */,
5443 aNotify);
5444 hrc = pTask->hrc();
5445 AssertComRC(hrc);
5446 if (FAILED(hrc))
5447 throw hrc;
5448
5449 /* register a task (it will deregister itself when done) */
5450 ++m->numCreateDiffTasks;
5451 Assert(m->numCreateDiffTasks != 0); /* overflow? */
5452
5453 aTarget->m->state = MediumState_Creating;
5454 }
5455 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
5456
5457 if (SUCCEEDED(hrc))
5458 {
5459 if (aWait)
5460 {
5461 hrc = pTask->runNow();
5462 delete pTask;
5463 }
5464 else
5465 hrc = pTask->createThread();
5466 pTask = NULL;
5467 if (SUCCEEDED(hrc) && aProgress != NULL)
5468 *aProgress = pProgress;
5469 }
5470 else if (pTask != NULL)
5471 delete pTask;
5472
5473 return hrc;
5474}
5475
5476/**
5477 * Returns a preferred format for differencing media.
5478 */
5479Utf8Str Medium::i_getPreferredDiffFormat()
5480{
5481 AutoCaller autoCaller(this);
5482 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
5483
5484 /* check that our own format supports diffs */
5485 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5486 {
5487 /* use the default format if not */
5488 Utf8Str tmp;
5489 m->pVirtualBox->i_getDefaultHardDiskFormat(tmp);
5490 return tmp;
5491 }
5492
5493 /* m->strFormat is const, no need to lock */
5494 return m->strFormat;
5495}
5496
5497/**
5498 * Returns a preferred variant for differencing media.
5499 */
5500MediumVariant_T Medium::i_getPreferredDiffVariant()
5501{
5502 AutoCaller autoCaller(this);
5503 AssertComRCReturn(autoCaller.hrc(), MediumVariant_Standard);
5504
5505 /* check that our own format supports diffs */
5506 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5507 return MediumVariant_Standard;
5508
5509 /* m->variant is const, no need to lock */
5510 ULONG mediumVariantFlags = (ULONG)m->variant;
5511 mediumVariantFlags &= ~(ULONG)(MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk);
5512 mediumVariantFlags |= MediumVariant_Diff;
5513 return (MediumVariant_T)mediumVariantFlags;
5514}
5515
5516/**
5517 * Implementation for the public Medium::Close() with the exception of calling
5518 * VirtualBox::saveRegistries(), in case someone wants to call this for several
5519 * media.
5520 *
5521 * After this returns with success, uninit() has been called on the medium, and
5522 * the object is no longer usable ("not ready" state).
5523 *
5524 * @param autoCaller AutoCaller instance which must have been created on the caller's
5525 * stack for this medium. This gets released hereupon
5526 * which the Medium instance gets uninitialized.
5527 * @return
5528 */
5529HRESULT Medium::i_close(AutoCaller &autoCaller)
5530{
5531 // must temporarily drop the caller, need the tree lock first
5532 autoCaller.release();
5533
5534 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
5535 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
5536 this->lockHandle()
5537 COMMA_LOCKVAL_SRC_POS);
5538
5539 autoCaller.add();
5540 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5541
5542 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5543 while (m->queryInfoRunning)
5544 {
5545 autoCaller.release();
5546 multilock.release();
5547 /* Must not hold the media tree lock, as Medium::i_queryInfo needs
5548 * this lock and thus we would run into a deadlock here. */
5549 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5550 /* must not hold the object lock now */
5551 Assert(!isWriteLockOnCurrentThread());
5552 {
5553 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5554 }
5555 multilock.acquire();
5556 autoCaller.add();
5557 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5558 }
5559
5560 LogFlowFunc(("ENTER for %s\n", i_getLocationFull().c_str()));
5561
5562 bool wasCreated = true;
5563
5564 switch (m->state)
5565 {
5566 case MediumState_NotCreated:
5567 wasCreated = false;
5568 break;
5569 case MediumState_Created:
5570 case MediumState_Inaccessible:
5571 break;
5572 default:
5573 return i_setStateError();
5574 }
5575
5576 if (m->fClosing)
5577 return setError(VBOX_E_OBJECT_IN_USE,
5578 tr("Medium '%s' cannot be closed because it is already in the process of being closed", ""),
5579 m->strLocationFull.c_str());
5580
5581 if (m->backRefs.size() != 0)
5582 return setError(VBOX_E_OBJECT_IN_USE,
5583 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines", "",
5584 m->backRefs.size()),
5585 m->strLocationFull.c_str(), m->backRefs.size());
5586
5587 // perform extra media-dependent close checks
5588 HRESULT hrc = i_canClose();
5589 if (FAILED(hrc)) return hrc;
5590
5591 m->fClosing = true;
5592
5593 if (wasCreated)
5594 {
5595 // remove from the list of known media before performing actual
5596 // uninitialization (to keep the media registry consistent on
5597 // failure to do so)
5598 hrc = i_unregisterWithVirtualBox();
5599 if (FAILED(hrc)) return hrc;
5600
5601 multilock.release();
5602 // Release the AutoCaller now, as otherwise uninit() will simply hang.
5603 // Needs to be done before mark the registries as modified and saving
5604 // the registry, as otherwise there may be a deadlock with someone else
5605 // closing this object while we're in i_saveModifiedRegistries(), which
5606 // needs the media tree lock, which the other thread holds until after
5607 // uninit() below.
5608 autoCaller.release();
5609 i_markRegistriesModified();
5610 m->pVirtualBox->i_saveModifiedRegistries();
5611 }
5612 else
5613 {
5614 multilock.release();
5615 // release the AutoCaller, as otherwise uninit() will simply hang
5616 autoCaller.release();
5617 }
5618
5619 uninit();
5620
5621 LogFlowFuncLeave();
5622
5623 return hrc;
5624}
5625
5626/**
5627 * Deletes the medium storage unit.
5628 *
5629 * If @a aProgress is not NULL but the object it points to is @c null then a new
5630 * progress object will be created and assigned to @a *aProgress on success,
5631 * otherwise the existing progress object is used. If Progress is NULL, then no
5632 * progress object is created/used at all.
5633 *
5634 * When @a aWait is @c false, this method will create a thread to perform the
5635 * delete operation asynchronously and will return immediately. Otherwise, it
5636 * will perform the operation on the calling thread and will not return to the
5637 * caller until the operation is completed. Note that @a aProgress cannot be
5638 * NULL when @a aWait is @c false (this method will assert in this case).
5639 *
5640 * @param aProgress Where to find/store a Progress object to track operation
5641 * completion.
5642 * @param aWait @c true if this method should block instead of creating
5643 * an asynchronous thread.
5644 * @param aNotify Notify about media for which metadata is changed
5645 * during execution of the function.
5646 *
5647 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
5648 * writing.
5649 */
5650HRESULT Medium::i_deleteStorage(ComObjPtr<Progress> *aProgress,
5651 bool aWait, bool aNotify)
5652{
5653 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5654
5655 HRESULT hrc = S_OK;
5656 ComObjPtr<Progress> pProgress;
5657 Medium::Task *pTask = NULL;
5658
5659 try
5660 {
5661 /* we're accessing the media tree, and i_canClose() needs it too */
5662 AutoWriteLock treelock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5663
5664 AutoCaller autoCaller(this);
5665 AssertComRCThrowRC(autoCaller.hrc());
5666
5667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5668
5669 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, i_getLocationFull().c_str() ));
5670
5671 if ( !(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
5672 | MediumFormatCapabilities_CreateFixed
5673 | MediumFormatCapabilities_File)))
5674 throw setError(VBOX_E_NOT_SUPPORTED,
5675 tr("Medium format '%s' does not support storage deletion"),
5676 m->strFormat.c_str());
5677
5678 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5679 /** @todo r=klaus would be great if this could be moved to the async
5680 * part of the operation as it can take quite a while */
5681 while (m->queryInfoRunning)
5682 {
5683 alock.release();
5684 autoCaller.release();
5685 treelock.release();
5686 /* Must not hold the media tree lock or the object lock, as
5687 * Medium::i_queryInfo needs this lock and thus we would run
5688 * into a deadlock here. */
5689 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5690 Assert(!isWriteLockOnCurrentThread());
5691 {
5692 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5693 }
5694 treelock.acquire();
5695 autoCaller.add();
5696 AssertComRCThrowRC(autoCaller.hrc());
5697 alock.acquire();
5698 }
5699
5700 /* Note that we are fine with Inaccessible state too: a) for symmetry
5701 * with create calls and b) because it doesn't really harm to try, if
5702 * it is really inaccessible, the delete operation will fail anyway.
5703 * Accepting Inaccessible state is especially important because all
5704 * registered media are initially Inaccessible upon VBoxSVC startup
5705 * until COMGETTER(RefreshState) is called. Accept Deleting state
5706 * because some callers need to put the medium in this state early
5707 * to prevent races. */
5708 switch (m->state)
5709 {
5710 case MediumState_Created:
5711 case MediumState_Deleting:
5712 case MediumState_Inaccessible:
5713 break;
5714 default:
5715 throw i_setStateError();
5716 }
5717
5718 if (m->backRefs.size() != 0)
5719 {
5720 Utf8Str strMachines;
5721 for (BackRefList::const_iterator it = m->backRefs.begin();
5722 it != m->backRefs.end();
5723 ++it)
5724 {
5725 const BackRef &b = *it;
5726 if (strMachines.length())
5727 strMachines.append(", ");
5728 strMachines.append(b.machineId.toString().c_str());
5729 }
5730#ifdef DEBUG
5731 i_dumpBackRefs();
5732#endif
5733 throw setError(VBOX_E_OBJECT_IN_USE,
5734 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s",
5735 "", m->backRefs.size()),
5736 m->strLocationFull.c_str(),
5737 m->backRefs.size(),
5738 strMachines.c_str());
5739 }
5740
5741 hrc = i_canClose();
5742 if (FAILED(hrc))
5743 throw hrc;
5744
5745 /* go to Deleting state, so that the medium is not actually locked */
5746 if (m->state != MediumState_Deleting)
5747 {
5748 hrc = i_markForDeletion();
5749 if (FAILED(hrc))
5750 throw hrc;
5751 }
5752
5753 /* Build the medium lock list. */
5754 MediumLockList *pMediumLockList(new MediumLockList());
5755 alock.release();
5756 autoCaller.release();
5757 treelock.release();
5758 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
5759 this /* pToLockWrite */,
5760 false /* fMediumLockWriteAll */,
5761 NULL,
5762 *pMediumLockList);
5763 treelock.acquire();
5764 autoCaller.add();
5765 AssertComRCThrowRC(autoCaller.hrc());
5766 alock.acquire();
5767 if (FAILED(hrc))
5768 {
5769 delete pMediumLockList;
5770 throw hrc;
5771 }
5772
5773 alock.release();
5774 autoCaller.release();
5775 treelock.release();
5776 hrc = pMediumLockList->Lock();
5777 treelock.acquire();
5778 autoCaller.add();
5779 AssertComRCThrowRC(autoCaller.hrc());
5780 alock.acquire();
5781 if (FAILED(hrc))
5782 {
5783 delete pMediumLockList;
5784 throw setError(hrc,
5785 tr("Failed to lock media when deleting '%s'"),
5786 i_getLocationFull().c_str());
5787 }
5788
5789 /* try to remove from the list of known media before performing
5790 * actual deletion (we favor the consistency of the media registry
5791 * which would have been broken if unregisterWithVirtualBox() failed
5792 * after we successfully deleted the storage) */
5793 hrc = i_unregisterWithVirtualBox();
5794 if (FAILED(hrc))
5795 throw hrc;
5796 // no longer need lock
5797 alock.release();
5798 autoCaller.release();
5799 treelock.release();
5800 i_markRegistriesModified();
5801
5802 if (aProgress != NULL)
5803 {
5804 /* use the existing progress object... */
5805 pProgress = *aProgress;
5806
5807 /* ...but create a new one if it is null */
5808 if (pProgress.isNull())
5809 {
5810 pProgress.createObject();
5811 hrc = pProgress->init(m->pVirtualBox,
5812 static_cast<IMedium*>(this),
5813 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
5814 FALSE /* aCancelable */);
5815 if (FAILED(hrc))
5816 throw hrc;
5817 }
5818 }
5819
5820 /* setup task object to carry out the operation sync/async */
5821 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList, false, aNotify);
5822 hrc = pTask->hrc();
5823 AssertComRC(hrc);
5824 if (FAILED(hrc))
5825 throw hrc;
5826 }
5827 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
5828
5829 if (SUCCEEDED(hrc))
5830 {
5831 if (aWait)
5832 {
5833 hrc = pTask->runNow();
5834 delete pTask;
5835 }
5836 else
5837 hrc = pTask->createThread();
5838 pTask = NULL;
5839 if (SUCCEEDED(hrc) && aProgress != NULL)
5840 *aProgress = pProgress;
5841 }
5842 else
5843 {
5844 if (pTask)
5845 delete pTask;
5846
5847 /* Undo deleting state if necessary. */
5848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5849 /* Make sure that any error signalled by unmarkForDeletion() is not
5850 * ending up in the error list (if the caller uses MultiResult). It
5851 * usually is spurious, as in most cases the medium hasn't been marked
5852 * for deletion when the error was thrown above. */
5853 ErrorInfoKeeper eik;
5854 i_unmarkForDeletion();
5855 }
5856
5857 return hrc;
5858}
5859
5860/**
5861 * Mark a medium for deletion.
5862 *
5863 * @note Caller must hold the write lock on this medium!
5864 */
5865HRESULT Medium::i_markForDeletion()
5866{
5867 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5868 switch (m->state)
5869 {
5870 case MediumState_Created:
5871 case MediumState_Inaccessible:
5872 m->preLockState = m->state;
5873 m->state = MediumState_Deleting;
5874 return S_OK;
5875 default:
5876 return i_setStateError();
5877 }
5878}
5879
5880/**
5881 * Removes the "mark for deletion".
5882 *
5883 * @note Caller must hold the write lock on this medium!
5884 */
5885HRESULT Medium::i_unmarkForDeletion()
5886{
5887 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5888 switch (m->state)
5889 {
5890 case MediumState_Deleting:
5891 m->state = m->preLockState;
5892 return S_OK;
5893 default:
5894 return i_setStateError();
5895 }
5896}
5897
5898/**
5899 * Mark a medium for deletion which is in locked state.
5900 *
5901 * @note Caller must hold the write lock on this medium!
5902 */
5903HRESULT Medium::i_markLockedForDeletion()
5904{
5905 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5906 if ( ( m->state == MediumState_LockedRead
5907 || m->state == MediumState_LockedWrite)
5908 && m->preLockState == MediumState_Created)
5909 {
5910 m->preLockState = MediumState_Deleting;
5911 return S_OK;
5912 }
5913 else
5914 return i_setStateError();
5915}
5916
5917/**
5918 * Removes the "mark for deletion" for a medium in locked state.
5919 *
5920 * @note Caller must hold the write lock on this medium!
5921 */
5922HRESULT Medium::i_unmarkLockedForDeletion()
5923{
5924 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5925 if ( ( m->state == MediumState_LockedRead
5926 || m->state == MediumState_LockedWrite)
5927 && m->preLockState == MediumState_Deleting)
5928 {
5929 m->preLockState = MediumState_Created;
5930 return S_OK;
5931 }
5932 else
5933 return i_setStateError();
5934}
5935
5936/**
5937 * Queries the preferred merge direction from this to the other medium, i.e.
5938 * the one which requires the least amount of I/O and therefore time and
5939 * disk consumption.
5940 *
5941 * @returns Status code.
5942 * @retval E_FAIL in case determining the merge direction fails for some reason,
5943 * for example if getting the size of the media fails. There is no
5944 * error set though and the caller is free to continue to find out
5945 * what was going wrong later. Leaves fMergeForward unset.
5946 * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
5947 * An error is set.
5948 * @param pOther The other medium to merge with.
5949 * @param fMergeForward Resulting preferred merge direction (out).
5950 */
5951HRESULT Medium::i_queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
5952 bool &fMergeForward)
5953{
5954 AssertReturn(pOther != NULL, E_FAIL);
5955 AssertReturn(pOther != this, E_FAIL);
5956
5957 HRESULT hrc = S_OK;
5958 bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
5959
5960 try
5961 {
5962 // locking: we need the tree lock first because we access parent pointers
5963 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5964
5965 AutoCaller autoCaller(this);
5966 AssertComRCThrowRC(autoCaller.hrc());
5967
5968 AutoCaller otherCaller(pOther);
5969 AssertComRCThrowRC(otherCaller.hrc());
5970
5971 /* more sanity checking and figuring out the current merge direction */
5972 ComObjPtr<Medium> pMedium = i_getParent();
5973 while (!pMedium.isNull() && pMedium != pOther)
5974 pMedium = pMedium->i_getParent();
5975 if (pMedium == pOther)
5976 fThisParent = false;
5977 else
5978 {
5979 pMedium = pOther->i_getParent();
5980 while (!pMedium.isNull() && pMedium != this)
5981 pMedium = pMedium->i_getParent();
5982 if (pMedium == this)
5983 fThisParent = true;
5984 else
5985 {
5986 Utf8Str tgtLoc;
5987 {
5988 AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
5989 tgtLoc = pOther->i_getLocationFull();
5990 }
5991
5992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5993 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5994 tr("Media '%s' and '%s' are unrelated"),
5995 m->strLocationFull.c_str(), tgtLoc.c_str());
5996 }
5997 }
5998
5999 /*
6000 * Figure out the preferred merge direction. The current way is to
6001 * get the current sizes of file based images and select the merge
6002 * direction depending on the size.
6003 *
6004 * Can't use the VD API to get current size here as the media might
6005 * be write locked by a running VM. Resort to RTFileQuerySize().
6006 */
6007 int vrc = VINF_SUCCESS;
6008 uint64_t cbMediumThis = 0;
6009 uint64_t cbMediumOther = 0;
6010
6011 if (i_isMediumFormatFile() && pOther->i_isMediumFormatFile())
6012 {
6013 vrc = RTFileQuerySizeByPath(this->i_getLocationFull().c_str(), &cbMediumThis);
6014 if (RT_SUCCESS(vrc))
6015 {
6016 vrc = RTFileQuerySizeByPath(pOther->i_getLocationFull().c_str(),
6017 &cbMediumOther);
6018 }
6019
6020 if (RT_FAILURE(vrc))
6021 hrc = E_FAIL;
6022 else
6023 {
6024 /*
6025 * Check which merge direction might be more optimal.
6026 * This method is not bullet proof of course as there might
6027 * be overlapping blocks in the images so the file size is
6028 * not the best indicator but it is good enough for our purpose
6029 * and everything else is too complicated, especially when the
6030 * media are used by a running VM.
6031 */
6032
6033 uint32_t mediumVariants = MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized;
6034 uint32_t mediumCaps = MediumFormatCapabilities_CreateDynamic | MediumFormatCapabilities_File;
6035
6036 bool fDynamicOther = pOther->i_getMediumFormat()->i_getCapabilities() & mediumCaps
6037 && pOther->i_getVariant() & ~mediumVariants;
6038 bool fDynamicThis = i_getMediumFormat()->i_getCapabilities() & mediumCaps
6039 && i_getVariant() & ~mediumVariants;
6040 bool fMergeIntoThis = (fDynamicThis && !fDynamicOther)
6041 || (fDynamicThis == fDynamicOther && cbMediumThis > cbMediumOther);
6042 fMergeForward = fMergeIntoThis != fThisParent;
6043 }
6044 }
6045 }
6046 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6047
6048 return hrc;
6049}
6050
6051/**
6052 * Prepares this (source) medium, target medium and all intermediate media
6053 * for the merge operation.
6054 *
6055 * This method is to be called prior to calling the #mergeTo() to perform
6056 * necessary consistency checks and place involved media to appropriate
6057 * states. If #mergeTo() is not called or fails, the state modifications
6058 * performed by this method must be undone by #i_cancelMergeTo().
6059 *
6060 * See #mergeTo() for more information about merging.
6061 *
6062 * @param pTarget Target medium.
6063 * @param aMachineId Allowed machine attachment. NULL means do not check.
6064 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
6065 * do not check.
6066 * @param fLockMedia Flag whether to lock the medium lock list or not.
6067 * If set to false and the medium lock list locking fails
6068 * later you must call #i_cancelMergeTo().
6069 * @param fMergeForward Resulting merge direction (out).
6070 * @param pParentForTarget New parent for target medium after merge (out).
6071 * @param aChildrenToReparent Medium lock list containing all children of the
6072 * source which will have to be reparented to the target
6073 * after merge (out).
6074 * @param aMediumLockList Medium locking information (out).
6075 *
6076 * @note Locks medium tree for reading. Locks this object, aTarget and all
6077 * intermediate media for writing.
6078 */
6079HRESULT Medium::i_prepareMergeTo(const ComObjPtr<Medium> &pTarget,
6080 const Guid *aMachineId,
6081 const Guid *aSnapshotId,
6082 bool fLockMedia,
6083 bool &fMergeForward,
6084 ComObjPtr<Medium> &pParentForTarget,
6085 MediumLockList * &aChildrenToReparent,
6086 MediumLockList * &aMediumLockList)
6087{
6088 AssertReturn(pTarget != NULL, E_FAIL);
6089 AssertReturn(pTarget != this, E_FAIL);
6090
6091 HRESULT hrc = S_OK;
6092 fMergeForward = false;
6093 pParentForTarget.setNull();
6094 Assert(aChildrenToReparent == NULL);
6095 aChildrenToReparent = NULL;
6096 Assert(aMediumLockList == NULL);
6097 aMediumLockList = NULL;
6098
6099 try
6100 {
6101 // locking: we need the tree lock first because we access parent pointers
6102 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6103
6104 AutoCaller autoCaller(this);
6105 AssertComRCThrowRC(autoCaller.hrc());
6106
6107 AutoCaller targetCaller(pTarget);
6108 AssertComRCThrowRC(targetCaller.hrc());
6109
6110 /* more sanity checking and figuring out the merge direction */
6111 ComObjPtr<Medium> pMedium = i_getParent();
6112 while (!pMedium.isNull() && pMedium != pTarget)
6113 pMedium = pMedium->i_getParent();
6114 if (pMedium == pTarget)
6115 fMergeForward = false;
6116 else
6117 {
6118 pMedium = pTarget->i_getParent();
6119 while (!pMedium.isNull() && pMedium != this)
6120 pMedium = pMedium->i_getParent();
6121 if (pMedium == this)
6122 fMergeForward = true;
6123 else
6124 {
6125 Utf8Str tgtLoc;
6126 {
6127 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6128 tgtLoc = pTarget->i_getLocationFull();
6129 }
6130
6131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6132 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6133 tr("Media '%s' and '%s' are unrelated"),
6134 m->strLocationFull.c_str(), tgtLoc.c_str());
6135 }
6136 }
6137
6138 /* Build the lock list. */
6139 aMediumLockList = new MediumLockList();
6140 targetCaller.release();
6141 autoCaller.release();
6142 treeLock.release();
6143 if (fMergeForward)
6144 hrc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
6145 pTarget /* pToLockWrite */,
6146 false /* fMediumLockWriteAll */,
6147 NULL,
6148 *aMediumLockList);
6149 else
6150 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
6151 pTarget /* pToLockWrite */,
6152 false /* fMediumLockWriteAll */,
6153 NULL,
6154 *aMediumLockList);
6155 treeLock.acquire();
6156 autoCaller.add();
6157 AssertComRCThrowRC(autoCaller.hrc());
6158 targetCaller.add();
6159 AssertComRCThrowRC(targetCaller.hrc());
6160 if (FAILED(hrc))
6161 throw hrc;
6162
6163 /* Sanity checking, must be after lock list creation as it depends on
6164 * valid medium states. The medium objects must be accessible. Only
6165 * do this if immediate locking is requested, otherwise it fails when
6166 * we construct a medium lock list for an already running VM. Snapshot
6167 * deletion uses this to simplify its life. */
6168 if (fLockMedia)
6169 {
6170 {
6171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6172 if (m->state != MediumState_Created)
6173 throw i_setStateError();
6174 }
6175 {
6176 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6177 if (pTarget->m->state != MediumState_Created)
6178 throw pTarget->i_setStateError();
6179 }
6180 }
6181
6182 /* check medium attachment and other sanity conditions */
6183 if (fMergeForward)
6184 {
6185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6186 if (i_getChildren().size() > 1)
6187 {
6188 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6189 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6190 m->strLocationFull.c_str(), i_getChildren().size());
6191 }
6192 /* One backreference is only allowed if the machine ID is not empty
6193 * and it matches the machine the medium is attached to (including
6194 * the snapshot ID if not empty). */
6195 if ( m->backRefs.size() != 0
6196 && ( !aMachineId
6197 || m->backRefs.size() != 1
6198 || aMachineId->isZero()
6199 || *i_getFirstMachineBackrefId() != *aMachineId
6200 || ( (!aSnapshotId || !aSnapshotId->isZero())
6201 && *i_getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
6202 throw setError(VBOX_E_OBJECT_IN_USE,
6203 tr("Medium '%s' is attached to %d virtual machines", "", m->backRefs.size()),
6204 m->strLocationFull.c_str(), m->backRefs.size());
6205 if (m->type == MediumType_Immutable)
6206 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6207 tr("Medium '%s' is immutable"),
6208 m->strLocationFull.c_str());
6209 if (m->type == MediumType_MultiAttach)
6210 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6211 tr("Medium '%s' is multi-attach"),
6212 m->strLocationFull.c_str());
6213 }
6214 else
6215 {
6216 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6217 if (pTarget->i_getChildren().size() > 1)
6218 {
6219 throw setError(VBOX_E_OBJECT_IN_USE,
6220 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6221 pTarget->m->strLocationFull.c_str(),
6222 pTarget->i_getChildren().size());
6223 }
6224 if (pTarget->m->type == MediumType_Immutable)
6225 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6226 tr("Medium '%s' is immutable"),
6227 pTarget->m->strLocationFull.c_str());
6228 if (pTarget->m->type == MediumType_MultiAttach)
6229 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6230 tr("Medium '%s' is multi-attach"),
6231 pTarget->m->strLocationFull.c_str());
6232 }
6233 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
6234 ComObjPtr<Medium> pLastIntermediate = pLast->i_getParent();
6235 for (pLast = pLastIntermediate;
6236 !pLast.isNull() && pLast != pTarget && pLast != this;
6237 pLast = pLast->i_getParent())
6238 {
6239 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
6240 if (pLast->i_getChildren().size() > 1)
6241 {
6242 throw setError(VBOX_E_OBJECT_IN_USE,
6243 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
6244 pLast->m->strLocationFull.c_str(),
6245 pLast->i_getChildren().size());
6246 }
6247 if (pLast->m->backRefs.size() != 0)
6248 throw setError(VBOX_E_OBJECT_IN_USE,
6249 tr("Medium '%s' is attached to %d virtual machines", "", pLast->m->backRefs.size()),
6250 pLast->m->strLocationFull.c_str(),
6251 pLast->m->backRefs.size());
6252
6253 }
6254
6255 /* Update medium states appropriately */
6256 {
6257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6258
6259 if (m->state == MediumState_Created)
6260 {
6261 hrc = i_markForDeletion();
6262 if (FAILED(hrc))
6263 throw hrc;
6264 }
6265 else
6266 {
6267 if (fLockMedia)
6268 throw i_setStateError();
6269 else if ( m->state == MediumState_LockedWrite
6270 || m->state == MediumState_LockedRead)
6271 {
6272 /* Either mark it for deletion in locked state or allow
6273 * others to have done so. */
6274 if (m->preLockState == MediumState_Created)
6275 i_markLockedForDeletion();
6276 else if (m->preLockState != MediumState_Deleting)
6277 throw i_setStateError();
6278 }
6279 else
6280 throw i_setStateError();
6281 }
6282 }
6283
6284 if (fMergeForward)
6285 {
6286 /* we will need parent to reparent target */
6287 pParentForTarget = i_getParent();
6288 }
6289 else
6290 {
6291 /* we will need to reparent children of the source */
6292 aChildrenToReparent = new MediumLockList();
6293 for (MediaList::const_iterator it = i_getChildren().begin();
6294 it != i_getChildren().end();
6295 ++it)
6296 {
6297 pMedium = *it;
6298 aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
6299 }
6300 if (fLockMedia && aChildrenToReparent)
6301 {
6302 targetCaller.release();
6303 autoCaller.release();
6304 treeLock.release();
6305 hrc = aChildrenToReparent->Lock();
6306 treeLock.acquire();
6307 autoCaller.add();
6308 AssertComRCThrowRC(autoCaller.hrc());
6309 targetCaller.add();
6310 AssertComRCThrowRC(targetCaller.hrc());
6311 if (FAILED(hrc))
6312 throw hrc;
6313 }
6314 }
6315 for (pLast = pLastIntermediate;
6316 !pLast.isNull() && pLast != pTarget && pLast != this;
6317 pLast = pLast->i_getParent())
6318 {
6319 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
6320 if (pLast->m->state == MediumState_Created)
6321 {
6322 hrc = pLast->i_markForDeletion();
6323 if (FAILED(hrc))
6324 throw hrc;
6325 }
6326 else
6327 throw pLast->i_setStateError();
6328 }
6329
6330 /* Tweak the lock list in the backward merge case, as the target
6331 * isn't marked to be locked for writing yet. */
6332 if (!fMergeForward)
6333 {
6334 MediumLockList::Base::iterator lockListBegin =
6335 aMediumLockList->GetBegin();
6336 MediumLockList::Base::iterator lockListEnd =
6337 aMediumLockList->GetEnd();
6338 ++lockListEnd;
6339 for (MediumLockList::Base::iterator it = lockListBegin;
6340 it != lockListEnd;
6341 ++it)
6342 {
6343 MediumLock &mediumLock = *it;
6344 if (mediumLock.GetMedium() == pTarget)
6345 {
6346 HRESULT hrc2 = mediumLock.UpdateLock(true);
6347 AssertComRC(hrc2);
6348 break;
6349 }
6350 }
6351 }
6352
6353 if (fLockMedia)
6354 {
6355 targetCaller.release();
6356 autoCaller.release();
6357 treeLock.release();
6358 hrc = aMediumLockList->Lock();
6359 treeLock.acquire();
6360 autoCaller.add();
6361 AssertComRCThrowRC(autoCaller.hrc());
6362 targetCaller.add();
6363 AssertComRCThrowRC(targetCaller.hrc());
6364 if (FAILED(hrc))
6365 {
6366 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6367 throw setError(hrc,
6368 tr("Failed to lock media when merging to '%s'"),
6369 pTarget->i_getLocationFull().c_str());
6370 }
6371 }
6372 }
6373 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6374
6375 if (FAILED(hrc))
6376 {
6377 if (aMediumLockList)
6378 {
6379 delete aMediumLockList;
6380 aMediumLockList = NULL;
6381 }
6382 if (aChildrenToReparent)
6383 {
6384 delete aChildrenToReparent;
6385 aChildrenToReparent = NULL;
6386 }
6387 }
6388
6389 return hrc;
6390}
6391
6392/**
6393 * Merges this medium to the specified medium which must be either its
6394 * direct ancestor or descendant.
6395 *
6396 * Given this medium is SOURCE and the specified medium is TARGET, we will
6397 * get two variants of the merge operation:
6398 *
6399 * forward merge
6400 * ------------------------->
6401 * [Extra] <- SOURCE <- Intermediate <- TARGET
6402 * Any Del Del LockWr
6403 *
6404 *
6405 * backward merge
6406 * <-------------------------
6407 * TARGET <- Intermediate <- SOURCE <- [Extra]
6408 * LockWr Del Del LockWr
6409 *
6410 * Each diagram shows the involved media on the media chain where
6411 * SOURCE and TARGET belong. Under each medium there is a state value which
6412 * the medium must have at a time of the mergeTo() call.
6413 *
6414 * The media in the square braces may be absent (e.g. when the forward
6415 * operation takes place and SOURCE is the base medium, or when the backward
6416 * merge operation takes place and TARGET is the last child in the chain) but if
6417 * they present they are involved too as shown.
6418 *
6419 * Neither the source medium nor intermediate media may be attached to
6420 * any VM directly or in the snapshot, otherwise this method will assert.
6421 *
6422 * The #i_prepareMergeTo() method must be called prior to this method to place
6423 * all involved to necessary states and perform other consistency checks.
6424 *
6425 * If @a aWait is @c true then this method will perform the operation on the
6426 * calling thread and will not return to the caller until the operation is
6427 * completed. When this method succeeds, all intermediate medium objects in
6428 * the chain will be uninitialized, the state of the target medium (and all
6429 * involved extra media) will be restored. @a aMediumLockList will not be
6430 * deleted, whether the operation is successful or not. The caller has to do
6431 * this if appropriate. Note that this (source) medium is not uninitialized
6432 * because of possible AutoCaller instances held by the caller of this method
6433 * on the current thread. It's therefore the responsibility of the caller to
6434 * call Medium::uninit() after releasing all callers.
6435 *
6436 * If @a aWait is @c false then this method will create a thread to perform the
6437 * operation asynchronously and will return immediately. If the operation
6438 * succeeds, the thread will uninitialize the source medium object and all
6439 * intermediate medium objects in the chain, reset the state of the target
6440 * medium (and all involved extra media) and delete @a aMediumLockList.
6441 * If the operation fails, the thread will only reset the states of all
6442 * involved media and delete @a aMediumLockList.
6443 *
6444 * When this method fails (regardless of the @a aWait mode), it is a caller's
6445 * responsibility to undo state changes and delete @a aMediumLockList using
6446 * #i_cancelMergeTo().
6447 *
6448 * If @a aProgress is not NULL but the object it points to is @c null then a new
6449 * progress object will be created and assigned to @a *aProgress on success,
6450 * otherwise the existing progress object is used. If Progress is NULL, then no
6451 * progress object is created/used at all. Note that @a aProgress cannot be
6452 * NULL when @a aWait is @c false (this method will assert in this case).
6453 *
6454 * @param pTarget Target medium.
6455 * @param fMergeForward Merge direction.
6456 * @param pParentForTarget New parent for target medium after merge.
6457 * @param aChildrenToReparent List of children of the source which will have
6458 * to be reparented to the target after merge.
6459 * @param aMediumLockList Medium locking information.
6460 * @param aProgress Where to find/store a Progress object to track operation
6461 * completion.
6462 * @param aWait @c true if this method should block instead of creating
6463 * an asynchronous thread.
6464 * @param aNotify Notify about media for which metadata is changed
6465 * during execution of the function.
6466 *
6467 * @note Locks the tree lock for writing. Locks the media from the chain
6468 * for writing.
6469 */
6470HRESULT Medium::i_mergeTo(const ComObjPtr<Medium> &pTarget,
6471 bool fMergeForward,
6472 const ComObjPtr<Medium> &pParentForTarget,
6473 MediumLockList *aChildrenToReparent,
6474 MediumLockList *aMediumLockList,
6475 ComObjPtr<Progress> *aProgress,
6476 bool aWait, bool aNotify)
6477{
6478 AssertReturn(pTarget != NULL, E_FAIL);
6479 AssertReturn(pTarget != this, E_FAIL);
6480 AssertReturn(aMediumLockList != NULL, E_FAIL);
6481 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6482
6483 AutoCaller autoCaller(this);
6484 AssertComRCReturnRC(autoCaller.hrc());
6485
6486 AutoCaller targetCaller(pTarget);
6487 AssertComRCReturnRC(targetCaller.hrc());
6488
6489 HRESULT hrc = S_OK;
6490 ComObjPtr<Progress> pProgress;
6491 Medium::Task *pTask = NULL;
6492
6493 try
6494 {
6495 if (aProgress != NULL)
6496 {
6497 /* use the existing progress object... */
6498 pProgress = *aProgress;
6499
6500 /* ...but create a new one if it is null */
6501 if (pProgress.isNull())
6502 {
6503 Utf8Str tgtName;
6504 {
6505 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6506 tgtName = pTarget->i_getName();
6507 }
6508
6509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6510
6511 pProgress.createObject();
6512 hrc = pProgress->init(m->pVirtualBox,
6513 static_cast<IMedium*>(this),
6514 BstrFmt(tr("Merging medium '%s' to '%s'"),
6515 i_getName().c_str(),
6516 tgtName.c_str()).raw(),
6517 TRUE, /* aCancelable */
6518 2, /* Number of opearations */
6519 BstrFmt(tr("Resizing medium '%s' before merge"),
6520 tgtName.c_str()).raw()
6521 );
6522 if (FAILED(hrc))
6523 throw hrc;
6524 }
6525 }
6526
6527 /* setup task object to carry out the operation sync/async */
6528 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
6529 pParentForTarget, aChildrenToReparent,
6530 pProgress, aMediumLockList,
6531 aWait /* fKeepMediumLockList */,
6532 aNotify);
6533 hrc = pTask->hrc();
6534 AssertComRC(hrc);
6535 if (FAILED(hrc))
6536 throw hrc;
6537 }
6538 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6539
6540 if (SUCCEEDED(hrc))
6541 {
6542 if (aWait)
6543 {
6544 hrc = pTask->runNow();
6545 delete pTask;
6546 }
6547 else
6548 hrc = pTask->createThread();
6549 pTask = NULL;
6550 if (SUCCEEDED(hrc) && aProgress != NULL)
6551 *aProgress = pProgress;
6552 }
6553 else if (pTask != NULL)
6554 delete pTask;
6555
6556 return hrc;
6557}
6558
6559/**
6560 * Undoes what #i_prepareMergeTo() did. Must be called if #mergeTo() is not
6561 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
6562 * the medium objects in @a aChildrenToReparent.
6563 *
6564 * @param aChildrenToReparent List of children of the source which will have
6565 * to be reparented to the target after merge.
6566 * @param aMediumLockList Medium locking information.
6567 *
6568 * @note Locks the tree lock for writing. Locks the media from the chain
6569 * for writing.
6570 */
6571void Medium::i_cancelMergeTo(MediumLockList *aChildrenToReparent,
6572 MediumLockList *aMediumLockList)
6573{
6574 AutoCaller autoCaller(this);
6575 AssertComRCReturnVoid(autoCaller.hrc());
6576
6577 AssertReturnVoid(aMediumLockList != NULL);
6578
6579 /* Revert media marked for deletion to previous state. */
6580 MediumLockList::Base::const_iterator mediumListBegin =
6581 aMediumLockList->GetBegin();
6582 MediumLockList::Base::const_iterator mediumListEnd =
6583 aMediumLockList->GetEnd();
6584 for (MediumLockList::Base::const_iterator it = mediumListBegin;
6585 it != mediumListEnd;
6586 ++it)
6587 {
6588 const MediumLock &mediumLock = *it;
6589 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6590 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6591
6592 if (pMedium->m->state == MediumState_Deleting)
6593 {
6594 HRESULT hrc = pMedium->i_unmarkForDeletion();
6595 AssertComRC(hrc);
6596 }
6597 else if ( ( pMedium->m->state == MediumState_LockedWrite
6598 || pMedium->m->state == MediumState_LockedRead)
6599 && pMedium->m->preLockState == MediumState_Deleting)
6600 {
6601 HRESULT hrc = pMedium->i_unmarkLockedForDeletion();
6602 AssertComRC(hrc);
6603 }
6604 }
6605
6606 /* the destructor will do the work */
6607 delete aMediumLockList;
6608
6609 /* unlock the children which had to be reparented, the destructor will do
6610 * the work */
6611 if (aChildrenToReparent)
6612 delete aChildrenToReparent;
6613}
6614
6615/**
6616 * Resizes the media.
6617 *
6618 * If @a aWait is @c true then this method will perform the operation on the
6619 * calling thread and will not return to the caller until the operation is
6620 * completed. When this method succeeds, the state of the target medium (and all
6621 * involved extra media) will be restored. @a aMediumLockList will not be
6622 * deleted, whether the operation is successful or not. The caller has to do
6623 * this if appropriate.
6624 *
6625 * If @a aWait is @c false then this method will create a thread to perform the
6626 * operation asynchronously and will return immediately. The thread will reset
6627 * the state of the target medium (and all involved extra media) and delete
6628 * @a aMediumLockList.
6629 *
6630 * When this method fails (regardless of the @a aWait mode), it is a caller's
6631 * responsibility to undo state changes and delete @a aMediumLockList.
6632 *
6633 * If @a aProgress is not NULL but the object it points to is @c null then a new
6634 * progress object will be created and assigned to @a *aProgress on success,
6635 * otherwise the existing progress object is used. If Progress is NULL, then no
6636 * progress object is created/used at all. Note that @a aProgress cannot be
6637 * NULL when @a aWait is @c false (this method will assert in this case).
6638 *
6639 * @param aLogicalSize New nominal capacity of the medium in bytes.
6640 * @param aMediumLockList Medium locking information.
6641 * @param aProgress Where to find/store a Progress object to track operation
6642 * completion.
6643 * @param aWait @c true if this method should block instead of creating
6644 * an asynchronous thread.
6645 * @param aNotify Notify about media for which metadata is changed
6646 * during execution of the function.
6647 *
6648 * @note Locks the media from the chain for writing.
6649 */
6650
6651HRESULT Medium::i_resize(uint64_t aLogicalSize,
6652 MediumLockList *aMediumLockList,
6653 ComObjPtr<Progress> *aProgress,
6654 bool aWait,
6655 bool aNotify)
6656{
6657 AssertReturn(aMediumLockList != NULL, E_FAIL);
6658 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6659
6660 AutoCaller autoCaller(this);
6661 AssertComRCReturnRC(autoCaller.hrc());
6662
6663 HRESULT hrc = S_OK;
6664 ComObjPtr<Progress> pProgress;
6665 Medium::Task *pTask = NULL;
6666
6667 try
6668 {
6669 if (aProgress != NULL)
6670 {
6671 /* use the existing progress object... */
6672 pProgress = *aProgress;
6673
6674 /* ...but create a new one if it is null */
6675 if (pProgress.isNull())
6676 {
6677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6678
6679 pProgress.createObject();
6680 hrc = pProgress->init(m->pVirtualBox,
6681 static_cast <IMedium *>(this),
6682 BstrFmt(tr("Resizing medium '%s'"), m->strLocationFull.c_str()).raw(),
6683 TRUE /* aCancelable */);
6684 if (FAILED(hrc))
6685 throw hrc;
6686 }
6687 }
6688
6689 /* setup task object to carry out the operation asynchronously */
6690 pTask = new Medium::ResizeTask(this,
6691 aLogicalSize,
6692 pProgress,
6693 aMediumLockList,
6694 aWait /* fKeepMediumLockList */,
6695 aNotify);
6696 hrc = pTask->hrc();
6697 AssertComRC(hrc);
6698 if (FAILED(hrc))
6699 throw hrc;
6700 }
6701 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6702
6703 if (SUCCEEDED(hrc))
6704 {
6705 if (aWait)
6706 {
6707 hrc = pTask->runNow();
6708 delete pTask;
6709 }
6710 else
6711 hrc = pTask->createThread();
6712 pTask = NULL;
6713 if (SUCCEEDED(hrc) && aProgress != NULL)
6714 *aProgress = pProgress;
6715 }
6716 else if (pTask != NULL)
6717 delete pTask;
6718
6719 return hrc;
6720}
6721
6722/**
6723 * Fix the parent UUID of all children to point to this medium as their
6724 * parent.
6725 */
6726HRESULT Medium::i_fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
6727{
6728 /** @todo r=klaus The code below needs to be double checked with regard
6729 * to lock order violations, it probably causes lock order issues related
6730 * to the AutoCaller usage. Likewise the code using this method seems
6731 * problematic. */
6732 Assert(!isWriteLockOnCurrentThread());
6733 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6734 MediumLockList mediumLockList;
6735 HRESULT hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
6736 NULL /* pToLockWrite */,
6737 false /* fMediumLockWriteAll */,
6738 this,
6739 mediumLockList);
6740 AssertComRCReturnRC(hrc);
6741
6742 try
6743 {
6744 PVDISK hdd;
6745 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6746 ComAssertRCThrow(vrc, E_FAIL);
6747
6748 try
6749 {
6750 MediumLockList::Base::iterator lockListBegin =
6751 mediumLockList.GetBegin();
6752 MediumLockList::Base::iterator lockListEnd =
6753 mediumLockList.GetEnd();
6754 for (MediumLockList::Base::iterator it = lockListBegin;
6755 it != lockListEnd;
6756 ++it)
6757 {
6758 MediumLock &mediumLock = *it;
6759 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6760 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6761
6762 // open the medium
6763 vrc = VDOpen(hdd,
6764 pMedium->m->strFormat.c_str(),
6765 pMedium->m->strLocationFull.c_str(),
6766 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
6767 pMedium->m->vdImageIfaces);
6768 if (RT_FAILURE(vrc))
6769 throw vrc;
6770 }
6771
6772 MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
6773 MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
6774 for (MediumLockList::Base::iterator it = childrenBegin;
6775 it != childrenEnd;
6776 ++it)
6777 {
6778 Medium *pMedium = it->GetMedium();
6779 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
6780 vrc = VDOpen(hdd,
6781 pMedium->m->strFormat.c_str(),
6782 pMedium->m->strLocationFull.c_str(),
6783 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
6784 pMedium->m->vdImageIfaces);
6785 if (RT_FAILURE(vrc))
6786 throw vrc;
6787
6788 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
6789 if (RT_FAILURE(vrc))
6790 throw vrc;
6791
6792 vrc = VDClose(hdd, false /* fDelete */);
6793 if (RT_FAILURE(vrc))
6794 throw vrc;
6795 }
6796 }
6797 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6798 catch (int vrcXcpt)
6799 {
6800 hrc = setErrorBoth(E_FAIL, vrcXcpt,
6801 tr("Could not update medium UUID references to parent '%s' (%s)"),
6802 m->strLocationFull.c_str(),
6803 i_vdError(vrcXcpt).c_str());
6804 }
6805
6806 VDDestroy(hdd);
6807 }
6808 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
6809
6810 return hrc;
6811}
6812
6813/**
6814 *
6815 * @note Similar code exists in i_taskExportHandler.
6816 */
6817HRESULT Medium::i_addRawToFss(const char *aFilename, SecretKeyStore *pKeyStore, RTVFSFSSTREAM hVfsFssDst,
6818 const ComObjPtr<Progress> &aProgress, bool fSparse)
6819{
6820 AutoCaller autoCaller(this);
6821 HRESULT hrc = autoCaller.hrc();
6822 if (SUCCEEDED(hrc))
6823 {
6824 /*
6825 * Get a readonly hdd for this medium.
6826 */
6827 MediumCryptoFilterSettings CryptoSettingsRead;
6828 MediumLockList SourceMediumLockList;
6829 PVDISK pHdd;
6830 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pHdd, &SourceMediumLockList, &CryptoSettingsRead);
6831 if (SUCCEEDED(hrc))
6832 {
6833 /*
6834 * Create a VFS file interface to the HDD and attach a progress wrapper
6835 * that monitors the progress reading of the raw image. The image will
6836 * be read twice if hVfsFssDst does sparse processing.
6837 */
6838 RTVFSFILE hVfsFileDisk = NIL_RTVFSFILE;
6839 int vrc = VDCreateVfsFileFromDisk(pHdd, 0 /*fFlags*/, &hVfsFileDisk);
6840 if (RT_SUCCESS(vrc))
6841 {
6842 RTVFSFILE hVfsFileProgress = NIL_RTVFSFILE;
6843 vrc = RTVfsCreateProgressForFile(hVfsFileDisk, aProgress->i_iprtProgressCallback, &*aProgress,
6844 RTVFSPROGRESS_F_CANCELABLE | RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ,
6845 VDGetSize(pHdd, VD_LAST_IMAGE) * (fSparse ? 2 : 1) /*cbExpectedRead*/,
6846 0 /*cbExpectedWritten*/, &hVfsFileProgress);
6847 RTVfsFileRelease(hVfsFileDisk);
6848 if (RT_SUCCESS(vrc))
6849 {
6850 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFileProgress);
6851 RTVfsFileRelease(hVfsFileProgress);
6852
6853 vrc = RTVfsFsStrmAdd(hVfsFssDst, aFilename, hVfsObj, 0 /*fFlags*/);
6854 RTVfsObjRelease(hVfsObj);
6855 if (RT_FAILURE(vrc))
6856 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to add '%s' to output (%Rrc)"), aFilename, vrc);
6857 }
6858 else
6859 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
6860 tr("RTVfsCreateProgressForFile failed when processing '%s' (%Rrc)"), aFilename, vrc);
6861 }
6862 else
6863 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("VDCreateVfsFileFromDisk failed for '%s' (%Rrc)"), aFilename, vrc);
6864 VDDestroy(pHdd);
6865 }
6866 }
6867 return hrc;
6868}
6869
6870/**
6871 * Used by IAppliance to export disk images.
6872 *
6873 * @param aFilename Filename to create (UTF8).
6874 * @param aFormat Medium format for creating @a aFilename.
6875 * @param aVariant Which exact image format variant to use for the
6876 * destination image.
6877 * @param pKeyStore The optional key store for decrypting the data for
6878 * encrypted media during the export.
6879 * @param hVfsIosDst The destination I/O stream object.
6880 * @param aProgress Progress object to use.
6881 * @return
6882 *
6883 * @note The source format is defined by the Medium instance.
6884 */
6885HRESULT Medium::i_exportFile(const char *aFilename,
6886 const ComObjPtr<MediumFormat> &aFormat,
6887 MediumVariant_T aVariant,
6888 SecretKeyStore *pKeyStore,
6889 RTVFSIOSTREAM hVfsIosDst,
6890 const ComObjPtr<Progress> &aProgress)
6891{
6892 AssertPtrReturn(aFilename, E_INVALIDARG);
6893 AssertReturn(aFormat.isNotNull(), E_INVALIDARG);
6894 AssertReturn(aProgress.isNotNull(), E_INVALIDARG);
6895
6896 AutoCaller autoCaller(this);
6897 HRESULT hrc = autoCaller.hrc();
6898 if (SUCCEEDED(hrc))
6899 {
6900 /*
6901 * Setup VD interfaces.
6902 */
6903 PVDINTERFACE pVDImageIfaces = m->vdImageIfaces;
6904 PVDINTERFACEIO pVfsIoIf;
6905 int vrc = VDIfCreateFromVfsStream(hVfsIosDst, RTFILE_O_WRITE, &pVfsIoIf);
6906 if (RT_SUCCESS(vrc))
6907 {
6908 vrc = VDInterfaceAdd(&pVfsIoIf->Core, "Medium::ExportTaskVfsIos", VDINTERFACETYPE_IO,
6909 pVfsIoIf, sizeof(VDINTERFACEIO), &pVDImageIfaces);
6910 if (RT_SUCCESS(vrc))
6911 {
6912 /*
6913 * Get a readonly hdd for this medium (source).
6914 */
6915 MediumCryptoFilterSettings CryptoSettingsRead;
6916 MediumLockList SourceMediumLockList;
6917 PVDISK pSrcHdd;
6918 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pSrcHdd, &SourceMediumLockList, &CryptoSettingsRead);
6919 if (SUCCEEDED(hrc))
6920 {
6921 /*
6922 * Create the target medium.
6923 */
6924 Utf8Str strDstFormat(aFormat->i_getId());
6925
6926 /* ensure the target directory exists */
6927 uint64_t fDstCapabilities = aFormat->i_getCapabilities();
6928 if (fDstCapabilities & MediumFormatCapabilities_File)
6929 {
6930 Utf8Str strDstLocation(aFilename);
6931 hrc = VirtualBox::i_ensureFilePathExists(strDstLocation.c_str(),
6932 !(aVariant & MediumVariant_NoCreateDir) /* fCreate */);
6933 }
6934 if (SUCCEEDED(hrc))
6935 {
6936 PVDISK pDstHdd;
6937 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDstHdd);
6938 if (RT_SUCCESS(vrc))
6939 {
6940 /*
6941 * Create an interface for getting progress callbacks.
6942 */
6943 VDINTERFACEPROGRESS ProgressIf = VDINTERFACEPROGRESS_INITALIZER(aProgress->i_vdProgressCallback);
6944 PVDINTERFACE pProgress = NULL;
6945 vrc = VDInterfaceAdd(&ProgressIf.Core, "export-progress", VDINTERFACETYPE_PROGRESS,
6946 &*aProgress, sizeof(ProgressIf), &pProgress);
6947 AssertRC(vrc);
6948
6949 /*
6950 * Do the exporting.
6951 */
6952 vrc = VDCopy(pSrcHdd,
6953 VD_LAST_IMAGE,
6954 pDstHdd,
6955 strDstFormat.c_str(),
6956 aFilename,
6957 false /* fMoveByRename */,
6958 0 /* cbSize */,
6959 aVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk),
6960 NULL /* pDstUuid */,
6961 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
6962 pProgress,
6963 pVDImageIfaces,
6964 NULL);
6965 if (RT_SUCCESS(vrc))
6966 hrc = S_OK;
6967 else
6968 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Could not create the exported medium '%s'%s"),
6969 aFilename, i_vdError(vrc).c_str());
6970 VDDestroy(pDstHdd);
6971 }
6972 else
6973 hrc = setErrorVrc(vrc);
6974 }
6975 }
6976 VDDestroy(pSrcHdd);
6977 }
6978 else
6979 hrc = setErrorVrc(vrc, "VDInterfaceAdd -> %Rrc", vrc);
6980 VDIfDestroyFromVfsStream(pVfsIoIf);
6981 }
6982 else
6983 hrc = setErrorVrc(vrc, "VDIfCreateFromVfsStream -> %Rrc", vrc);
6984 }
6985 return hrc;
6986}
6987
6988/**
6989 * Used by IAppliance to import disk images.
6990 *
6991 * @param aFilename Filename to read (UTF8).
6992 * @param aFormat Medium format for reading @a aFilename.
6993 * @param aVariant Which exact image format variant to use
6994 * for the destination image.
6995 * @param aVfsIosSrc Handle to the source I/O stream.
6996 * @param aParent Parent medium. May be NULL.
6997 * @param aProgress Progress object to use.
6998 * @param aNotify Notify about media for which metadata is changed
6999 * during execution of the function.
7000 * @return
7001 * @note The destination format is defined by the Medium instance.
7002 *
7003 * @todo The only consumer of this method (Appliance::i_importOneDiskImage) is
7004 * already on a worker thread, so perhaps consider bypassing the thread
7005 * here and run in the task synchronously? VBoxSVC has enough threads as
7006 * it is...
7007 */
7008HRESULT Medium::i_importFile(const char *aFilename,
7009 const ComObjPtr<MediumFormat> &aFormat,
7010 MediumVariant_T aVariant,
7011 RTVFSIOSTREAM aVfsIosSrc,
7012 const ComObjPtr<Medium> &aParent,
7013 const ComObjPtr<Progress> &aProgress,
7014 bool aNotify)
7015{
7016 /** @todo r=klaus The code below needs to be double checked with regard
7017 * to lock order violations, it probably causes lock order issues related
7018 * to the AutoCaller usage. */
7019 AssertPtrReturn(aFilename, E_INVALIDARG);
7020 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
7021 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
7022
7023 AutoCaller autoCaller(this);
7024 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7025
7026 HRESULT hrc = S_OK;
7027 Medium::Task *pTask = NULL;
7028
7029 try
7030 {
7031 // locking: we need the tree lock first because we access parent pointers
7032 // and we need to write-lock the media involved
7033 uint32_t cHandles = 2;
7034 LockHandle* pHandles[3] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
7035 this->lockHandle() };
7036 /* Only add parent to the lock if it is not null */
7037 if (!aParent.isNull())
7038 pHandles[cHandles++] = aParent->lockHandle();
7039 AutoWriteLock alock(cHandles,
7040 pHandles
7041 COMMA_LOCKVAL_SRC_POS);
7042
7043 if ( m->state != MediumState_NotCreated
7044 && m->state != MediumState_Created)
7045 throw i_setStateError();
7046
7047 /* Build the target lock list. */
7048 MediumLockList *pTargetMediumLockList(new MediumLockList());
7049 alock.release();
7050 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
7051 this /* pToLockWrite */,
7052 false /* fMediumLockWriteAll */,
7053 aParent,
7054 *pTargetMediumLockList);
7055 alock.acquire();
7056 if (FAILED(hrc))
7057 {
7058 delete pTargetMediumLockList;
7059 throw hrc;
7060 }
7061
7062 alock.release();
7063 hrc = pTargetMediumLockList->Lock();
7064 alock.acquire();
7065 if (FAILED(hrc))
7066 {
7067 delete pTargetMediumLockList;
7068 throw setError(hrc,
7069 tr("Failed to lock target media '%s'"),
7070 i_getLocationFull().c_str());
7071 }
7072
7073 /* setup task object to carry out the operation asynchronously */
7074 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat, aVariant,
7075 aVfsIosSrc, aParent, pTargetMediumLockList, false, aNotify);
7076 hrc = pTask->hrc();
7077 AssertComRC(hrc);
7078 if (FAILED(hrc))
7079 throw hrc;
7080
7081 if (m->state == MediumState_NotCreated)
7082 m->state = MediumState_Creating;
7083 }
7084 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
7085
7086 if (SUCCEEDED(hrc))
7087 {
7088 hrc = pTask->createThread();
7089 pTask = NULL;
7090 }
7091 else if (pTask != NULL)
7092 delete pTask;
7093
7094 return hrc;
7095}
7096
7097/**
7098 * Internal version of the public CloneTo API which allows to enable certain
7099 * optimizations to improve speed during VM cloning.
7100 *
7101 * @param aTarget Target medium
7102 * @param aVariant Which exact image format variant to use
7103 * for the destination image.
7104 * @param aParent Parent medium. May be NULL.
7105 * @param aProgress Progress object to use.
7106 * @param idxSrcImageSame The last image in the source chain which has the
7107 * same content as the given image in the destination
7108 * chain. Use UINT32_MAX to disable this optimization.
7109 * @param idxDstImageSame The last image in the destination chain which has the
7110 * same content as the given image in the source chain.
7111 * Use UINT32_MAX to disable this optimization.
7112 * @param aNotify Notify about media for which metadata is changed
7113 * during execution of the function.
7114 * @return
7115 */
7116HRESULT Medium::i_cloneToEx(const ComObjPtr<Medium> &aTarget, MediumVariant_T aVariant,
7117 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
7118 uint32_t idxSrcImageSame, uint32_t idxDstImageSame, bool aNotify)
7119{
7120 /** @todo r=klaus The code below needs to be double checked with regard
7121 * to lock order violations, it probably causes lock order issues related
7122 * to the AutoCaller usage. */
7123 CheckComArgNotNull(aTarget);
7124 CheckComArgOutPointerValid(aProgress);
7125 ComAssertRet(aTarget != this, E_INVALIDARG);
7126
7127 AutoCaller autoCaller(this);
7128 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7129
7130 HRESULT hrc = S_OK;
7131 ComObjPtr<Progress> pProgress;
7132 Medium::Task *pTask = NULL;
7133
7134 try
7135 {
7136 // locking: we need the tree lock first because we access parent pointers
7137 // and we need to write-lock the media involved
7138 uint32_t cHandles = 3;
7139 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
7140 this->lockHandle(),
7141 aTarget->lockHandle() };
7142 /* Only add parent to the lock if it is not null */
7143 if (!aParent.isNull())
7144 pHandles[cHandles++] = aParent->lockHandle();
7145 AutoWriteLock alock(cHandles,
7146 pHandles
7147 COMMA_LOCKVAL_SRC_POS);
7148
7149 if ( aTarget->m->state != MediumState_NotCreated
7150 && aTarget->m->state != MediumState_Created)
7151 throw aTarget->i_setStateError();
7152
7153 /* Build the source lock list. */
7154 MediumLockList *pSourceMediumLockList(new MediumLockList());
7155 alock.release();
7156 hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
7157 NULL /* pToLockWrite */,
7158 false /* fMediumLockWriteAll */,
7159 NULL,
7160 *pSourceMediumLockList);
7161 alock.acquire();
7162 if (FAILED(hrc))
7163 {
7164 delete pSourceMediumLockList;
7165 throw hrc;
7166 }
7167
7168 /* Build the target lock list (including the to-be parent chain). */
7169 MediumLockList *pTargetMediumLockList(new MediumLockList());
7170 alock.release();
7171 hrc = aTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
7172 aTarget /* pToLockWrite */,
7173 false /* fMediumLockWriteAll */,
7174 aParent,
7175 *pTargetMediumLockList);
7176 alock.acquire();
7177 if (FAILED(hrc))
7178 {
7179 delete pSourceMediumLockList;
7180 delete pTargetMediumLockList;
7181 throw hrc;
7182 }
7183
7184 alock.release();
7185 hrc = pSourceMediumLockList->Lock();
7186 alock.acquire();
7187 if (FAILED(hrc))
7188 {
7189 delete pSourceMediumLockList;
7190 delete pTargetMediumLockList;
7191 throw setError(hrc,
7192 tr("Failed to lock source media '%s'"),
7193 i_getLocationFull().c_str());
7194 }
7195 alock.release();
7196 hrc = pTargetMediumLockList->Lock();
7197 alock.acquire();
7198 if (FAILED(hrc))
7199 {
7200 delete pSourceMediumLockList;
7201 delete pTargetMediumLockList;
7202 throw setError(hrc,
7203 tr("Failed to lock target media '%s'"),
7204 aTarget->i_getLocationFull().c_str());
7205 }
7206
7207 pProgress.createObject();
7208 hrc = pProgress->init(m->pVirtualBox,
7209 static_cast <IMedium *>(this),
7210 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
7211 TRUE /* aCancelable */);
7212 if (FAILED(hrc))
7213 {
7214 delete pSourceMediumLockList;
7215 delete pTargetMediumLockList;
7216 throw hrc;
7217 }
7218
7219 /* setup task object to carry out the operation asynchronously */
7220 pTask = new Medium::CloneTask(this, pProgress, aTarget, aVariant,
7221 aParent, idxSrcImageSame,
7222 idxDstImageSame, pSourceMediumLockList,
7223 pTargetMediumLockList, false, false, aNotify);
7224 hrc = pTask->hrc();
7225 AssertComRC(hrc);
7226 if (FAILED(hrc))
7227 throw hrc;
7228
7229 if (aTarget->m->state == MediumState_NotCreated)
7230 aTarget->m->state = MediumState_Creating;
7231 }
7232 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
7233
7234 if (SUCCEEDED(hrc))
7235 {
7236 hrc = pTask->createThread();
7237 pTask = NULL;
7238 if (SUCCEEDED(hrc))
7239 pProgress.queryInterfaceTo(aProgress);
7240 }
7241 else if (pTask != NULL)
7242 delete pTask;
7243
7244 return hrc;
7245}
7246
7247/**
7248 * Returns the key identifier for this medium if encryption is configured.
7249 *
7250 * @returns Key identifier or empty string if no encryption is configured.
7251 */
7252const Utf8Str& Medium::i_getKeyId()
7253{
7254 ComObjPtr<Medium> pBase = i_getBase();
7255
7256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7257
7258 settings::StringsMap::const_iterator it = pBase->m->mapProperties.find("CRYPT/KeyId");
7259 if (it == pBase->m->mapProperties.end())
7260 return Utf8Str::Empty;
7261
7262 return it->second;
7263}
7264
7265
7266/**
7267 * Returns all filter related properties.
7268 *
7269 * @returns COM status code.
7270 * @param aReturnNames Where to store the properties names on success.
7271 * @param aReturnValues Where to store the properties values on success.
7272 */
7273HRESULT Medium::i_getFilterProperties(std::vector<com::Utf8Str> &aReturnNames,
7274 std::vector<com::Utf8Str> &aReturnValues)
7275{
7276 std::vector<com::Utf8Str> aPropNames;
7277 std::vector<com::Utf8Str> aPropValues;
7278 HRESULT hrc = getProperties(Utf8Str(""), aPropNames, aPropValues);
7279
7280 if (SUCCEEDED(hrc))
7281 {
7282 unsigned cReturnSize = 0;
7283 aReturnNames.resize(0);
7284 aReturnValues.resize(0);
7285 for (unsigned idx = 0; idx < aPropNames.size(); idx++)
7286 {
7287 if (i_isPropertyForFilter(aPropNames[idx]))
7288 {
7289 aReturnNames.resize(cReturnSize + 1);
7290 aReturnValues.resize(cReturnSize + 1);
7291 aReturnNames[cReturnSize] = aPropNames[idx];
7292 aReturnValues[cReturnSize] = aPropValues[idx];
7293 cReturnSize++;
7294 }
7295 }
7296 }
7297
7298 return hrc;
7299}
7300
7301/**
7302 * Preparation to move this medium to a new location
7303 *
7304 * @param aLocation Location of the storage unit. If the location is a FS-path,
7305 * then it can be relative to the VirtualBox home directory.
7306 *
7307 * @note Must be called from under this object's write lock.
7308 */
7309HRESULT Medium::i_preparationForMoving(const Utf8Str &aLocation)
7310{
7311 HRESULT hrc = E_FAIL;
7312
7313 if (i_getLocationFull() != aLocation)
7314 {
7315 m->strNewLocationFull = aLocation;
7316 m->fMoveThisMedium = true;
7317 hrc = S_OK;
7318 }
7319
7320 return hrc;
7321}
7322
7323/**
7324 * Checking whether current operation "moving" or not
7325 */
7326bool Medium::i_isMoveOperation(const ComObjPtr<Medium> &aTarget) const
7327{
7328 RT_NOREF(aTarget);
7329 return m->fMoveThisMedium;
7330}
7331
7332bool Medium::i_resetMoveOperationData()
7333{
7334 m->strNewLocationFull.setNull();
7335 m->fMoveThisMedium = false;
7336 return true;
7337}
7338
7339Utf8Str Medium::i_getNewLocationForMoving() const
7340{
7341 if (m->fMoveThisMedium == true)
7342 return m->strNewLocationFull;
7343 else
7344 return Utf8Str();
7345}
7346////////////////////////////////////////////////////////////////////////////////
7347//
7348// Private methods
7349//
7350////////////////////////////////////////////////////////////////////////////////
7351
7352/**
7353 * Queries information from the medium.
7354 *
7355 * As a result of this call, the accessibility state and data members such as
7356 * size and description will be updated with the current information.
7357 *
7358 * @note This method may block during a system I/O call that checks storage
7359 * accessibility.
7360 *
7361 * @note Caller MUST NOT hold the media tree or medium lock.
7362 *
7363 * @note Locks m->pParent for reading. Locks this object for writing.
7364 *
7365 * @param fSetImageId Whether to reset the UUID contained in the image file
7366 * to the UUID in the medium instance data (see SetIDs())
7367 * @param fSetParentId Whether to reset the parent UUID contained in the image
7368 * file to the parent UUID in the medium instance data (see
7369 * SetIDs())
7370 * @param autoCaller
7371 * @return
7372 */
7373HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
7374{
7375 Assert(!isWriteLockOnCurrentThread());
7376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7377
7378 if ( ( m->state != MediumState_Created
7379 && m->state != MediumState_Inaccessible
7380 && m->state != MediumState_LockedRead)
7381 || m->fClosing)
7382 return E_FAIL;
7383
7384 HRESULT hrc = S_OK;
7385
7386 int vrc = VINF_SUCCESS;
7387
7388 /* check if a blocking i_queryInfo() call is in progress on some other thread,
7389 * and wait for it to finish if so instead of querying data ourselves */
7390 if (m->queryInfoRunning)
7391 {
7392 Assert( m->state == MediumState_LockedRead
7393 || m->state == MediumState_LockedWrite);
7394
7395 while (m->queryInfoRunning)
7396 {
7397 alock.release();
7398 /* must not hold the object lock now */
7399 Assert(!isWriteLockOnCurrentThread());
7400 {
7401 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
7402 }
7403 alock.acquire();
7404 }
7405
7406 return S_OK;
7407 }
7408
7409 bool success = false;
7410 Utf8Str lastAccessError;
7411
7412 /* are we dealing with a new medium constructed using the existing
7413 * location? */
7414 bool isImport = m->id.isZero();
7415 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
7416
7417 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
7418 * media because that would prevent necessary modifications
7419 * when opening media of some third-party formats for the first
7420 * time in VirtualBox (such as VMDK for which VDOpen() needs to
7421 * generate an UUID if it is missing) */
7422 if ( m->hddOpenMode == OpenReadOnly
7423 || m->type == MediumType_Readonly
7424 || (!isImport && !fSetImageId && !fSetParentId)
7425 )
7426 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7427
7428 /* Open shareable medium with the appropriate flags */
7429 if (m->type == MediumType_Shareable)
7430 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7431
7432 /* Lock the medium, which makes the behavior much more consistent, must be
7433 * done before dropping the object lock and setting queryInfoRunning. */
7434 ComPtr<IToken> pToken;
7435 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
7436 hrc = LockRead(pToken.asOutParam());
7437 else
7438 hrc = LockWrite(pToken.asOutParam());
7439 if (FAILED(hrc)) return hrc;
7440
7441 /* Copies of the input state fields which are not read-only,
7442 * as we're dropping the lock. CAUTION: be extremely careful what
7443 * you do with the contents of this medium object, as you will
7444 * create races if there are concurrent changes. */
7445 Utf8Str format(m->strFormat);
7446 Utf8Str location(m->strLocationFull);
7447 ComObjPtr<MediumFormat> formatObj = m->formatObj;
7448
7449 /* "Output" values which can't be set because the lock isn't held
7450 * at the time the values are determined. */
7451 Guid mediumId = m->id;
7452 uint64_t mediumSize = 0;
7453 uint64_t mediumLogicalSize = 0;
7454
7455 /* Flag whether a base image has a non-zero parent UUID and thus
7456 * need repairing after it was closed again. */
7457 bool fRepairImageZeroParentUuid = false;
7458
7459 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
7460
7461 /* must be set before leaving the object lock the first time */
7462 m->queryInfoRunning = true;
7463
7464 /* must leave object lock now, because a lock from a higher lock class
7465 * is needed and also a lengthy operation is coming */
7466 alock.release();
7467 autoCaller.release();
7468
7469 /* Note that taking the queryInfoSem after leaving the object lock above
7470 * can lead to short spinning of the loops waiting for i_queryInfo() to
7471 * complete. This is unavoidable since the other order causes a lock order
7472 * violation: here it would be requesting the object lock (at the beginning
7473 * of the method), then queryInfoSem, and below the other way round. */
7474 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
7475
7476 /* take the opportunity to have a media tree lock, released initially */
7477 Assert(!isWriteLockOnCurrentThread());
7478 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7479 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7480 treeLock.release();
7481
7482 /* re-take the caller, but not the object lock, to keep uninit away */
7483 autoCaller.add();
7484 if (FAILED(autoCaller.hrc()))
7485 {
7486 m->queryInfoRunning = false;
7487 return autoCaller.hrc();
7488 }
7489
7490 try
7491 {
7492 /* skip accessibility checks for host drives */
7493 if (m->hostDrive)
7494 {
7495 success = true;
7496 throw S_OK;
7497 }
7498
7499 PVDISK hdd;
7500 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7501 ComAssertRCThrow(vrc, E_FAIL);
7502
7503 try
7504 {
7505 /** @todo This kind of opening of media is assuming that diff
7506 * media can be opened as base media. Should be documented that
7507 * it must work for all medium format backends. */
7508 vrc = VDOpen(hdd,
7509 format.c_str(),
7510 location.c_str(),
7511 uOpenFlags | m->uOpenFlagsDef,
7512 m->vdImageIfaces);
7513 if (RT_FAILURE(vrc))
7514 {
7515 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
7516 location.c_str(), i_vdError(vrc).c_str());
7517 throw S_OK;
7518 }
7519
7520 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
7521 {
7522 /* Modify the UUIDs if necessary. The associated fields are
7523 * not modified by other code, so no need to copy. */
7524 if (fSetImageId)
7525 {
7526 alock.acquire();
7527 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
7528 alock.release();
7529 if (RT_FAILURE(vrc))
7530 {
7531 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
7532 location.c_str(), i_vdError(vrc).c_str());
7533 throw S_OK;
7534 }
7535 mediumId = m->uuidImage;
7536 }
7537 if (fSetParentId)
7538 {
7539 alock.acquire();
7540 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
7541 alock.release();
7542 if (RT_FAILURE(vrc))
7543 {
7544 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
7545 location.c_str(), i_vdError(vrc).c_str());
7546 throw S_OK;
7547 }
7548 }
7549 /* zap the information, these are no long-term members */
7550 alock.acquire();
7551 unconst(m->uuidImage).clear();
7552 unconst(m->uuidParentImage).clear();
7553 alock.release();
7554
7555 /* check the UUID */
7556 RTUUID uuid;
7557 vrc = VDGetUuid(hdd, 0, &uuid);
7558 ComAssertRCThrow(vrc, E_FAIL);
7559
7560 if (isImport)
7561 {
7562 mediumId = uuid;
7563
7564 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
7565 // only when importing a VDMK that has no UUID, create one in memory
7566 mediumId.create();
7567 }
7568 else
7569 {
7570 Assert(!mediumId.isZero());
7571
7572 if (mediumId != uuid)
7573 {
7574 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7575 lastAccessError = Utf8StrFmt(
7576 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
7577 &uuid,
7578 location.c_str(),
7579 mediumId.raw(),
7580 pVirtualBox->i_settingsFilePath().c_str());
7581 throw S_OK;
7582 }
7583 }
7584 }
7585 else
7586 {
7587 /* the backend does not support storing UUIDs within the
7588 * underlying storage so use what we store in XML */
7589
7590 if (fSetImageId)
7591 {
7592 /* set the UUID if an API client wants to change it */
7593 alock.acquire();
7594 mediumId = m->uuidImage;
7595 alock.release();
7596 }
7597 else if (isImport)
7598 {
7599 /* generate an UUID for an imported UUID-less medium */
7600 mediumId.create();
7601 }
7602 }
7603
7604 /* set the image uuid before the below parent uuid handling code
7605 * might place it somewhere in the media tree, so that the medium
7606 * UUID is valid at this point */
7607 alock.acquire();
7608 if (isImport || fSetImageId)
7609 unconst(m->id) = mediumId;
7610 alock.release();
7611
7612 /* get the medium variant */
7613 unsigned uImageFlags;
7614 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7615 ComAssertRCThrow(vrc, E_FAIL);
7616 alock.acquire();
7617 m->variant = (MediumVariant_T)uImageFlags;
7618 alock.release();
7619
7620 /* check/get the parent uuid and update corresponding state */
7621 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
7622 {
7623 RTUUID parentId;
7624 vrc = VDGetParentUuid(hdd, 0, &parentId);
7625 ComAssertRCThrow(vrc, E_FAIL);
7626
7627 /* streamOptimized VMDK images are only accepted as base
7628 * images, as this allows automatic repair of OVF appliances.
7629 * Since such images don't support random writes they will not
7630 * be created for diff images. Only an overly smart user might
7631 * manually create this case. Too bad for him. */
7632 if ( (isImport || fSetParentId)
7633 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
7634 {
7635 /* the parent must be known to us. Note that we freely
7636 * call locking methods of mVirtualBox and parent, as all
7637 * relevant locks must be already held. There may be no
7638 * concurrent access to the just opened medium on other
7639 * threads yet (and init() will fail if this method reports
7640 * MediumState_Inaccessible) */
7641
7642 ComObjPtr<Medium> pParent;
7643 if (RTUuidIsNull(&parentId))
7644 hrc = VBOX_E_OBJECT_NOT_FOUND;
7645 else
7646 hrc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
7647 if (FAILED(hrc))
7648 {
7649 if (fSetImageId && !fSetParentId)
7650 {
7651 /* If the image UUID gets changed for an existing
7652 * image then the parent UUID can be stale. In such
7653 * cases clear the parent information. The parent
7654 * information may/will be re-set later if the
7655 * API client wants to adjust a complete medium
7656 * hierarchy one by one. */
7657 hrc = S_OK;
7658 alock.acquire();
7659 RTUuidClear(&parentId);
7660 vrc = VDSetParentUuid(hdd, 0, &parentId);
7661 alock.release();
7662 ComAssertRCThrow(vrc, E_FAIL);
7663 }
7664 else
7665 {
7666 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
7667 &parentId, location.c_str(),
7668 pVirtualBox->i_settingsFilePath().c_str());
7669 throw S_OK;
7670 }
7671 }
7672
7673 /* must drop the caller before taking the tree lock */
7674 autoCaller.release();
7675 /* we set m->pParent & children() */
7676 treeLock.acquire();
7677 autoCaller.add();
7678 if (FAILED(autoCaller.hrc()))
7679 throw autoCaller.hrc();
7680
7681 if (m->pParent)
7682 i_deparent();
7683
7684 if (!pParent.isNull())
7685 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7686 {
7687 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
7688 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7689 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"),
7690 pParent->m->strLocationFull.c_str());
7691 }
7692 i_setParent(pParent);
7693
7694 treeLock.release();
7695 }
7696 else
7697 {
7698 /* must drop the caller before taking the tree lock */
7699 autoCaller.release();
7700 /* we access m->pParent */
7701 treeLock.acquire();
7702 autoCaller.add();
7703 if (FAILED(autoCaller.hrc()))
7704 throw autoCaller.hrc();
7705
7706 /* check that parent UUIDs match. Note that there's no need
7707 * for the parent's AutoCaller (our lifetime is bound to
7708 * it) */
7709
7710 if (m->pParent.isNull())
7711 {
7712 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
7713 * and 3.1.0-3.1.8 there are base images out there
7714 * which have a non-zero parent UUID. No point in
7715 * complaining about them, instead automatically
7716 * repair the problem. Later we can bring back the
7717 * error message, but we should wait until really
7718 * most users have repaired their images, either with
7719 * VBoxFixHdd or this way. */
7720#if 1
7721 fRepairImageZeroParentUuid = true;
7722#else /* 0 */
7723 lastAccessError = Utf8StrFmt(
7724 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
7725 location.c_str(),
7726 pVirtualBox->settingsFilePath().c_str());
7727 treeLock.release();
7728 throw S_OK;
7729#endif /* 0 */
7730 }
7731
7732 {
7733 autoCaller.release();
7734 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
7735 autoCaller.add();
7736 if (FAILED(autoCaller.hrc()))
7737 throw autoCaller.hrc();
7738
7739 if ( !fRepairImageZeroParentUuid
7740 && m->pParent->i_getState() != MediumState_Inaccessible
7741 && m->pParent->i_getId() != parentId)
7742 {
7743 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7744 lastAccessError = Utf8StrFmt(
7745 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
7746 &parentId, location.c_str(),
7747 m->pParent->i_getId().raw(),
7748 pVirtualBox->i_settingsFilePath().c_str());
7749 parentLock.release();
7750 treeLock.release();
7751 throw S_OK;
7752 }
7753 }
7754
7755 /// @todo NEWMEDIA what to do if the parent is not
7756 /// accessible while the diff is? Probably nothing. The
7757 /// real code will detect the mismatch anyway.
7758
7759 treeLock.release();
7760 }
7761 }
7762
7763 mediumSize = VDGetFileSize(hdd, 0);
7764 mediumLogicalSize = VDGetSize(hdd, 0);
7765
7766 success = true;
7767 }
7768 catch (HRESULT hrcXcpt)
7769 {
7770 hrc = hrcXcpt;
7771 }
7772
7773 vrc = VDDestroy(hdd);
7774 if (RT_FAILURE(vrc))
7775 {
7776 lastAccessError.printf(tr("Could not update and close the medium '%s'%s"),
7777 location.c_str(), i_vdError(vrc).c_str());
7778 success = false;
7779 throw S_OK;
7780 }
7781 }
7782 catch (HRESULT hrcXcpt)
7783 {
7784 hrc = hrcXcpt;
7785 }
7786
7787 autoCaller.release();
7788 treeLock.acquire();
7789 autoCaller.add();
7790 if (FAILED(autoCaller.hrc()))
7791 {
7792 m->queryInfoRunning = false;
7793 return autoCaller.hrc();
7794 }
7795 alock.acquire();
7796
7797 if (success)
7798 {
7799 m->size = mediumSize;
7800 m->logicalSize = mediumLogicalSize;
7801 m->strLastAccessError.setNull();
7802 }
7803 else
7804 {
7805 m->strLastAccessError = lastAccessError;
7806 Log1WarningFunc(("'%s' is not accessible (error='%s', hrc=%Rhrc, vrc=%Rrc)\n",
7807 location.c_str(), m->strLastAccessError.c_str(), hrc, vrc));
7808 }
7809
7810 /* Set the proper state according to the result of the check */
7811 if (success)
7812 m->preLockState = MediumState_Created;
7813 else
7814 m->preLockState = MediumState_Inaccessible;
7815
7816 /* unblock anyone waiting for the i_queryInfo results */
7817 qlock.release();
7818 m->queryInfoRunning = false;
7819
7820 pToken->Abandon();
7821 pToken.setNull();
7822
7823 if (FAILED(hrc))
7824 return hrc;
7825
7826 /* If this is a base image which incorrectly has a parent UUID set,
7827 * repair the image now by zeroing the parent UUID. This is only done
7828 * when we have structural information from a config file, on import
7829 * this is not possible. If someone would accidentally call openMedium
7830 * with a diff image before the base is registered this would destroy
7831 * the diff. Not acceptable. */
7832 do
7833 {
7834 if (fRepairImageZeroParentUuid)
7835 {
7836 hrc = LockWrite(pToken.asOutParam());
7837 if (FAILED(hrc))
7838 break;
7839
7840 alock.release();
7841
7842 try
7843 {
7844 PVDISK hdd;
7845 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7846 ComAssertRCThrow(vrc, E_FAIL);
7847
7848 try
7849 {
7850 vrc = VDOpen(hdd,
7851 format.c_str(),
7852 location.c_str(),
7853 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
7854 m->vdImageIfaces);
7855 if (RT_FAILURE(vrc))
7856 throw S_OK;
7857
7858 RTUUID zeroParentUuid;
7859 RTUuidClear(&zeroParentUuid);
7860 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
7861 ComAssertRCThrow(vrc, E_FAIL);
7862 }
7863 catch (HRESULT hrcXcpt)
7864 {
7865 hrc = hrcXcpt;
7866 }
7867
7868 VDDestroy(hdd);
7869 }
7870 catch (HRESULT hrcXcpt)
7871 {
7872 hrc = hrcXcpt;
7873 }
7874
7875 pToken->Abandon();
7876 pToken.setNull();
7877 if (FAILED(hrc))
7878 break;
7879 }
7880 } while(0);
7881
7882 return hrc;
7883}
7884
7885/**
7886 * Performs extra checks if the medium can be closed and returns S_OK in
7887 * this case. Otherwise, returns a respective error message. Called by
7888 * Close() under the medium tree lock and the medium lock.
7889 *
7890 * @note Also reused by Medium::Reset().
7891 *
7892 * @note Caller must hold the media tree write lock!
7893 */
7894HRESULT Medium::i_canClose()
7895{
7896 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7897
7898 if (i_getChildren().size() != 0)
7899 return setError(VBOX_E_OBJECT_IN_USE,
7900 tr("Cannot close medium '%s' because it has %d child media", "", i_getChildren().size()),
7901 m->strLocationFull.c_str(), i_getChildren().size());
7902
7903 return S_OK;
7904}
7905
7906/**
7907 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
7908 *
7909 * @note Caller must have locked the media tree lock for writing!
7910 */
7911HRESULT Medium::i_unregisterWithVirtualBox()
7912{
7913 /* Note that we need to de-associate ourselves from the parent to let
7914 * VirtualBox::i_unregisterMedium() properly save the registry */
7915
7916 /* we modify m->pParent and access children */
7917 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7918
7919 Medium *pParentBackup = m->pParent;
7920 AssertReturn(i_getChildren().size() == 0, E_FAIL);
7921 if (m->pParent)
7922 i_deparent();
7923
7924 HRESULT hrc = m->pVirtualBox->i_unregisterMedium(this);
7925 if (FAILED(hrc))
7926 {
7927 if (pParentBackup)
7928 {
7929 // re-associate with the parent as we are still relatives in the registry
7930 i_setParent(pParentBackup);
7931 }
7932 }
7933
7934 return hrc;
7935}
7936
7937/**
7938 * Like SetProperty but do not trigger a settings store. Only for internal use!
7939 */
7940HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
7941{
7942 AutoCaller autoCaller(this);
7943 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7944
7945 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
7946
7947 switch (m->state)
7948 {
7949 case MediumState_Created:
7950 case MediumState_Inaccessible:
7951 break;
7952 default:
7953 return i_setStateError();
7954 }
7955
7956 m->mapProperties[aName] = aValue;
7957
7958 return S_OK;
7959}
7960
7961/**
7962 * Sets the extended error info according to the current media state.
7963 *
7964 * @note Must be called from under this object's write or read lock.
7965 */
7966HRESULT Medium::i_setStateError()
7967{
7968 HRESULT hrc;
7969
7970 switch (m->state)
7971 {
7972 case MediumState_NotCreated:
7973 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
7974 tr("Storage for the medium '%s' is not created"),
7975 m->strLocationFull.c_str());
7976 break;
7977 case MediumState_Created:
7978 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
7979 tr("Storage for the medium '%s' is already created"),
7980 m->strLocationFull.c_str());
7981 break;
7982 case MediumState_LockedRead:
7983 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
7984 tr("Medium '%s' is locked for reading by another task"),
7985 m->strLocationFull.c_str());
7986 break;
7987 case MediumState_LockedWrite:
7988 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
7989 tr("Medium '%s' is locked for writing by another task"),
7990 m->strLocationFull.c_str());
7991 break;
7992 case MediumState_Inaccessible:
7993 /* be in sync with Console::powerUpThread() */
7994 if (!m->strLastAccessError.isEmpty())
7995 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
7996 tr("Medium '%s' is not accessible. %s"),
7997 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
7998 else
7999 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8000 tr("Medium '%s' is not accessible"),
8001 m->strLocationFull.c_str());
8002 break;
8003 case MediumState_Creating:
8004 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8005 tr("Storage for the medium '%s' is being created"),
8006 m->strLocationFull.c_str());
8007 break;
8008 case MediumState_Deleting:
8009 hrc = setError(VBOX_E_INVALID_OBJECT_STATE,
8010 tr("Storage for the medium '%s' is being deleted"),
8011 m->strLocationFull.c_str());
8012 break;
8013 default:
8014 AssertFailed();
8015 hrc = E_FAIL;
8016 break;
8017 }
8018
8019 return hrc;
8020}
8021
8022/**
8023 * Sets the value of m->strLocationFull. The given location must be a fully
8024 * qualified path; relative paths are not supported here.
8025 *
8026 * As a special exception, if the specified location is a file path that ends with '/'
8027 * then the file name part will be generated by this method automatically in the format
8028 * '{\<uuid\>}.\<ext\>' where \<uuid\> is a fresh UUID that this method will generate
8029 * and assign to this medium, and \<ext\> is the default extension for this
8030 * medium's storage format. Note that this procedure requires the media state to
8031 * be NotCreated and will return a failure otherwise.
8032 *
8033 * @param aLocation Location of the storage unit. If the location is a FS-path,
8034 * then it can be relative to the VirtualBox home directory.
8035 * @param aFormat Optional fallback format if it is an import and the format
8036 * cannot be determined.
8037 *
8038 * @note Must be called from under this object's write lock.
8039 */
8040HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
8041 const Utf8Str &aFormat /* = Utf8Str::Empty */)
8042{
8043 AssertReturn(!aLocation.isEmpty(), E_FAIL);
8044
8045 AutoCaller autoCaller(this);
8046 AssertComRCReturnRC(autoCaller.hrc());
8047
8048 /* formatObj may be null only when initializing from an existing path and
8049 * no format is known yet */
8050 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
8051 || ( getObjectState().getState() == ObjectState::InInit
8052 && m->state != MediumState_NotCreated
8053 && m->id.isZero()
8054 && m->strFormat.isEmpty()
8055 && m->formatObj.isNull()),
8056 E_FAIL);
8057
8058 /* are we dealing with a new medium constructed using the existing
8059 * location? */
8060 bool isImport = m->strFormat.isEmpty();
8061
8062 if ( isImport
8063 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
8064 && !m->hostDrive))
8065 {
8066 Guid id;
8067
8068 Utf8Str locationFull(aLocation);
8069
8070 if (m->state == MediumState_NotCreated)
8071 {
8072 /* must be a file (formatObj must be already known) */
8073 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
8074
8075 if (RTPathFilename(aLocation.c_str()) == NULL)
8076 {
8077 /* no file name is given (either an empty string or ends with a
8078 * slash), generate a new UUID + file name if the state allows
8079 * this */
8080
8081 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
8082 (tr("Must be at least one extension if it is MediumFormatCapabilities_File\n")),
8083 E_FAIL);
8084
8085 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
8086 ComAssertMsgRet(!strExt.isEmpty(),
8087 (tr("Default extension must not be empty\n")),
8088 E_FAIL);
8089
8090 id.create();
8091
8092 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
8093 aLocation.c_str(), id.raw(), strExt.c_str());
8094 }
8095 }
8096
8097 // we must always have full paths now (if it refers to a file)
8098 if ( ( m->formatObj.isNull()
8099 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
8100 && !RTPathStartsWithRoot(locationFull.c_str()))
8101 return setError(VBOX_E_FILE_ERROR,
8102 tr("The given path '%s' is not fully qualified"),
8103 locationFull.c_str());
8104
8105 /* detect the backend from the storage unit if importing */
8106 if (isImport)
8107 {
8108 VDTYPE const enmDesiredType = i_convertDeviceType();
8109 VDTYPE enmType = VDTYPE_INVALID;
8110 char *backendName = NULL;
8111
8112 /* is it a file? */
8113 RTFILE hFile;
8114 int vrc = RTFileOpen(&hFile, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
8115 if (RT_SUCCESS(vrc))
8116 {
8117 RTFileClose(hFile);
8118 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
8119 locationFull.c_str(), enmDesiredType, &backendName, &enmType);
8120 }
8121 else if ( vrc != VERR_FILE_NOT_FOUND
8122 && vrc != VERR_PATH_NOT_FOUND
8123 && vrc != VERR_ACCESS_DENIED
8124 && locationFull != aLocation)
8125 {
8126 /* assume it's not a file, restore the original location */
8127 locationFull = aLocation;
8128 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
8129 locationFull.c_str(), enmDesiredType, &backendName, &enmType);
8130 }
8131
8132 if (RT_FAILURE(vrc))
8133 {
8134 if (vrc == VERR_ACCESS_DENIED)
8135 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8136 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
8137 locationFull.c_str(), vrc);
8138 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
8139 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8140 tr("Could not find file for the medium '%s' (%Rrc)"),
8141 locationFull.c_str(), vrc);
8142 if (aFormat.isEmpty())
8143 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8144 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
8145 locationFull.c_str(), vrc);
8146 HRESULT hrc = i_setFormat(aFormat);
8147 /* setFormat() must not fail since we've just used the backend so
8148 * the format object must be there */
8149 AssertComRCReturnRC(hrc);
8150 }
8151 else if ( enmType == VDTYPE_INVALID
8152 || m->devType != i_convertToDeviceType(enmType))
8153 {
8154 /*
8155 * The user tried to use a image as a device which is not supported
8156 * by the backend.
8157 */
8158 RTStrFree(backendName);
8159 return setError(E_FAIL,
8160 tr("The medium '%s' can't be used as the requested device type (%s, detected %s)"),
8161 locationFull.c_str(), getDeviceTypeName(m->devType), getVDTypeName(enmType));
8162 }
8163 else
8164 {
8165 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
8166
8167 HRESULT hrc = i_setFormat(backendName);
8168 RTStrFree(backendName);
8169
8170 /* setFormat() must not fail since we've just used the backend so
8171 * the format object must be there */
8172 AssertComRCReturnRC(hrc);
8173 }
8174 }
8175
8176 m->strLocationFull = locationFull;
8177
8178 /* is it still a file? */
8179 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
8180 && (m->state == MediumState_NotCreated)
8181 )
8182 /* assign a new UUID (this UUID will be used when calling
8183 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
8184 * also do that if we didn't generate it to make sure it is
8185 * either generated by us or reset to null */
8186 unconst(m->id) = id;
8187 }
8188 else
8189 m->strLocationFull = aLocation;
8190
8191 return S_OK;
8192}
8193
8194/**
8195 * Checks that the format ID is valid and sets it on success.
8196 *
8197 * Note that this method will caller-reference the format object on success!
8198 * This reference must be released somewhere to let the MediumFormat object be
8199 * uninitialized.
8200 *
8201 * @note Must be called from under this object's write lock.
8202 */
8203HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
8204{
8205 /* get the format object first */
8206 {
8207 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
8208 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
8209
8210 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
8211 if (m->formatObj.isNull())
8212 return setError(E_INVALIDARG,
8213 tr("Invalid medium storage format '%s'"),
8214 aFormat.c_str());
8215
8216 /* get properties (preinsert them as keys in the map). Note that the
8217 * map doesn't grow over the object life time since the set of
8218 * properties is meant to be constant. */
8219
8220 Assert(m->mapProperties.empty());
8221
8222 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
8223 it != m->formatObj->i_getProperties().end();
8224 ++it)
8225 {
8226 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
8227 }
8228 }
8229
8230 unconst(m->strFormat) = aFormat;
8231
8232 return S_OK;
8233}
8234
8235/**
8236 * Converts the Medium device type to the VD type.
8237 */
8238VDTYPE Medium::i_convertDeviceType()
8239{
8240 VDTYPE enmType;
8241
8242 switch (m->devType)
8243 {
8244 case DeviceType_HardDisk:
8245 enmType = VDTYPE_HDD;
8246 break;
8247 case DeviceType_DVD:
8248 enmType = VDTYPE_OPTICAL_DISC;
8249 break;
8250 case DeviceType_Floppy:
8251 enmType = VDTYPE_FLOPPY;
8252 break;
8253 default:
8254 ComAssertFailedRet(VDTYPE_INVALID);
8255 }
8256
8257 return enmType;
8258}
8259
8260/**
8261 * Converts from the VD type to the medium type.
8262 */
8263DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
8264{
8265 DeviceType_T devType;
8266
8267 switch (enmType)
8268 {
8269 case VDTYPE_HDD:
8270 devType = DeviceType_HardDisk;
8271 break;
8272 case VDTYPE_OPTICAL_DISC:
8273 devType = DeviceType_DVD;
8274 break;
8275 case VDTYPE_FLOPPY:
8276 devType = DeviceType_Floppy;
8277 break;
8278 default:
8279 ComAssertFailedRet(DeviceType_Null);
8280 }
8281
8282 return devType;
8283}
8284
8285/**
8286 * Internal method which checks whether a property name is for a filter plugin.
8287 */
8288bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
8289{
8290 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
8291 size_t offSlash;
8292 if ((offSlash = aName.find("/", 0)) != aName.npos)
8293 {
8294 com::Utf8Str strFilter;
8295 com::Utf8Str strKey;
8296
8297 HRESULT hrc = strFilter.assignEx(aName, 0, offSlash);
8298 if (FAILED(hrc))
8299 return false;
8300
8301 hrc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
8302 if (FAILED(hrc))
8303 return false;
8304
8305 VDFILTERINFO FilterInfo;
8306 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
8307 if (RT_SUCCESS(vrc))
8308 {
8309 /* Check that the property exists. */
8310 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
8311 while (paConfig->pszKey)
8312 {
8313 if (strKey.equals(paConfig->pszKey))
8314 return true;
8315 paConfig++;
8316 }
8317 }
8318 }
8319
8320 return false;
8321}
8322
8323/**
8324 * Returns the last error message collected by the i_vdErrorCall callback and
8325 * resets it.
8326 *
8327 * The error message is returned prepended with a dot and a space, like this:
8328 * <code>
8329 * ". <error_text> (%Rrc)"
8330 * </code>
8331 * to make it easily appendable to a more general error message. The @c %Rrc
8332 * format string is given @a aVRC as an argument.
8333 *
8334 * If there is no last error message collected by i_vdErrorCall or if it is a
8335 * null or empty string, then this function returns the following text:
8336 * <code>
8337 * " (%Rrc)"
8338 * </code>
8339 *
8340 * @note Doesn't do any object locking; it is assumed that the caller makes sure
8341 * the callback isn't called by more than one thread at a time.
8342 *
8343 * @param aVRC VBox error code to use when no error message is provided.
8344 */
8345Utf8Str Medium::i_vdError(int aVRC)
8346{
8347 Utf8Str error;
8348
8349 if (m->vdError.isEmpty())
8350 error.printf(" (%Rrc)", aVRC);
8351 else
8352 error.printf(".\n%s", m->vdError.c_str());
8353
8354 m->vdError.setNull();
8355
8356 return error;
8357}
8358
8359/**
8360 * Error message callback.
8361 *
8362 * Puts the reported error message to the m->vdError field.
8363 *
8364 * @note Doesn't do any object locking; it is assumed that the caller makes sure
8365 * the callback isn't called by more than one thread at a time.
8366 *
8367 * @param pvUser The opaque data passed on container creation.
8368 * @param vrc The VBox error code.
8369 * @param SRC_POS Use RT_SRC_POS.
8370 * @param pszFormat Error message format string.
8371 * @param va Error message arguments.
8372 */
8373/*static*/
8374DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int vrc, RT_SRC_POS_DECL,
8375 const char *pszFormat, va_list va)
8376{
8377 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
8378
8379 Medium *that = static_cast<Medium*>(pvUser);
8380 AssertReturnVoid(that != NULL);
8381
8382 va_list vaCopy; /* For gcc */
8383 va_copy(vaCopy, va);
8384 if (that->m->vdError.isEmpty())
8385 that->m->vdError.printf("%N (%Rrc)", pszFormat, &vaCopy, vrc);
8386 else
8387 that->m->vdError.appendPrintf(".\n%N (%Rrc)", pszFormat, &vaCopy, vrc);
8388 va_end(vaCopy);
8389}
8390
8391/* static */
8392DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
8393 const char * /* pszzValid */)
8394{
8395 Medium *that = static_cast<Medium*>(pvUser);
8396 AssertReturn(that != NULL, false);
8397
8398 /* we always return true since the only keys we have are those found in
8399 * VDBACKENDINFO */
8400 return true;
8401}
8402
8403/* static */
8404DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
8405 const char *pszName,
8406 size_t *pcbValue)
8407{
8408 AssertPtrReturn(pcbValue, VERR_INVALID_POINTER);
8409
8410 Medium *that = static_cast<Medium*>(pvUser);
8411 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
8412
8413 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
8414 if (it == that->m->mapProperties.end())
8415 return VERR_CFGM_VALUE_NOT_FOUND;
8416
8417 /* we interpret null values as "no value" in Medium */
8418 if (it->second.isEmpty())
8419 return VERR_CFGM_VALUE_NOT_FOUND;
8420
8421 *pcbValue = it->second.length() + 1 /* include terminator */;
8422
8423 return VINF_SUCCESS;
8424}
8425
8426/* static */
8427DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
8428 const char *pszName,
8429 char *pszValue,
8430 size_t cchValue)
8431{
8432 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
8433
8434 Medium *that = static_cast<Medium*>(pvUser);
8435 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
8436
8437 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
8438 if (it == that->m->mapProperties.end())
8439 return VERR_CFGM_VALUE_NOT_FOUND;
8440
8441 /* we interpret null values as "no value" in Medium */
8442 if (it->second.isEmpty())
8443 return VERR_CFGM_VALUE_NOT_FOUND;
8444
8445 const Utf8Str &value = it->second;
8446 if (value.length() >= cchValue)
8447 return VERR_CFGM_NOT_ENOUGH_SPACE;
8448
8449 memcpy(pszValue, value.c_str(), value.length() + 1);
8450
8451 return VINF_SUCCESS;
8452}
8453
8454DECLCALLBACK(bool) Medium::i_vdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid)
8455{
8456 /* Just return always true here. */
8457 NOREF(pvUser);
8458 NOREF(pszzValid);
8459 return true;
8460}
8461
8462DECLCALLBACK(int) Medium::i_vdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
8463{
8464 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8465 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8466 AssertPtrReturn(pcbValue, VERR_INVALID_POINTER);
8467
8468 size_t cbValue = 0;
8469 if (!strcmp(pszName, "Algorithm"))
8470 cbValue = strlen(pSettings->pszCipher) + 1;
8471 else if (!strcmp(pszName, "KeyId"))
8472 cbValue = sizeof("irrelevant");
8473 else if (!strcmp(pszName, "KeyStore"))
8474 {
8475 if (!pSettings->pszKeyStoreLoad)
8476 return VERR_CFGM_VALUE_NOT_FOUND;
8477 cbValue = strlen(pSettings->pszKeyStoreLoad) + 1;
8478 }
8479 else if (!strcmp(pszName, "CreateKeyStore"))
8480 cbValue = 2; /* Single digit + terminator. */
8481 else
8482 return VERR_CFGM_VALUE_NOT_FOUND;
8483
8484 *pcbValue = cbValue + 1 /* include terminator */;
8485
8486 return VINF_SUCCESS;
8487}
8488
8489DECLCALLBACK(int) Medium::i_vdCryptoConfigQuery(void *pvUser, const char *pszName,
8490 char *pszValue, size_t cchValue)
8491{
8492 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8493 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8494 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
8495
8496 const char *psz = NULL;
8497 if (!strcmp(pszName, "Algorithm"))
8498 psz = pSettings->pszCipher;
8499 else if (!strcmp(pszName, "KeyId"))
8500 psz = "irrelevant";
8501 else if (!strcmp(pszName, "KeyStore"))
8502 psz = pSettings->pszKeyStoreLoad;
8503 else if (!strcmp(pszName, "CreateKeyStore"))
8504 {
8505 if (pSettings->fCreateKeyStore)
8506 psz = "1";
8507 else
8508 psz = "0";
8509 }
8510 else
8511 return VERR_CFGM_VALUE_NOT_FOUND;
8512
8513 size_t cch = strlen(psz);
8514 if (cch >= cchValue)
8515 return VERR_CFGM_NOT_ENOUGH_SPACE;
8516
8517 memcpy(pszValue, psz, cch + 1);
8518 return VINF_SUCCESS;
8519}
8520
8521DECLCALLBACK(int) Medium::i_vdConfigUpdate(void *pvUser,
8522 bool fCreate,
8523 const char *pszName,
8524 const char *pszValue)
8525{
8526 Medium *that = (Medium *)pvUser;
8527
8528 // Detect if this runs inside i_queryInfo() on the current thread.
8529 // Skip if not. Check does not need synchronization.
8530 if (!that->m || !that->m->queryInfoRunning || !that->m->queryInfoSem.isWriteLockOnCurrentThread())
8531 return VINF_SUCCESS;
8532
8533 // It's guaranteed that this code is executing inside Medium::i_queryInfo,
8534 // can assume it took care of synchronization.
8535 int rv = VINF_SUCCESS;
8536 Utf8Str strName(pszName);
8537 settings::StringsMap::const_iterator it = that->m->mapProperties.find(strName);
8538 if (it == that->m->mapProperties.end() && !fCreate)
8539 rv = VERR_CFGM_VALUE_NOT_FOUND;
8540 else
8541 that->m->mapProperties[strName] = Utf8Str(pszValue);
8542 return rv;
8543}
8544
8545DECLCALLBACK(int) Medium::i_vdCryptoKeyRetain(void *pvUser, const char *pszId,
8546 const uint8_t **ppbKey, size_t *pcbKey)
8547{
8548 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8549 NOREF(pszId);
8550 NOREF(ppbKey);
8551 NOREF(pcbKey);
8552 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8553 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8554}
8555
8556DECLCALLBACK(int) Medium::i_vdCryptoKeyRelease(void *pvUser, const char *pszId)
8557{
8558 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8559 NOREF(pszId);
8560 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8561 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8562}
8563
8564DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
8565{
8566 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8567 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8568
8569 NOREF(pszId);
8570 *ppszPassword = pSettings->pszPassword;
8571 return VINF_SUCCESS;
8572}
8573
8574DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
8575{
8576 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8577 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8578 NOREF(pszId);
8579 return VINF_SUCCESS;
8580}
8581
8582DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore)
8583{
8584 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8585 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8586
8587 pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore);
8588 if (!pSettings->pszKeyStore)
8589 return VERR_NO_MEMORY;
8590
8591 memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore);
8592 return VINF_SUCCESS;
8593}
8594
8595DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher,
8596 const uint8_t *pbDek, size_t cbDek)
8597{
8598 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8599 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8600
8601 pSettings->pszCipherReturned = RTStrDup(pszCipher);
8602 pSettings->pbDek = pbDek;
8603 pSettings->cbDek = cbDek;
8604
8605 return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY;
8606}
8607
8608/**
8609 * Creates a VDISK instance for this medium.
8610 *
8611 * @note Caller should not hold any medium related locks as this method will
8612 * acquire the medium lock for writing and others (VirtualBox).
8613 *
8614 * @returns COM status code.
8615 * @param fWritable Whether to return a writable VDISK instance
8616 * (true) or a read-only one (false).
8617 * @param pKeyStore The key store.
8618 * @param ppHdd Where to return the pointer to the VDISK on
8619 * success.
8620 * @param pMediumLockList The lock list to populate and lock. Caller
8621 * is responsible for calling the destructor or
8622 * MediumLockList::Clear() after destroying
8623 * @a *ppHdd
8624 * @param pCryptoSettings The crypto settings to use for setting up
8625 * decryption/encryption of the VDISK. This object
8626 * must be alive until the VDISK is destroyed!
8627 */
8628HRESULT Medium::i_openForIO(bool fWritable, SecretKeyStore *pKeyStore, PVDISK *ppHdd, MediumLockList *pMediumLockList,
8629 MediumCryptoFilterSettings *pCryptoSettings)
8630{
8631 /*
8632 * Create the media lock list and lock the media.
8633 */
8634 HRESULT hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
8635 fWritable ? this : NULL /* pToLockWrite */,
8636 false /* fMediumLockWriteAll */,
8637 NULL,
8638 *pMediumLockList);
8639 if (SUCCEEDED(hrc))
8640 hrc = pMediumLockList->Lock();
8641 if (FAILED(hrc))
8642 return hrc;
8643
8644 /*
8645 * Get the base medium before write locking this medium.
8646 */
8647 ComObjPtr<Medium> pBase = i_getBase();
8648 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8649
8650 /*
8651 * Create the VDISK instance.
8652 */
8653 PVDISK pHdd;
8654 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pHdd);
8655 AssertRCReturn(vrc, E_FAIL);
8656
8657 /*
8658 * Goto avoidance using try/catch/throw(HRESULT).
8659 */
8660 try
8661 {
8662 settings::StringsMap::iterator itKeyStore = pBase->m->mapProperties.find("CRYPT/KeyStore");
8663 if (itKeyStore != pBase->m->mapProperties.end())
8664 {
8665#ifdef VBOX_WITH_EXTPACK
8666 settings::StringsMap::iterator itKeyId = pBase->m->mapProperties.find("CRYPT/KeyId");
8667
8668 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
8669 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
8670 {
8671 /* Load the plugin */
8672 Utf8Str strPlugin;
8673 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
8674 if (SUCCEEDED(hrc))
8675 {
8676 vrc = VDPluginLoadFromFilename(strPlugin.c_str());
8677 if (RT_FAILURE(vrc))
8678 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
8679 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
8680 i_vdError(vrc).c_str());
8681 }
8682 else
8683 throw setError(VBOX_E_NOT_SUPPORTED,
8684 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
8685 ORACLE_PUEL_EXTPACK_NAME);
8686 }
8687 else
8688 throw setError(VBOX_E_NOT_SUPPORTED,
8689 tr("Encryption is not supported because the extension pack '%s' is missing"),
8690 ORACLE_PUEL_EXTPACK_NAME);
8691
8692 if (itKeyId == pBase->m->mapProperties.end())
8693 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8694 tr("Image '%s' is configured for encryption but doesn't has a key identifier set"),
8695 pBase->m->strLocationFull.c_str());
8696
8697 /* Find the proper secret key in the key store. */
8698 if (!pKeyStore)
8699 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8700 tr("Image '%s' is configured for encryption but there is no key store to retrieve the password from"),
8701 pBase->m->strLocationFull.c_str());
8702
8703 SecretKey *pKey = NULL;
8704 vrc = pKeyStore->retainSecretKey(itKeyId->second, &pKey);
8705 if (RT_FAILURE(vrc))
8706 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
8707 tr("Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)"),
8708 itKeyId->second.c_str(), vrc);
8709
8710 i_taskEncryptSettingsSetup(pCryptoSettings, NULL, itKeyStore->second.c_str(), (const char *)pKey->getKeyBuffer(),
8711 false /* fCreateKeyStore */);
8712 vrc = VDFilterAdd(pHdd, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pCryptoSettings->vdFilterIfaces);
8713 pKeyStore->releaseSecretKey(itKeyId->second);
8714 if (vrc == VERR_VD_PASSWORD_INCORRECT)
8715 throw setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc, tr("The password to decrypt the image is incorrect"));
8716 if (RT_FAILURE(vrc))
8717 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc, tr("Failed to load the decryption filter: %s"),
8718 i_vdError(vrc).c_str());
8719#else
8720 RT_NOREF(pKeyStore, pCryptoSettings);
8721 throw setError(VBOX_E_NOT_SUPPORTED,
8722 tr("Encryption is not supported because extension pack support is not built in"));
8723#endif /* VBOX_WITH_EXTPACK */
8724 }
8725
8726 /*
8727 * Open all media in the source chain.
8728 */
8729 MediumLockList::Base::const_iterator sourceListBegin = pMediumLockList->GetBegin();
8730 MediumLockList::Base::const_iterator sourceListEnd = pMediumLockList->GetEnd();
8731 MediumLockList::Base::const_iterator mediumListLast = sourceListEnd;
8732 --mediumListLast;
8733
8734 for (MediumLockList::Base::const_iterator it = sourceListBegin; it != sourceListEnd; ++it)
8735 {
8736 const MediumLock &mediumLock = *it;
8737 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8738 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8739
8740 /* sanity check */
8741 Assert(pMedium->m->state == (fWritable && it == mediumListLast ? MediumState_LockedWrite : MediumState_LockedRead));
8742
8743 /* Open all media in read-only mode. */
8744 vrc = VDOpen(pHdd,
8745 pMedium->m->strFormat.c_str(),
8746 pMedium->m->strLocationFull.c_str(),
8747 m->uOpenFlagsDef | (fWritable && it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
8748 pMedium->m->vdImageIfaces);
8749 if (RT_FAILURE(vrc))
8750 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8751 tr("Could not open the medium storage unit '%s'%s"),
8752 pMedium->m->strLocationFull.c_str(),
8753 i_vdError(vrc).c_str());
8754 }
8755
8756 Assert(m->state == (fWritable ? MediumState_LockedWrite : MediumState_LockedRead));
8757
8758 /*
8759 * Done!
8760 */
8761 *ppHdd = pHdd;
8762 return S_OK;
8763 }
8764 catch (HRESULT hrc2)
8765 {
8766 hrc = hrc2;
8767 }
8768
8769 VDDestroy(pHdd);
8770 return hrc;
8771
8772}
8773
8774/**
8775 * Implementation code for the "create base" task.
8776 *
8777 * This only gets started from Medium::CreateBaseStorage() and always runs
8778 * asynchronously. As a result, we always save the VirtualBox.xml file when
8779 * we're done here.
8780 *
8781 * @param task
8782 * @return
8783 */
8784HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
8785{
8786 /** @todo r=klaus The code below needs to be double checked with regard
8787 * to lock order violations, it probably causes lock order issues related
8788 * to the AutoCaller usage. */
8789 HRESULT hrc = S_OK;
8790
8791 /* these parameters we need after creation */
8792 uint64_t size = 0, logicalSize = 0;
8793 MediumVariant_T variant = MediumVariant_Standard;
8794 bool fGenerateUuid = false;
8795
8796 try
8797 {
8798 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8799
8800 /* The object may request a specific UUID (through a special form of
8801 * the moveTo() argument). Otherwise we have to generate it */
8802 Guid id = m->id;
8803
8804 fGenerateUuid = id.isZero();
8805 if (fGenerateUuid)
8806 {
8807 id.create();
8808 /* VirtualBox::i_registerMedium() will need UUID */
8809 unconst(m->id) = id;
8810 }
8811
8812 Utf8Str format(m->strFormat);
8813 Utf8Str location(m->strLocationFull);
8814 uint64_t capabilities = m->formatObj->i_getCapabilities();
8815 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
8816 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
8817 Assert(m->state == MediumState_Creating);
8818
8819 PVDISK hdd;
8820 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8821 ComAssertRCThrow(vrc, E_FAIL);
8822
8823 /* unlock before the potentially lengthy operation */
8824 thisLock.release();
8825
8826 try
8827 {
8828 /* ensure the directory exists */
8829 if (capabilities & MediumFormatCapabilities_File)
8830 {
8831 hrc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8832 if (FAILED(hrc))
8833 throw hrc;
8834 }
8835
8836 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
8837
8838 vrc = VDCreateBase(hdd,
8839 format.c_str(),
8840 location.c_str(),
8841 task.mSize,
8842 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
8843 NULL,
8844 &geo,
8845 &geo,
8846 id.raw(),
8847 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8848 m->vdImageIfaces,
8849 task.mVDOperationIfaces);
8850 if (RT_FAILURE(vrc))
8851 {
8852 if (vrc == VERR_VD_INVALID_TYPE)
8853 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8854 tr("Parameters for creating the medium storage unit '%s' are invalid%s"),
8855 location.c_str(), i_vdError(vrc).c_str());
8856 else
8857 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8858 tr("Could not create the medium storage unit '%s'%s"),
8859 location.c_str(), i_vdError(vrc).c_str());
8860 }
8861
8862 if (task.mVariant & MediumVariant_Formatted)
8863 {
8864 RTVFSFILE hVfsFile;
8865 vrc = VDCreateVfsFileFromDisk(hdd, 0 /*fFlags*/, &hVfsFile);
8866 if (RT_FAILURE(vrc))
8867 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Opening medium storage unit '%s' failed%s"),
8868 location.c_str(), i_vdError(vrc).c_str());
8869 RTERRINFOSTATIC ErrInfo;
8870 vrc = RTFsFatVolFormat(hVfsFile, 0 /* offVol */, 0 /* cbVol */, RTFSFATVOL_FMT_F_FULL,
8871 0 /* cbSector */, 0 /* cbSectorPerCluster */, RTFSFATTYPE_INVALID,
8872 0 /* cHeads */, 0 /* cSectorsPerTrack*/, 0 /* bMedia */,
8873 0 /* cRootDirEntries */, 0 /* cHiddenSectors */,
8874 RTErrInfoInitStatic(&ErrInfo));
8875 RTVfsFileRelease(hVfsFile);
8876 if (RT_FAILURE(vrc) && RTErrInfoIsSet(&ErrInfo.Core))
8877 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed: %s"),
8878 location.c_str(), ErrInfo.Core.pszMsg);
8879 if (RT_FAILURE(vrc))
8880 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed%s"),
8881 location.c_str(), i_vdError(vrc).c_str());
8882 }
8883
8884 size = VDGetFileSize(hdd, 0);
8885 logicalSize = VDGetSize(hdd, 0);
8886 unsigned uImageFlags;
8887 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8888 if (RT_SUCCESS(vrc))
8889 variant = (MediumVariant_T)uImageFlags;
8890 }
8891 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
8892
8893 VDDestroy(hdd);
8894 }
8895 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
8896
8897 if (SUCCEEDED(hrc))
8898 {
8899 /* register with mVirtualBox as the last step and move to
8900 * Created state only on success (leaving an orphan file is
8901 * better than breaking media registry consistency) */
8902 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8903 ComObjPtr<Medium> pMedium;
8904 hrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
8905 Assert(pMedium == NULL || this == pMedium);
8906 }
8907
8908 // re-acquire the lock before changing state
8909 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8910
8911 if (SUCCEEDED(hrc))
8912 {
8913 m->state = MediumState_Created;
8914
8915 m->size = size;
8916 m->logicalSize = logicalSize;
8917 m->variant = variant;
8918
8919 thisLock.release();
8920 i_markRegistriesModified();
8921 if (task.isAsync())
8922 {
8923 // in asynchronous mode, save settings now
8924 m->pVirtualBox->i_saveModifiedRegistries();
8925 }
8926 }
8927 else
8928 {
8929 /* back to NotCreated on failure */
8930 m->state = MediumState_NotCreated;
8931
8932 /* reset UUID to prevent it from being reused next time */
8933 if (fGenerateUuid)
8934 unconst(m->id).clear();
8935 }
8936
8937 if (task.NotifyAboutChanges() && SUCCEEDED(hrc))
8938 {
8939 m->pVirtualBox->i_onMediumConfigChanged(this);
8940 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
8941 }
8942
8943 return hrc;
8944}
8945
8946/**
8947 * Implementation code for the "create diff" task.
8948 *
8949 * This task always gets started from Medium::createDiffStorage() and can run
8950 * synchronously or asynchronously depending on the "wait" parameter passed to
8951 * that function. If we run synchronously, the caller expects the medium
8952 * registry modification to be set before returning; otherwise (in asynchronous
8953 * mode), we save the settings ourselves.
8954 *
8955 * @param task
8956 * @return
8957 */
8958HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
8959{
8960 /** @todo r=klaus The code below needs to be double checked with regard
8961 * to lock order violations, it probably causes lock order issues related
8962 * to the AutoCaller usage. */
8963 HRESULT hrcTmp = S_OK;
8964
8965 const ComObjPtr<Medium> &pTarget = task.mTarget;
8966
8967 uint64_t size = 0, logicalSize = 0;
8968 MediumVariant_T variant = MediumVariant_Standard;
8969 bool fGenerateUuid = false;
8970
8971 try
8972 {
8973 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8974 {
8975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8976 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8977 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"),
8978 m->strLocationFull.c_str());
8979 }
8980
8981 /* Lock both in {parent,child} order. */
8982 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8983
8984 /* The object may request a specific UUID (through a special form of
8985 * the moveTo() argument). Otherwise we have to generate it */
8986 Guid targetId = pTarget->m->id;
8987
8988 fGenerateUuid = targetId.isZero();
8989 if (fGenerateUuid)
8990 {
8991 targetId.create();
8992 /* VirtualBox::i_registerMedium() will need UUID */
8993 unconst(pTarget->m->id) = targetId;
8994 }
8995
8996 Guid id = m->id;
8997
8998 Utf8Str targetFormat(pTarget->m->strFormat);
8999 Utf8Str targetLocation(pTarget->m->strLocationFull);
9000 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
9001 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
9002
9003 Assert(pTarget->m->state == MediumState_Creating);
9004 Assert(m->state == MediumState_LockedRead);
9005
9006 PVDISK hdd;
9007 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9008 ComAssertRCThrow(vrc, E_FAIL);
9009
9010 /* the two media are now protected by their non-default states;
9011 * unlock the media before the potentially lengthy operation */
9012 mediaLock.release();
9013
9014 try
9015 {
9016 /* Open all media in the target chain but the last. */
9017 MediumLockList::Base::const_iterator targetListBegin =
9018 task.mpMediumLockList->GetBegin();
9019 MediumLockList::Base::const_iterator targetListEnd =
9020 task.mpMediumLockList->GetEnd();
9021 for (MediumLockList::Base::const_iterator it = targetListBegin;
9022 it != targetListEnd;
9023 ++it)
9024 {
9025 const MediumLock &mediumLock = *it;
9026 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9027
9028 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9029
9030 /* Skip over the target diff medium */
9031 if (pMedium->m->state == MediumState_Creating)
9032 continue;
9033
9034 /* sanity check */
9035 Assert(pMedium->m->state == MediumState_LockedRead);
9036
9037 /* Open all media in appropriate mode. */
9038 vrc = VDOpen(hdd,
9039 pMedium->m->strFormat.c_str(),
9040 pMedium->m->strLocationFull.c_str(),
9041 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9042 pMedium->m->vdImageIfaces);
9043 if (RT_FAILURE(vrc))
9044 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9045 tr("Could not open the medium storage unit '%s'%s"),
9046 pMedium->m->strLocationFull.c_str(),
9047 i_vdError(vrc).c_str());
9048 }
9049
9050 /* ensure the target directory exists */
9051 if (capabilities & MediumFormatCapabilities_File)
9052 {
9053 HRESULT hrc = VirtualBox::i_ensureFilePathExists(targetLocation,
9054 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9055 if (FAILED(hrc))
9056 throw hrc;
9057 }
9058
9059 vrc = VDCreateDiff(hdd,
9060 targetFormat.c_str(),
9061 targetLocation.c_str(),
9062 (task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk))
9063 | VD_IMAGE_FLAGS_DIFF,
9064 NULL,
9065 targetId.raw(),
9066 id.raw(),
9067 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9068 pTarget->m->vdImageIfaces,
9069 task.mVDOperationIfaces);
9070 if (RT_FAILURE(vrc))
9071 {
9072 if (vrc == VERR_VD_INVALID_TYPE)
9073 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9074 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
9075 targetLocation.c_str(), i_vdError(vrc).c_str());
9076 else
9077 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9078 tr("Could not create the differencing medium storage unit '%s'%s"),
9079 targetLocation.c_str(), i_vdError(vrc).c_str());
9080 }
9081
9082 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9083 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9084 unsigned uImageFlags;
9085 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9086 if (RT_SUCCESS(vrc))
9087 variant = (MediumVariant_T)uImageFlags;
9088 }
9089 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9090
9091 VDDestroy(hdd);
9092 }
9093 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9094
9095 MultiResult mrc(hrcTmp);
9096
9097 if (SUCCEEDED(mrc))
9098 {
9099 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9100
9101 Assert(pTarget->m->pParent.isNull());
9102
9103 /* associate child with the parent, maximum depth was checked above */
9104 pTarget->i_setParent(this);
9105
9106 /* diffs for immutable media are auto-reset by default */
9107 bool fAutoReset;
9108 {
9109 ComObjPtr<Medium> pBase = i_getBase();
9110 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
9111 fAutoReset = (pBase->m->type == MediumType_Immutable);
9112 }
9113 {
9114 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
9115 pTarget->m->autoReset = fAutoReset;
9116 }
9117
9118 /* register with mVirtualBox as the last step and move to
9119 * Created state only on success (leaving an orphan file is
9120 * better than breaking media registry consistency) */
9121 ComObjPtr<Medium> pMedium;
9122 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
9123 Assert(pTarget == pMedium);
9124
9125 if (FAILED(mrc))
9126 /* break the parent association on failure to register */
9127 i_deparent();
9128 }
9129
9130 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
9131
9132 if (SUCCEEDED(mrc))
9133 {
9134 pTarget->m->state = MediumState_Created;
9135
9136 pTarget->m->size = size;
9137 pTarget->m->logicalSize = logicalSize;
9138 pTarget->m->variant = variant;
9139 }
9140 else
9141 {
9142 /* back to NotCreated on failure */
9143 pTarget->m->state = MediumState_NotCreated;
9144
9145 pTarget->m->autoReset = false;
9146
9147 /* reset UUID to prevent it from being reused next time */
9148 if (fGenerateUuid)
9149 unconst(pTarget->m->id).clear();
9150 }
9151
9152 // deregister the task registered in createDiffStorage()
9153 Assert(m->numCreateDiffTasks != 0);
9154 --m->numCreateDiffTasks;
9155
9156 mediaLock.release();
9157 i_markRegistriesModified();
9158 if (task.isAsync())
9159 {
9160 // in asynchronous mode, save settings now
9161 m->pVirtualBox->i_saveModifiedRegistries();
9162 }
9163
9164 /* Note that in sync mode, it's the caller's responsibility to
9165 * unlock the medium. */
9166
9167 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
9168 {
9169 m->pVirtualBox->i_onMediumConfigChanged(this);
9170 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
9171 }
9172
9173 return mrc;
9174}
9175
9176/**
9177 * Implementation code for the "merge" task.
9178 *
9179 * This task always gets started from Medium::mergeTo() and can run
9180 * synchronously or asynchronously depending on the "wait" parameter passed to
9181 * that function. If we run synchronously, the caller expects the medium
9182 * registry modification to be set before returning; otherwise (in asynchronous
9183 * mode), we save the settings ourselves.
9184 *
9185 * @param task
9186 * @return
9187 */
9188HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
9189{
9190 /** @todo r=klaus The code below needs to be double checked with regard
9191 * to lock order violations, it probably causes lock order issues related
9192 * to the AutoCaller usage. */
9193 HRESULT hrcTmp = S_OK;
9194
9195 const ComObjPtr<Medium> &pTarget = task.mTarget;
9196
9197 try
9198 {
9199 if (!task.mParentForTarget.isNull())
9200 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9201 {
9202 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
9203 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9204 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9205 task.mParentForTarget->m->strLocationFull.c_str());
9206 }
9207
9208 // Resize target to source size, if possible. Otherwise throw an error.
9209 // It's offline resizing. Online resizing will be called in the
9210 // SessionMachine::onlineMergeMedium.
9211
9212 uint64_t sourceSize = 0;
9213 Utf8Str sourceName;
9214 {
9215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9216 sourceSize = i_getLogicalSize();
9217 sourceName = i_getName();
9218 }
9219 uint64_t targetSize = 0;
9220 Utf8Str targetName;
9221 {
9222 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
9223 targetSize = pTarget->i_getLogicalSize();
9224 targetName = pTarget->i_getName();
9225 }
9226
9227 //reducing vm disks are not implemented yet
9228 if (sourceSize > targetSize)
9229 {
9230 if (i_isMediumFormatFile())
9231 {
9232 /// @todo r=klaus Can this use the standard code for creating a medium lock list?
9233 // Have to make own lock list, because "resize" method resizes the last image
9234 // in the lock chain only. The lock chain is already in the task.mpMediumLockList,
9235 // so just make new lock list based on it, with the right last medium. The own
9236 // lock list skips double locking and therefore does not affect the general lock
9237 // state after the "resize" method.
9238 MediumLockList* pMediumLockListForResize = new MediumLockList();
9239
9240 for (MediumLockList::Base::iterator it = task.mpMediumLockList->GetBegin();
9241 it != task.mpMediumLockList->GetEnd();
9242 ++it)
9243 {
9244 ComObjPtr<Medium> pMedium = it->GetMedium();
9245 pMediumLockListForResize->Append(pMedium, pMedium->m->state == MediumState_LockedWrite);
9246 if (pMedium == pTarget)
9247 break;
9248 }
9249
9250 // just to switch internal state of the lock list to avoid errors during list deletion,
9251 // because all media in the list already locked by task.mpMediumLockList
9252 HRESULT hrc = pMediumLockListForResize->Lock(true /* fSkipOverLockedMedia */);
9253 if (FAILED(hrc))
9254 {
9255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9256 delete pMediumLockListForResize;
9257 throw setError(hrc,
9258 tr("Failed to lock the medium '%s' to resize before merge"),
9259 targetName.c_str());
9260 }
9261
9262 ComObjPtr<Progress> pProgress(task.GetProgressObject());
9263 hrc = pTarget->i_resize(sourceSize, pMediumLockListForResize, &pProgress, true, false);
9264 if (FAILED(hrc))
9265 {
9266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9267 delete pMediumLockListForResize;
9268 throw setError(hrc,
9269 tr("Failed to set size of '%s' to size of '%s'"),
9270 targetName.c_str(), sourceName.c_str());
9271 }
9272 delete pMediumLockListForResize;
9273 }
9274 else
9275 {
9276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9277 throw setError(VBOX_E_NOT_SUPPORTED,
9278 tr("Sizes of '%s' and '%s' are different and medium format does not support resing"),
9279 sourceName.c_str(), targetName.c_str());
9280 }
9281 }
9282
9283 task.GetProgressObject()->SetNextOperation(BstrFmt(tr("Merging medium '%s' to '%s'"),
9284 i_getName().c_str(),
9285 targetName.c_str()).raw(),
9286 1);
9287
9288 PVDISK hdd;
9289 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9290 ComAssertRCThrow(vrc, E_FAIL);
9291
9292 try
9293 {
9294 // Similar code appears in SessionMachine::onlineMergeMedium, so
9295 // if you make any changes below check whether they are applicable
9296 // in that context as well.
9297
9298 unsigned uTargetIdx = VD_LAST_IMAGE;
9299 unsigned uSourceIdx = VD_LAST_IMAGE;
9300 /* Open all media in the chain. */
9301 MediumLockList::Base::iterator lockListBegin =
9302 task.mpMediumLockList->GetBegin();
9303 MediumLockList::Base::iterator lockListEnd =
9304 task.mpMediumLockList->GetEnd();
9305 unsigned i = 0;
9306 for (MediumLockList::Base::iterator it = lockListBegin;
9307 it != lockListEnd;
9308 ++it)
9309 {
9310 MediumLock &mediumLock = *it;
9311 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9312
9313 if (pMedium == this)
9314 uSourceIdx = i;
9315 else if (pMedium == pTarget)
9316 uTargetIdx = i;
9317
9318 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9319
9320 /*
9321 * complex sanity (sane complexity)
9322 *
9323 * The current medium must be in the Deleting (medium is merged)
9324 * or LockedRead (parent medium) state if it is not the target.
9325 * If it is the target it must be in the LockedWrite state.
9326 */
9327 Assert( ( pMedium != pTarget
9328 && ( pMedium->m->state == MediumState_Deleting
9329 || pMedium->m->state == MediumState_LockedRead))
9330 || ( pMedium == pTarget
9331 && pMedium->m->state == MediumState_LockedWrite));
9332 /*
9333 * Medium must be the target, in the LockedRead state
9334 * or Deleting state where it is not allowed to be attached
9335 * to a virtual machine.
9336 */
9337 Assert( pMedium == pTarget
9338 || pMedium->m->state == MediumState_LockedRead
9339 || ( pMedium->m->backRefs.size() == 0
9340 && pMedium->m->state == MediumState_Deleting));
9341 /* The source medium must be in Deleting state. */
9342 Assert( pMedium != this
9343 || pMedium->m->state == MediumState_Deleting);
9344
9345 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9346
9347 if ( pMedium->m->state == MediumState_LockedRead
9348 || pMedium->m->state == MediumState_Deleting)
9349 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9350 if (pMedium->m->type == MediumType_Shareable)
9351 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9352
9353 /* Open the medium */
9354 vrc = VDOpen(hdd,
9355 pMedium->m->strFormat.c_str(),
9356 pMedium->m->strLocationFull.c_str(),
9357 uOpenFlags | m->uOpenFlagsDef,
9358 pMedium->m->vdImageIfaces);
9359 if (RT_FAILURE(vrc))
9360 throw vrc;
9361
9362 i++;
9363 }
9364
9365 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
9366 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
9367
9368 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
9369 task.mVDOperationIfaces);
9370 if (RT_FAILURE(vrc))
9371 throw vrc;
9372
9373 /* update parent UUIDs */
9374 if (!task.mfMergeForward)
9375 {
9376 /* we need to update UUIDs of all source's children
9377 * which cannot be part of the container at once so
9378 * add each one in there individually */
9379 if (task.mpChildrenToReparent)
9380 {
9381 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9382 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9383 for (MediumLockList::Base::iterator it = childrenBegin;
9384 it != childrenEnd;
9385 ++it)
9386 {
9387 Medium *pMedium = it->GetMedium();
9388 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
9389 vrc = VDOpen(hdd,
9390 pMedium->m->strFormat.c_str(),
9391 pMedium->m->strLocationFull.c_str(),
9392 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9393 pMedium->m->vdImageIfaces);
9394 if (RT_FAILURE(vrc))
9395 throw vrc;
9396
9397 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
9398 pTarget->m->id.raw());
9399 if (RT_FAILURE(vrc))
9400 throw vrc;
9401
9402 vrc = VDClose(hdd, false /* fDelete */);
9403 if (RT_FAILURE(vrc))
9404 throw vrc;
9405 }
9406 }
9407 }
9408 }
9409 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9410 catch (int aVRC)
9411 {
9412 hrcTmp = setErrorBoth(VBOX_E_FILE_ERROR, aVRC,
9413 tr("Could not merge the medium '%s' to '%s'%s"),
9414 m->strLocationFull.c_str(),
9415 pTarget->m->strLocationFull.c_str(),
9416 i_vdError(aVRC).c_str());
9417 }
9418
9419 VDDestroy(hdd);
9420 }
9421 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9422
9423 ErrorInfoKeeper eik;
9424 MultiResult mrc(hrcTmp);
9425 HRESULT hrc2;
9426
9427 std::set<ComObjPtr<Medium> > pMediaForNotify;
9428 std::map<Guid, DeviceType_T> uIdsForNotify;
9429
9430 if (SUCCEEDED(mrc))
9431 {
9432 /* all media but the target were successfully deleted by
9433 * VDMerge; reparent the last one and uninitialize deleted media. */
9434
9435 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9436
9437 if (task.mfMergeForward)
9438 {
9439 /* first, unregister the target since it may become a base
9440 * medium which needs re-registration */
9441 hrc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
9442 AssertComRC(hrc2);
9443
9444 /* then, reparent it and disconnect the deleted branch at both ends
9445 * (chain->parent() is source's parent). Depth check above. */
9446 pTarget->i_deparent();
9447 pTarget->i_setParent(task.mParentForTarget);
9448 if (task.mParentForTarget)
9449 {
9450 i_deparent();
9451 if (task.NotifyAboutChanges())
9452 pMediaForNotify.insert(task.mParentForTarget);
9453 }
9454
9455 /* then, register again */
9456 ComObjPtr<Medium> pMedium;
9457 hrc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
9458 AssertComRC(hrc2);
9459 }
9460 else
9461 {
9462 Assert(pTarget->i_getChildren().size() == 1);
9463 Medium *targetChild = pTarget->i_getChildren().front();
9464
9465 /* disconnect the deleted branch at the elder end */
9466 targetChild->i_deparent();
9467
9468 /* reparent source's children and disconnect the deleted
9469 * branch at the younger end */
9470 if (task.mpChildrenToReparent)
9471 {
9472 /* obey {parent,child} lock order */
9473 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
9474
9475 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9476 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9477 for (MediumLockList::Base::iterator it = childrenBegin;
9478 it != childrenEnd;
9479 ++it)
9480 {
9481 Medium *pMedium = it->GetMedium();
9482 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
9483
9484 pMedium->i_deparent(); // removes pMedium from source
9485 // no depth check, reduces depth
9486 pMedium->i_setParent(pTarget);
9487
9488 if (task.NotifyAboutChanges())
9489 pMediaForNotify.insert(pMedium);
9490 }
9491 }
9492 pMediaForNotify.insert(pTarget);
9493 }
9494
9495 /* unregister and uninitialize all media removed by the merge */
9496 MediumLockList::Base::iterator lockListBegin =
9497 task.mpMediumLockList->GetBegin();
9498 MediumLockList::Base::iterator lockListEnd =
9499 task.mpMediumLockList->GetEnd();
9500 for (MediumLockList::Base::iterator it = lockListBegin;
9501 it != lockListEnd;
9502 )
9503 {
9504 MediumLock &mediumLock = *it;
9505 /* Create a real copy of the medium pointer, as the medium
9506 * lock deletion below would invalidate the referenced object. */
9507 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
9508
9509 /* The target and all media not merged (readonly) are skipped */
9510 if ( pMedium == pTarget
9511 || pMedium->m->state == MediumState_LockedRead)
9512 {
9513 ++it;
9514 continue;
9515 }
9516
9517 uIdsForNotify[pMedium->i_getId()] = pMedium->i_getDeviceType();
9518 hrc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
9519 AssertComRC(hrc2);
9520
9521 /* now, uninitialize the deleted medium (note that
9522 * due to the Deleting state, uninit() will not touch
9523 * the parent-child relationship so we need to
9524 * uninitialize each disk individually) */
9525
9526 /* note that the operation initiator medium (which is
9527 * normally also the source medium) is a special case
9528 * -- there is one more caller added by Task to it which
9529 * we must release. Also, if we are in sync mode, the
9530 * caller may still hold an AutoCaller instance for it
9531 * and therefore we cannot uninit() it (it's therefore
9532 * the caller's responsibility) */
9533 if (pMedium == this)
9534 {
9535 Assert(i_getChildren().size() == 0);
9536 Assert(m->backRefs.size() == 0);
9537 task.mMediumCaller.release();
9538 }
9539
9540 /* Delete the medium lock list entry, which also releases the
9541 * caller added by MergeChain before uninit() and updates the
9542 * iterator to point to the right place. */
9543 hrc2 = task.mpMediumLockList->RemoveByIterator(it);
9544 AssertComRC(hrc2);
9545
9546 if (task.isAsync() || pMedium != this)
9547 {
9548 treeLock.release();
9549 pMedium->uninit();
9550 treeLock.acquire();
9551 }
9552 }
9553 }
9554
9555 i_markRegistriesModified();
9556 if (task.isAsync())
9557 {
9558 // in asynchronous mode, save settings now
9559 eik.restore();
9560 m->pVirtualBox->i_saveModifiedRegistries();
9561 eik.fetch();
9562 }
9563
9564 if (FAILED(mrc))
9565 {
9566 /* Here we come if either VDMerge() failed (in which case we
9567 * assume that it tried to do everything to make a further
9568 * retry possible -- e.g. not deleted intermediate media
9569 * and so on) or VirtualBox::saveRegistries() failed (where we
9570 * should have the original tree but with intermediate storage
9571 * units deleted by VDMerge()). We have to only restore states
9572 * (through the MergeChain dtor) unless we are run synchronously
9573 * in which case it's the responsibility of the caller as stated
9574 * in the mergeTo() docs. The latter also implies that we
9575 * don't own the merge chain, so release it in this case. */
9576 if (task.isAsync())
9577 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
9578 }
9579 else if (task.NotifyAboutChanges())
9580 {
9581 for (std::set<ComObjPtr<Medium> >::const_iterator it = pMediaForNotify.begin();
9582 it != pMediaForNotify.end();
9583 ++it)
9584 {
9585 if (it->isNotNull())
9586 m->pVirtualBox->i_onMediumConfigChanged(*it);
9587 }
9588 for (std::map<Guid, DeviceType_T>::const_iterator it = uIdsForNotify.begin();
9589 it != uIdsForNotify.end();
9590 ++it)
9591 {
9592 m->pVirtualBox->i_onMediumRegistered(it->first, it->second, FALSE);
9593 }
9594 }
9595
9596 return mrc;
9597}
9598
9599/**
9600 * Implementation code for the "clone" task.
9601 *
9602 * This only gets started from Medium::CloneTo() and always runs asynchronously.
9603 * As a result, we always save the VirtualBox.xml file when we're done here.
9604 *
9605 * @param task
9606 * @return
9607 */
9608HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
9609{
9610 /** @todo r=klaus The code below needs to be double checked with regard
9611 * to lock order violations, it probably causes lock order issues related
9612 * to the AutoCaller usage. */
9613 HRESULT hrcTmp = S_OK;
9614
9615 const ComObjPtr<Medium> &pTarget = task.mTarget;
9616 const ComObjPtr<Medium> &pParent = task.mParent;
9617
9618 bool fCreatingTarget = false;
9619 bool cloningWithinList = false;
9620 unsigned uTargetIdx = VD_LAST_IMAGE;
9621
9622 uint64_t size = 0, logicalSize = 0;
9623 MediumVariant_T variant = MediumVariant_Standard;
9624 bool fGenerateUuid = false;
9625
9626 try
9627 {
9628 if (!pParent.isNull())
9629 {
9630
9631 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9632 {
9633 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
9634 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9635 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9636 pParent->m->strLocationFull.c_str());
9637 }
9638 }
9639
9640 /* Lock all in {parent,child} order. The lock is also used as a
9641 * signal from the task initiator (which releases it only after
9642 * RTThreadCreate()) that we can start the job. */
9643 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
9644
9645 fCreatingTarget = pTarget->m->state == MediumState_Creating;
9646
9647 /* The object may request a specific UUID (through a special form of
9648 * the moveTo() argument). Otherwise we have to generate it */
9649 Guid targetId = pTarget->m->id;
9650
9651 fGenerateUuid = targetId.isZero();
9652 if (fGenerateUuid)
9653 {
9654 targetId.create();
9655 /* VirtualBox::registerMedium() will need UUID */
9656 unconst(pTarget->m->id) = targetId;
9657 }
9658
9659 PVDISK hdd;
9660 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9661 ComAssertRCThrow(vrc, E_FAIL);
9662
9663 try
9664 {
9665 /* Open all media in the source chain. */
9666 MediumLockList::Base::const_iterator sourceListBegin =
9667 task.mpSourceMediumLockList->GetBegin();
9668 MediumLockList::Base::const_iterator sourceListEnd =
9669 task.mpSourceMediumLockList->GetEnd();
9670 unsigned i=0;
9671 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9672 it != sourceListEnd;
9673 ++it)
9674 {
9675 const MediumLock &mediumLock = *it;
9676 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9677 if (pMedium == pTarget)
9678 {
9679 cloningWithinList = true;
9680 uTargetIdx = i;
9681 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9682 Assert(pMedium->m->state == MediumState_LockedWrite);
9683 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9684 if (pMedium->m->type == MediumType_Shareable)
9685 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9686 /* Open target in appropriate mode. */
9687 vrc = VDOpen(hdd,
9688 pMedium->m->strFormat.c_str(),
9689 pMedium->m->strLocationFull.c_str(),
9690 uOpenFlags | m->uOpenFlagsDef,
9691 pMedium->m->vdImageIfaces);
9692 if (RT_FAILURE(vrc))
9693 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9694 tr("Could not open the medium storage unit '%s'%s"),
9695 pMedium->m->strLocationFull.c_str(),
9696 i_vdError(vrc).c_str());
9697 }
9698 i++;
9699 if (!cloningWithinList || pMedium != pTarget)
9700 {
9701 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9702 /* sanity check */
9703 Assert(pMedium->m->state == MediumState_LockedRead);
9704 /** Open all media in read-only mode. */
9705 vrc = VDOpen(hdd,
9706 pMedium->m->strFormat.c_str(),
9707 pMedium->m->strLocationFull.c_str(),
9708 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9709 pMedium->m->vdImageIfaces);
9710 if (RT_FAILURE(vrc))
9711 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9712 tr("Could not open the medium storage unit '%s'%s"),
9713 pMedium->m->strLocationFull.c_str(),
9714 i_vdError(vrc).c_str());
9715 }
9716 }
9717
9718 Utf8Str targetFormat(pTarget->m->strFormat);
9719 Utf8Str targetLocation(pTarget->m->strLocationFull);
9720 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
9721
9722 Assert( pTarget->m->state == MediumState_Creating
9723 || pTarget->m->state == MediumState_LockedWrite);
9724 Assert(m->state == MediumState_LockedRead);
9725 Assert( pParent.isNull()
9726 || pParent->m->state == MediumState_LockedRead);
9727
9728 /* unlock before the potentially lengthy operation */
9729 thisLock.release();
9730
9731 /* ensure the target directory exists */
9732 if (capabilities & MediumFormatCapabilities_File)
9733 {
9734 HRESULT hrc = VirtualBox::i_ensureFilePathExists(targetLocation,
9735 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9736 if (FAILED(hrc))
9737 throw hrc;
9738 }
9739
9740 PVDISK targetHdd;
9741 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9742 ComAssertRCThrow(vrc, E_FAIL);
9743
9744 try
9745 {
9746 if (!cloningWithinList)
9747 {
9748 /* Open all media in the target chain. */
9749 MediumLockList::Base::const_iterator targetListBegin =
9750 task.mpTargetMediumLockList->GetBegin();
9751 MediumLockList::Base::const_iterator targetListEnd =
9752 task.mpTargetMediumLockList->GetEnd();
9753 for (MediumLockList::Base::const_iterator it = targetListBegin;
9754 it != targetListEnd;
9755 ++it)
9756 {
9757 const MediumLock &mediumLock = *it;
9758 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9759 /* If the target medium is not created yet there's no
9760 * reason to open it. */
9761 if (pMedium == pTarget && fCreatingTarget)
9762 continue;
9763 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9764 /* sanity check */
9765 Assert( pMedium->m->state == MediumState_LockedRead
9766 || pMedium->m->state == MediumState_LockedWrite);
9767 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9768 if (pMedium->m->state != MediumState_LockedWrite)
9769 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9770 if (pMedium->m->type == MediumType_Shareable)
9771 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9772 /* Open all media in appropriate mode. */
9773 vrc = VDOpen(targetHdd,
9774 pMedium->m->strFormat.c_str(),
9775 pMedium->m->strLocationFull.c_str(),
9776 uOpenFlags | m->uOpenFlagsDef,
9777 pMedium->m->vdImageIfaces);
9778 if (RT_FAILURE(vrc))
9779 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9780 tr("Could not open the medium storage unit '%s'%s"),
9781 pMedium->m->strLocationFull.c_str(),
9782 i_vdError(vrc).c_str());
9783 }
9784 /* target isn't locked, but no changing data is accessed */
9785 if (task.midxSrcImageSame == UINT32_MAX)
9786 {
9787 vrc = VDCopy(hdd,
9788 VD_LAST_IMAGE,
9789 targetHdd,
9790 targetFormat.c_str(),
9791 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9792 false /* fMoveByRename */,
9793 task.mTargetLogicalSize /* cbSize */,
9794 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk),
9795 targetId.raw(),
9796 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9797 NULL /* pVDIfsOperation */,
9798 pTarget->m->vdImageIfaces,
9799 task.mVDOperationIfaces);
9800 }
9801 else
9802 {
9803 vrc = VDCopyEx(hdd,
9804 VD_LAST_IMAGE,
9805 targetHdd,
9806 VD_LAST_IMAGE,
9807 targetFormat.c_str(),
9808 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9809 false /* fMoveByRename */,
9810 task.mTargetLogicalSize /* cbSize */,
9811 task.midxSrcImageSame,
9812 task.midxDstImageSame,
9813 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk),
9814 targetId.raw(),
9815 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9816 NULL /* pVDIfsOperation */,
9817 pTarget->m->vdImageIfaces,
9818 task.mVDOperationIfaces);
9819 }
9820 if (RT_FAILURE(vrc))
9821 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9822 tr("Could not create the clone medium '%s'%s"),
9823 targetLocation.c_str(), i_vdError(vrc).c_str());
9824
9825 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
9826 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
9827 unsigned uImageFlags;
9828 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
9829 if (RT_SUCCESS(vrc))
9830 variant = (MediumVariant_T)uImageFlags;
9831 }
9832 else //cloningWithinList - only use source chain
9833 {
9834 if (task.midxSrcImageSame == UINT32_MAX)
9835 {
9836 vrc = VDCopyEx(hdd,
9837 VD_LAST_IMAGE,
9838 hdd,
9839 uTargetIdx,
9840 targetFormat.c_str(),
9841 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9842 false /* fMoveByRename */,
9843 task.mTargetLogicalSize /* cbSize */,
9844 VD_IMAGE_CONTENT_UNKNOWN,
9845 VD_IMAGE_CONTENT_UNKNOWN,
9846 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk),
9847 targetId.raw(),
9848 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9849 NULL /* pVDIfsOperation */,
9850 pTarget->m->vdImageIfaces,
9851 task.mVDOperationIfaces);
9852 }
9853 else
9854 {
9855 vrc = VDCopyEx(hdd,
9856 VD_LAST_IMAGE,
9857 hdd,
9858 uTargetIdx,
9859 targetFormat.c_str(),
9860 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9861 false /* fMoveByRename */,
9862 task.mTargetLogicalSize /* cbSize */,
9863 task.midxSrcImageSame,
9864 task.midxDstImageSame,
9865 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk),
9866 targetId.raw(),
9867 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9868 NULL /* pVDIfsOperation */,
9869 pTarget->m->vdImageIfaces,
9870 task.mVDOperationIfaces);
9871 }
9872 if (RT_FAILURE(vrc))
9873 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9874 tr("Could not create the clone medium '%s'%s"),
9875 targetLocation.c_str(), i_vdError(vrc).c_str());
9876
9877 size = VDGetFileSize(hdd, uTargetIdx);
9878 logicalSize = VDGetSize(hdd, uTargetIdx);
9879 unsigned uImageFlags;
9880 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9881 if (RT_SUCCESS(vrc))
9882 variant = (MediumVariant_T)uImageFlags;
9883
9884 }
9885 }
9886 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9887
9888 VDDestroy(targetHdd);
9889 }
9890 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9891
9892 VDDestroy(hdd);
9893 }
9894 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
9895
9896 ErrorInfoKeeper eik;
9897 MultiResult mrc(hrcTmp);
9898
9899 /* Only do the parent changes for newly created media. */
9900 if (SUCCEEDED(mrc) && fCreatingTarget)
9901 {
9902 /* we set m->pParent & children() */
9903 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9904
9905 Assert(pTarget->m->pParent.isNull());
9906
9907 if (pParent)
9908 {
9909 /* Associate the clone with the parent and deassociate
9910 * from VirtualBox. Depth check above. */
9911 pTarget->i_setParent(pParent);
9912
9913 /* register with mVirtualBox as the last step and move to
9914 * Created state only on success (leaving an orphan file is
9915 * better than breaking media registry consistency) */
9916 eik.restore();
9917 ComObjPtr<Medium> pMedium;
9918 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9919 treeLock);
9920 Assert( FAILED(mrc)
9921 || pTarget == pMedium);
9922 eik.fetch();
9923
9924 if (FAILED(mrc))
9925 /* break parent association on failure to register */
9926 pTarget->i_deparent(); // removes target from parent
9927 }
9928 else
9929 {
9930 /* just register */
9931 eik.restore();
9932 ComObjPtr<Medium> pMedium;
9933 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9934 treeLock);
9935 Assert( FAILED(mrc)
9936 || pTarget == pMedium);
9937 eik.fetch();
9938 }
9939 }
9940
9941 if (fCreatingTarget)
9942 {
9943 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
9944
9945 if (SUCCEEDED(mrc))
9946 {
9947 pTarget->m->state = MediumState_Created;
9948
9949 pTarget->m->size = size;
9950 pTarget->m->logicalSize = logicalSize;
9951 pTarget->m->variant = variant;
9952 }
9953 else
9954 {
9955 /* back to NotCreated on failure */
9956 pTarget->m->state = MediumState_NotCreated;
9957
9958 /* reset UUID to prevent it from being reused next time */
9959 if (fGenerateUuid)
9960 unconst(pTarget->m->id).clear();
9961 }
9962 }
9963
9964 /* Copy any filter related settings over to the target. */
9965 if (SUCCEEDED(mrc))
9966 {
9967 /* Copy any filter related settings over. */
9968 ComObjPtr<Medium> pBase = i_getBase();
9969 ComObjPtr<Medium> pTargetBase = pTarget->i_getBase();
9970 std::vector<com::Utf8Str> aFilterPropNames;
9971 std::vector<com::Utf8Str> aFilterPropValues;
9972 mrc = pBase->i_getFilterProperties(aFilterPropNames, aFilterPropValues);
9973 if (SUCCEEDED(mrc))
9974 {
9975 /* Go through the properties and add them to the target medium. */
9976 for (unsigned idx = 0; idx < aFilterPropNames.size(); idx++)
9977 {
9978 mrc = pTargetBase->i_setPropertyDirect(aFilterPropNames[idx], aFilterPropValues[idx]);
9979 if (FAILED(mrc)) break;
9980 }
9981
9982 // now, at the end of this task (always asynchronous), save the settings
9983 if (SUCCEEDED(mrc))
9984 {
9985 // save the settings
9986 i_markRegistriesModified();
9987 /* collect multiple errors */
9988 eik.restore();
9989 m->pVirtualBox->i_saveModifiedRegistries();
9990 eik.fetch();
9991
9992 if (task.NotifyAboutChanges())
9993 {
9994 if (!fCreatingTarget)
9995 {
9996 if (!aFilterPropNames.empty())
9997 m->pVirtualBox->i_onMediumConfigChanged(pTargetBase);
9998 if (pParent)
9999 m->pVirtualBox->i_onMediumConfigChanged(pParent);
10000 }
10001 else
10002 {
10003 m->pVirtualBox->i_onMediumRegistered(pTarget->i_getId(), pTarget->i_getDeviceType(), TRUE);
10004 }
10005 }
10006 }
10007 }
10008 }
10009
10010 /* Everything is explicitly unlocked when the task exits,
10011 * as the task destruction also destroys the source chain. */
10012
10013 /* Make sure the source chain is released early. It could happen
10014 * that we get a deadlock in Appliance::Import when Medium::Close
10015 * is called & the source chain is released at the same time. */
10016 task.mpSourceMediumLockList->Clear();
10017
10018 return mrc;
10019}
10020
10021/**
10022 * Implementation code for the "move" task.
10023 *
10024 * This only gets started from Medium::MoveTo() and always
10025 * runs asynchronously.
10026 *
10027 * @param task
10028 * @return
10029 */
10030HRESULT Medium::i_taskMoveHandler(Medium::MoveTask &task)
10031{
10032 LogFlowFuncEnter();
10033 HRESULT hrcOut = S_OK;
10034
10035 /* pTarget is equal "this" in our case */
10036 const ComObjPtr<Medium> &pTarget = task.mMedium;
10037
10038 uint64_t size = 0; NOREF(size);
10039 uint64_t logicalSize = 0; NOREF(logicalSize);
10040 MediumVariant_T variant = MediumVariant_Standard; NOREF(variant);
10041
10042 /*
10043 * it's exactly moving, not cloning
10044 */
10045 if (!i_isMoveOperation(pTarget))
10046 {
10047 LogFlowFunc(("LEAVE: hrc=VBOX_E_FILE_ERROR (early)\n"));
10048 return setError(VBOX_E_FILE_ERROR,
10049 tr("Wrong preconditions for moving the medium %s"),
10050 pTarget->m->strLocationFull.c_str());
10051 }
10052
10053 try
10054 {
10055 /* Lock all in {parent,child} order. The lock is also used as a
10056 * signal from the task initiator (which releases it only after
10057 * RTThreadCreate()) that we can start the job. */
10058
10059 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10060
10061 PVDISK hdd;
10062 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10063 ComAssertRCThrow(vrc, E_FAIL);
10064
10065 try
10066 {
10067 /* Open all media in the source chain. */
10068 MediumLockList::Base::const_iterator sourceListBegin =
10069 task.mpMediumLockList->GetBegin();
10070 MediumLockList::Base::const_iterator sourceListEnd =
10071 task.mpMediumLockList->GetEnd();
10072 for (MediumLockList::Base::const_iterator it = sourceListBegin;
10073 it != sourceListEnd;
10074 ++it)
10075 {
10076 const MediumLock &mediumLock = *it;
10077 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10078 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10079
10080 /* sanity check */
10081 Assert(pMedium->m->state == MediumState_LockedWrite);
10082
10083 vrc = VDOpen(hdd,
10084 pMedium->m->strFormat.c_str(),
10085 pMedium->m->strLocationFull.c_str(),
10086 VD_OPEN_FLAGS_NORMAL,
10087 pMedium->m->vdImageIfaces);
10088 if (RT_FAILURE(vrc))
10089 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10090 tr("Could not open the medium storage unit '%s'%s"),
10091 pMedium->m->strLocationFull.c_str(),
10092 i_vdError(vrc).c_str());
10093 }
10094
10095 /* we can directly use pTarget->m->"variables" but for better reading we use local copies */
10096 Guid targetId = pTarget->m->id;
10097 Utf8Str targetFormat(pTarget->m->strFormat);
10098 uint64_t targetCapabilities = pTarget->m->formatObj->i_getCapabilities();
10099
10100 /*
10101 * change target location
10102 * m->strNewLocationFull has been set already together with m->fMoveThisMedium in
10103 * i_preparationForMoving()
10104 */
10105 Utf8Str targetLocation = i_getNewLocationForMoving();
10106
10107 /* unlock before the potentially lengthy operation */
10108 thisLock.release();
10109
10110 /* ensure the target directory exists */
10111 if (targetCapabilities & MediumFormatCapabilities_File)
10112 {
10113 HRESULT hrc = VirtualBox::i_ensureFilePathExists(targetLocation,
10114 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
10115 if (FAILED(hrc))
10116 throw hrc;
10117 }
10118
10119 try
10120 {
10121 vrc = VDCopy(hdd,
10122 VD_LAST_IMAGE,
10123 hdd,
10124 targetFormat.c_str(),
10125 targetLocation.c_str(),
10126 true /* fMoveByRename */,
10127 0 /* cbSize */,
10128 VD_IMAGE_FLAGS_NONE,
10129 targetId.raw(),
10130 VD_OPEN_FLAGS_NORMAL,
10131 NULL /* pVDIfsOperation */,
10132 pTarget->m->vdImageIfaces,
10133 task.mVDOperationIfaces);
10134 if (RT_FAILURE(vrc))
10135 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10136 tr("Could not move medium '%s'%s"),
10137 targetLocation.c_str(), i_vdError(vrc).c_str());
10138 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10139 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10140 unsigned uImageFlags;
10141 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
10142 if (RT_SUCCESS(vrc))
10143 variant = (MediumVariant_T)uImageFlags;
10144
10145 /*
10146 * set current location, because VDCopy\VDCopyEx doesn't do it.
10147 * also reset moving flag
10148 */
10149 i_resetMoveOperationData();
10150 m->strLocationFull = targetLocation;
10151
10152 }
10153 catch (HRESULT hrcXcpt) { hrcOut = hrcXcpt; }
10154
10155 }
10156 catch (HRESULT hrcXcpt) { hrcOut = hrcXcpt; }
10157
10158 VDDestroy(hdd);
10159 }
10160 catch (HRESULT hrcXcpt) { hrcOut = hrcXcpt; }
10161
10162 ErrorInfoKeeper eik;
10163 MultiResult mrc(hrcOut);
10164
10165 // now, at the end of this task (always asynchronous), save the settings
10166 if (SUCCEEDED(mrc))
10167 {
10168 // save the settings
10169 i_markRegistriesModified();
10170 /* collect multiple errors */
10171 eik.restore();
10172 m->pVirtualBox->i_saveModifiedRegistries();
10173 eik.fetch();
10174 }
10175
10176 /* Everything is explicitly unlocked when the task exits,
10177 * as the task destruction also destroys the source chain. */
10178
10179 task.mpMediumLockList->Clear();
10180
10181 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
10182 m->pVirtualBox->i_onMediumConfigChanged(this);
10183
10184 LogFlowFunc(("LEAVE: mrc=%Rhrc\n", (HRESULT)mrc));
10185 return mrc;
10186}
10187
10188/**
10189 * Implementation code for the "delete" task.
10190 *
10191 * This task always gets started from Medium::deleteStorage() and can run
10192 * synchronously or asynchronously depending on the "wait" parameter passed to
10193 * that function.
10194 *
10195 * @param task
10196 * @return
10197 */
10198HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
10199{
10200 NOREF(task);
10201 HRESULT hrc = S_OK;
10202
10203 try
10204 {
10205 /* The lock is also used as a signal from the task initiator (which
10206 * releases it only after RTThreadCreate()) that we can start the job */
10207 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10208
10209 PVDISK hdd;
10210 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10211 ComAssertRCThrow(vrc, E_FAIL);
10212
10213 Utf8Str format(m->strFormat);
10214 Utf8Str location(m->strLocationFull);
10215
10216 /* unlock before the potentially lengthy operation */
10217 Assert(m->state == MediumState_Deleting);
10218 thisLock.release();
10219
10220 try
10221 {
10222 vrc = VDOpen(hdd,
10223 format.c_str(),
10224 location.c_str(),
10225 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
10226 m->vdImageIfaces);
10227 if (RT_SUCCESS(vrc))
10228 vrc = VDClose(hdd, true /* fDelete */);
10229
10230 if (RT_FAILURE(vrc) && vrc != VERR_FILE_NOT_FOUND)
10231 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10232 tr("Could not delete the medium storage unit '%s'%s"),
10233 location.c_str(), i_vdError(vrc).c_str());
10234
10235 }
10236 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10237
10238 VDDestroy(hdd);
10239 }
10240 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10241
10242 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10243
10244 /* go to the NotCreated state even on failure since the storage
10245 * may have been already partially deleted and cannot be used any
10246 * more. One will be able to manually re-open the storage if really
10247 * needed to re-register it. */
10248 m->state = MediumState_NotCreated;
10249
10250 /* Reset UUID to prevent Create* from reusing it again */
10251 com::Guid uOldId = m->id;
10252 unconst(m->id).clear();
10253
10254 if (task.NotifyAboutChanges() && SUCCEEDED(hrc))
10255 {
10256 if (m->pParent.isNotNull())
10257 m->pVirtualBox->i_onMediumConfigChanged(m->pParent);
10258 m->pVirtualBox->i_onMediumRegistered(uOldId, m->devType, FALSE);
10259 }
10260
10261 return hrc;
10262}
10263
10264/**
10265 * Implementation code for the "reset" task.
10266 *
10267 * This always gets started asynchronously from Medium::Reset().
10268 *
10269 * @param task
10270 * @return
10271 */
10272HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
10273{
10274 HRESULT hrc = S_OK;
10275
10276 uint64_t size = 0, logicalSize = 0;
10277 MediumVariant_T variant = MediumVariant_Standard;
10278
10279 try
10280 {
10281 /* The lock is also used as a signal from the task initiator (which
10282 * releases it only after RTThreadCreate()) that we can start the job */
10283 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10284
10285 /// @todo Below we use a pair of delete/create operations to reset
10286 /// the diff contents but the most efficient way will of course be
10287 /// to add a VDResetDiff() API call
10288
10289 PVDISK hdd;
10290 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10291 ComAssertRCThrow(vrc, E_FAIL);
10292
10293 Guid id = m->id;
10294 Utf8Str format(m->strFormat);
10295 Utf8Str location(m->strLocationFull);
10296
10297 Medium *pParent = m->pParent;
10298 Guid parentId = pParent->m->id;
10299 Utf8Str parentFormat(pParent->m->strFormat);
10300 Utf8Str parentLocation(pParent->m->strLocationFull);
10301
10302 Assert(m->state == MediumState_LockedWrite);
10303
10304 /* unlock before the potentially lengthy operation */
10305 thisLock.release();
10306
10307 try
10308 {
10309 /* Open all media in the target chain but the last. */
10310 MediumLockList::Base::const_iterator targetListBegin =
10311 task.mpMediumLockList->GetBegin();
10312 MediumLockList::Base::const_iterator targetListEnd =
10313 task.mpMediumLockList->GetEnd();
10314 for (MediumLockList::Base::const_iterator it = targetListBegin;
10315 it != targetListEnd;
10316 ++it)
10317 {
10318 const MediumLock &mediumLock = *it;
10319 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10320
10321 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10322
10323 /* sanity check, "this" is checked above */
10324 Assert( pMedium == this
10325 || pMedium->m->state == MediumState_LockedRead);
10326
10327 /* Open all media in appropriate mode. */
10328 vrc = VDOpen(hdd,
10329 pMedium->m->strFormat.c_str(),
10330 pMedium->m->strLocationFull.c_str(),
10331 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
10332 pMedium->m->vdImageIfaces);
10333 if (RT_FAILURE(vrc))
10334 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10335 tr("Could not open the medium storage unit '%s'%s"),
10336 pMedium->m->strLocationFull.c_str(),
10337 i_vdError(vrc).c_str());
10338
10339 /* Done when we hit the media which should be reset */
10340 if (pMedium == this)
10341 break;
10342 }
10343
10344 /* first, delete the storage unit */
10345 vrc = VDClose(hdd, true /* fDelete */);
10346 if (RT_FAILURE(vrc))
10347 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10348 tr("Could not delete the medium storage unit '%s'%s"),
10349 location.c_str(), i_vdError(vrc).c_str());
10350
10351 /* next, create it again */
10352 vrc = VDOpen(hdd,
10353 parentFormat.c_str(),
10354 parentLocation.c_str(),
10355 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
10356 m->vdImageIfaces);
10357 if (RT_FAILURE(vrc))
10358 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10359 tr("Could not open the medium storage unit '%s'%s"),
10360 parentLocation.c_str(), i_vdError(vrc).c_str());
10361
10362 vrc = VDCreateDiff(hdd,
10363 format.c_str(),
10364 location.c_str(),
10365 /// @todo use the same medium variant as before
10366 VD_IMAGE_FLAGS_NONE,
10367 NULL,
10368 id.raw(),
10369 parentId.raw(),
10370 VD_OPEN_FLAGS_NORMAL,
10371 m->vdImageIfaces,
10372 task.mVDOperationIfaces);
10373 if (RT_FAILURE(vrc))
10374 {
10375 if (vrc == VERR_VD_INVALID_TYPE)
10376 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10377 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
10378 location.c_str(), i_vdError(vrc).c_str());
10379 else
10380 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10381 tr("Could not create the differencing medium storage unit '%s'%s"),
10382 location.c_str(), i_vdError(vrc).c_str());
10383 }
10384
10385 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10386 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10387 unsigned uImageFlags;
10388 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
10389 if (RT_SUCCESS(vrc))
10390 variant = (MediumVariant_T)uImageFlags;
10391 }
10392 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10393
10394 VDDestroy(hdd);
10395 }
10396 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10397
10398 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10399
10400 m->size = size;
10401 m->logicalSize = logicalSize;
10402 m->variant = variant;
10403
10404 if (task.NotifyAboutChanges() && SUCCEEDED(hrc))
10405 m->pVirtualBox->i_onMediumConfigChanged(this);
10406
10407 /* Everything is explicitly unlocked when the task exits,
10408 * as the task destruction also destroys the media chain. */
10409
10410 return hrc;
10411}
10412
10413/**
10414 * Implementation code for the "compact" task.
10415 *
10416 * @param task
10417 * @return
10418 */
10419HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
10420{
10421 HRESULT hrc = S_OK;
10422
10423 /* Lock all in {parent,child} order. The lock is also used as a
10424 * signal from the task initiator (which releases it only after
10425 * RTThreadCreate()) that we can start the job. */
10426 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10427
10428 try
10429 {
10430 PVDISK hdd;
10431 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10432 ComAssertRCThrow(vrc, E_FAIL);
10433
10434 try
10435 {
10436 /* Open all media in the chain. */
10437 MediumLockList::Base::const_iterator mediumListBegin =
10438 task.mpMediumLockList->GetBegin();
10439 MediumLockList::Base::const_iterator mediumListEnd =
10440 task.mpMediumLockList->GetEnd();
10441 MediumLockList::Base::const_iterator mediumListLast =
10442 mediumListEnd;
10443 --mediumListLast;
10444 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10445 it != mediumListEnd;
10446 ++it)
10447 {
10448 const MediumLock &mediumLock = *it;
10449 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10450 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10451
10452 /* sanity check */
10453 if (it == mediumListLast)
10454 Assert(pMedium->m->state == MediumState_LockedWrite);
10455 else
10456 Assert(pMedium->m->state == MediumState_LockedRead);
10457
10458 /* Open all media but last in read-only mode. Do not handle
10459 * shareable media, as compaction and sharing are mutually
10460 * exclusive. */
10461 vrc = VDOpen(hdd,
10462 pMedium->m->strFormat.c_str(),
10463 pMedium->m->strLocationFull.c_str(),
10464 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10465 pMedium->m->vdImageIfaces);
10466 if (RT_FAILURE(vrc))
10467 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10468 tr("Could not open the medium storage unit '%s'%s"),
10469 pMedium->m->strLocationFull.c_str(),
10470 i_vdError(vrc).c_str());
10471 }
10472
10473 Assert(m->state == MediumState_LockedWrite);
10474
10475 Utf8Str location(m->strLocationFull);
10476
10477 /* unlock before the potentially lengthy operation */
10478 thisLock.release();
10479
10480 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
10481 if (RT_FAILURE(vrc))
10482 {
10483 if (vrc == VERR_NOT_SUPPORTED)
10484 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10485 tr("Compacting is not yet supported for medium '%s'"),
10486 location.c_str());
10487 else if (vrc == VERR_NOT_IMPLEMENTED)
10488 throw setErrorBoth(E_NOTIMPL, vrc,
10489 tr("Compacting is not implemented, medium '%s'"),
10490 location.c_str());
10491 else
10492 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10493 tr("Could not compact medium '%s'%s"),
10494 location.c_str(),
10495 i_vdError(vrc).c_str());
10496 }
10497 }
10498 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10499
10500 VDDestroy(hdd);
10501 }
10502 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10503
10504 if (task.NotifyAboutChanges() && SUCCEEDED(hrc))
10505 m->pVirtualBox->i_onMediumConfigChanged(this);
10506
10507 /* Everything is explicitly unlocked when the task exits,
10508 * as the task destruction also destroys the media chain. */
10509
10510 return hrc;
10511}
10512
10513/**
10514 * Implementation code for the "resize" task.
10515 *
10516 * @param task
10517 * @return
10518 */
10519HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
10520{
10521 HRESULT hrc = S_OK;
10522
10523 uint64_t size = 0, logicalSize = 0;
10524
10525 try
10526 {
10527 /* The lock is also used as a signal from the task initiator (which
10528 * releases it only after RTThreadCreate()) that we can start the job */
10529 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10530
10531 PVDISK hdd;
10532 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10533 ComAssertRCThrow(vrc, E_FAIL);
10534
10535 try
10536 {
10537 /* Open all media in the chain. */
10538 MediumLockList::Base::const_iterator mediumListBegin =
10539 task.mpMediumLockList->GetBegin();
10540 MediumLockList::Base::const_iterator mediumListEnd =
10541 task.mpMediumLockList->GetEnd();
10542 MediumLockList::Base::const_iterator mediumListLast =
10543 mediumListEnd;
10544 --mediumListLast;
10545 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10546 it != mediumListEnd;
10547 ++it)
10548 {
10549 const MediumLock &mediumLock = *it;
10550 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10551 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10552
10553 /* sanity check */
10554 if (it == mediumListLast)
10555 Assert(pMedium->m->state == MediumState_LockedWrite);
10556 else
10557 Assert(pMedium->m->state == MediumState_LockedRead ||
10558 // Allow resize the target image during mergeTo in case
10559 // of direction from parent to child because all intermediate
10560 // images are marked to MediumState_Deleting and will be
10561 // destroyed after successful merge
10562 pMedium->m->state == MediumState_Deleting);
10563
10564 /* Open all media but last in read-only mode. Do not handle
10565 * shareable media, as compaction and sharing are mutually
10566 * exclusive. */
10567 vrc = VDOpen(hdd,
10568 pMedium->m->strFormat.c_str(),
10569 pMedium->m->strLocationFull.c_str(),
10570 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10571 pMedium->m->vdImageIfaces);
10572 if (RT_FAILURE(vrc))
10573 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10574 tr("Could not open the medium storage unit '%s'%s"),
10575 pMedium->m->strLocationFull.c_str(),
10576 i_vdError(vrc).c_str());
10577 }
10578
10579 Assert(m->state == MediumState_LockedWrite);
10580
10581 Utf8Str location(m->strLocationFull);
10582
10583 /* unlock before the potentially lengthy operation */
10584 thisLock.release();
10585
10586 VDGEOMETRY geo = {0, 0, 0}; /* auto */
10587 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
10588 if (RT_FAILURE(vrc))
10589 {
10590 if (vrc == VERR_VD_SHRINK_NOT_SUPPORTED)
10591 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10592 tr("Shrinking is not yet supported for medium '%s'"),
10593 location.c_str());
10594 if (vrc == VERR_NOT_SUPPORTED)
10595 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10596 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
10597 task.mSize, location.c_str());
10598 else if (vrc == VERR_NOT_IMPLEMENTED)
10599 throw setErrorBoth(E_NOTIMPL, vrc,
10600 tr("Resizing is not implemented, medium '%s'"),
10601 location.c_str());
10602 else
10603 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10604 tr("Could not resize medium '%s'%s"),
10605 location.c_str(),
10606 i_vdError(vrc).c_str());
10607 }
10608 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10609 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10610 }
10611 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10612
10613 VDDestroy(hdd);
10614 }
10615 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
10616
10617 if (SUCCEEDED(hrc))
10618 {
10619 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10620 m->size = size;
10621 m->logicalSize = logicalSize;
10622
10623 if (task.NotifyAboutChanges())
10624 m->pVirtualBox->i_onMediumConfigChanged(this);
10625 }
10626
10627 /* Everything is explicitly unlocked when the task exits,
10628 * as the task destruction also destroys the media chain. */
10629
10630 return hrc;
10631}
10632
10633/**
10634 * Implementation code for the "import" task.
10635 *
10636 * This only gets started from Medium::importFile() and always runs
10637 * asynchronously. It potentially touches the media registry, so we
10638 * always save the VirtualBox.xml file when we're done here.
10639 *
10640 * @param task
10641 * @return
10642 */
10643HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
10644{
10645 /** @todo r=klaus The code below needs to be double checked with regard
10646 * to lock order violations, it probably causes lock order issues related
10647 * to the AutoCaller usage. */
10648 HRESULT hrcTmp = S_OK;
10649
10650 const ComObjPtr<Medium> &pParent = task.mParent;
10651
10652 bool fCreatingTarget = false;
10653
10654 uint64_t size = 0, logicalSize = 0;
10655 MediumVariant_T variant = MediumVariant_Standard;
10656 bool fGenerateUuid = false;
10657
10658 try
10659 {
10660 if (!pParent.isNull())
10661 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
10662 {
10663 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
10664 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10665 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
10666 pParent->m->strLocationFull.c_str());
10667 }
10668
10669 /* Lock all in {parent,child} order. The lock is also used as a
10670 * signal from the task initiator (which releases it only after
10671 * RTThreadCreate()) that we can start the job. */
10672 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
10673
10674 fCreatingTarget = m->state == MediumState_Creating;
10675
10676 /* The object may request a specific UUID (through a special form of
10677 * the moveTo() argument). Otherwise we have to generate it */
10678 Guid targetId = m->id;
10679
10680 fGenerateUuid = targetId.isZero();
10681 if (fGenerateUuid)
10682 {
10683 targetId.create();
10684 /* VirtualBox::i_registerMedium() will need UUID */
10685 unconst(m->id) = targetId;
10686 }
10687
10688
10689 PVDISK hdd;
10690 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10691 ComAssertRCThrow(vrc, E_FAIL);
10692
10693 try
10694 {
10695 /* Open source medium. */
10696 vrc = VDOpen(hdd,
10697 task.mFormat->i_getId().c_str(),
10698 task.mFilename.c_str(),
10699 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
10700 task.mVDImageIfaces);
10701 if (RT_FAILURE(vrc))
10702 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10703 tr("Could not open the medium storage unit '%s'%s"),
10704 task.mFilename.c_str(),
10705 i_vdError(vrc).c_str());
10706
10707 Utf8Str targetFormat(m->strFormat);
10708 Utf8Str targetLocation(m->strLocationFull);
10709 uint64_t capabilities = task.mFormat->i_getCapabilities();
10710
10711 Assert( m->state == MediumState_Creating
10712 || m->state == MediumState_LockedWrite);
10713 Assert( pParent.isNull()
10714 || pParent->m->state == MediumState_LockedRead);
10715
10716 /* unlock before the potentially lengthy operation */
10717 thisLock.release();
10718
10719 /* ensure the target directory exists */
10720 if (capabilities & MediumFormatCapabilities_File)
10721 {
10722 HRESULT hrc = VirtualBox::i_ensureFilePathExists(targetLocation,
10723 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
10724 if (FAILED(hrc))
10725 throw hrc;
10726 }
10727
10728 PVDISK targetHdd;
10729 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
10730 ComAssertRCThrow(vrc, E_FAIL);
10731
10732 try
10733 {
10734 /* Open all media in the target chain. */
10735 MediumLockList::Base::const_iterator targetListBegin =
10736 task.mpTargetMediumLockList->GetBegin();
10737 MediumLockList::Base::const_iterator targetListEnd =
10738 task.mpTargetMediumLockList->GetEnd();
10739 for (MediumLockList::Base::const_iterator it = targetListBegin;
10740 it != targetListEnd;
10741 ++it)
10742 {
10743 const MediumLock &mediumLock = *it;
10744 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10745
10746 /* If the target medium is not created yet there's no
10747 * reason to open it. */
10748 if (pMedium == this && fCreatingTarget)
10749 continue;
10750
10751 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10752
10753 /* sanity check */
10754 Assert( pMedium->m->state == MediumState_LockedRead
10755 || pMedium->m->state == MediumState_LockedWrite);
10756
10757 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
10758 if (pMedium->m->state != MediumState_LockedWrite)
10759 uOpenFlags = VD_OPEN_FLAGS_READONLY;
10760 if (pMedium->m->type == MediumType_Shareable)
10761 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
10762
10763 /* Open all media in appropriate mode. */
10764 vrc = VDOpen(targetHdd,
10765 pMedium->m->strFormat.c_str(),
10766 pMedium->m->strLocationFull.c_str(),
10767 uOpenFlags | m->uOpenFlagsDef,
10768 pMedium->m->vdImageIfaces);
10769 if (RT_FAILURE(vrc))
10770 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10771 tr("Could not open the medium storage unit '%s'%s"),
10772 pMedium->m->strLocationFull.c_str(),
10773 i_vdError(vrc).c_str());
10774 }
10775
10776 vrc = VDCopy(hdd,
10777 VD_LAST_IMAGE,
10778 targetHdd,
10779 targetFormat.c_str(),
10780 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
10781 false /* fMoveByRename */,
10782 0 /* cbSize */,
10783 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX | MediumVariant_VmdkRawDisk),
10784 targetId.raw(),
10785 VD_OPEN_FLAGS_NORMAL,
10786 NULL /* pVDIfsOperation */,
10787 m->vdImageIfaces,
10788 task.mVDOperationIfaces);
10789 if (RT_FAILURE(vrc))
10790 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10791 tr("Could not create the imported medium '%s'%s"),
10792 targetLocation.c_str(), i_vdError(vrc).c_str());
10793
10794 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
10795 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
10796 unsigned uImageFlags;
10797 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
10798 if (RT_SUCCESS(vrc))
10799 variant = (MediumVariant_T)uImageFlags;
10800 }
10801 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
10802
10803 VDDestroy(targetHdd);
10804 }
10805 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
10806
10807 VDDestroy(hdd);
10808 }
10809 catch (HRESULT hrcXcpt) { hrcTmp = hrcXcpt; }
10810
10811 ErrorInfoKeeper eik;
10812 MultiResult mrc(hrcTmp);
10813
10814 /* Only do the parent changes for newly created media. */
10815 if (SUCCEEDED(mrc) && fCreatingTarget)
10816 {
10817 /* we set m->pParent & children() */
10818 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10819
10820 Assert(m->pParent.isNull());
10821
10822 if (pParent)
10823 {
10824 /* Associate the imported medium with the parent and deassociate
10825 * from VirtualBox. Depth check above. */
10826 i_setParent(pParent);
10827
10828 /* register with mVirtualBox as the last step and move to
10829 * Created state only on success (leaving an orphan file is
10830 * better than breaking media registry consistency) */
10831 eik.restore();
10832 ComObjPtr<Medium> pMedium;
10833 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
10834 treeLock);
10835 Assert(this == pMedium);
10836 eik.fetch();
10837
10838 if (FAILED(mrc))
10839 /* break parent association on failure to register */
10840 this->i_deparent(); // removes target from parent
10841 }
10842 else
10843 {
10844 /* just register */
10845 eik.restore();
10846 ComObjPtr<Medium> pMedium;
10847 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
10848 Assert(this == pMedium);
10849 eik.fetch();
10850 }
10851 }
10852
10853 if (fCreatingTarget)
10854 {
10855 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
10856
10857 if (SUCCEEDED(mrc))
10858 {
10859 m->state = MediumState_Created;
10860
10861 m->size = size;
10862 m->logicalSize = logicalSize;
10863 m->variant = variant;
10864 }
10865 else
10866 {
10867 /* back to NotCreated on failure */
10868 m->state = MediumState_NotCreated;
10869
10870 /* reset UUID to prevent it from being reused next time */
10871 if (fGenerateUuid)
10872 unconst(m->id).clear();
10873 }
10874 }
10875
10876 // now, at the end of this task (always asynchronous), save the settings
10877 {
10878 // save the settings
10879 i_markRegistriesModified();
10880 /* collect multiple errors */
10881 eik.restore();
10882 m->pVirtualBox->i_saveModifiedRegistries();
10883 eik.fetch();
10884 }
10885
10886 /* Everything is explicitly unlocked when the task exits,
10887 * as the task destruction also destroys the target chain. */
10888
10889 /* Make sure the target chain is released early, otherwise it can
10890 * lead to deadlocks with concurrent IAppliance activities. */
10891 task.mpTargetMediumLockList->Clear();
10892
10893 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
10894 {
10895 if (pParent)
10896 m->pVirtualBox->i_onMediumConfigChanged(pParent);
10897 if (fCreatingTarget)
10898 m->pVirtualBox->i_onMediumConfigChanged(this);
10899 else
10900 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
10901 }
10902
10903 return mrc;
10904}
10905
10906/**
10907 * Sets up the encryption settings for a filter.
10908 */
10909void Medium::i_taskEncryptSettingsSetup(MediumCryptoFilterSettings *pSettings, const char *pszCipher,
10910 const char *pszKeyStore, const char *pszPassword,
10911 bool fCreateKeyStore)
10912{
10913 pSettings->pszCipher = pszCipher;
10914 pSettings->pszPassword = pszPassword;
10915 pSettings->pszKeyStoreLoad = pszKeyStore;
10916 pSettings->fCreateKeyStore = fCreateKeyStore;
10917 pSettings->pbDek = NULL;
10918 pSettings->cbDek = 0;
10919 pSettings->vdFilterIfaces = NULL;
10920
10921 pSettings->vdIfCfg.pfnAreKeysValid = i_vdCryptoConfigAreKeysValid;
10922 pSettings->vdIfCfg.pfnQuerySize = i_vdCryptoConfigQuerySize;
10923 pSettings->vdIfCfg.pfnQuery = i_vdCryptoConfigQuery;
10924 pSettings->vdIfCfg.pfnQueryBytes = NULL;
10925
10926 pSettings->vdIfCrypto.pfnKeyRetain = i_vdCryptoKeyRetain;
10927 pSettings->vdIfCrypto.pfnKeyRelease = i_vdCryptoKeyRelease;
10928 pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = i_vdCryptoKeyStorePasswordRetain;
10929 pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = i_vdCryptoKeyStorePasswordRelease;
10930 pSettings->vdIfCrypto.pfnKeyStoreSave = i_vdCryptoKeyStoreSave;
10931 pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = i_vdCryptoKeyStoreReturnParameters;
10932
10933 int vrc = VDInterfaceAdd(&pSettings->vdIfCfg.Core,
10934 "Medium::vdInterfaceCfgCrypto",
10935 VDINTERFACETYPE_CONFIG, pSettings,
10936 sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces);
10937 AssertRC(vrc);
10938
10939 vrc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core,
10940 "Medium::vdInterfaceCrypto",
10941 VDINTERFACETYPE_CRYPTO, pSettings,
10942 sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces);
10943 AssertRC(vrc);
10944}
10945
10946/**
10947 * Implementation code for the "encrypt" task.
10948 *
10949 * @param task
10950 * @return
10951 */
10952HRESULT Medium::i_taskEncryptHandler(Medium::EncryptTask &task)
10953{
10954# ifndef VBOX_WITH_EXTPACK
10955 RT_NOREF(task);
10956# endif
10957 HRESULT hrc = S_OK;
10958
10959 /* Lock all in {parent,child} order. The lock is also used as a
10960 * signal from the task initiator (which releases it only after
10961 * RTThreadCreate()) that we can start the job. */
10962 ComObjPtr<Medium> pBase = i_getBase();
10963 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10964
10965 try
10966 {
10967# ifdef VBOX_WITH_EXTPACK
10968 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
10969 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
10970 {
10971 /* Load the plugin */
10972 Utf8Str strPlugin;
10973 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
10974 if (SUCCEEDED(hrc))
10975 {
10976 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
10977 if (RT_FAILURE(vrc))
10978 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10979 tr("Encrypting the image failed because the encryption plugin could not be loaded (%s)"),
10980 i_vdError(vrc).c_str());
10981 }
10982 else
10983 throw setError(VBOX_E_NOT_SUPPORTED,
10984 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
10985 ORACLE_PUEL_EXTPACK_NAME);
10986 }
10987 else
10988 throw setError(VBOX_E_NOT_SUPPORTED,
10989 tr("Encryption is not supported because the extension pack '%s' is missing"),
10990 ORACLE_PUEL_EXTPACK_NAME);
10991
10992 PVDISK pDisk = NULL;
10993 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
10994 ComAssertRCThrow(vrc, E_FAIL);
10995
10996 MediumCryptoFilterSettings CryptoSettingsRead;
10997 MediumCryptoFilterSettings CryptoSettingsWrite;
10998
10999 void *pvBuf = NULL;
11000 const char *pszPasswordNew = NULL;
11001 try
11002 {
11003 /* Set up disk encryption filters. */
11004 if (task.mstrCurrentPassword.isEmpty())
11005 {
11006 /*
11007 * Query whether the medium property indicating that encryption is
11008 * configured is existing.
11009 */
11010 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
11011 if (it != pBase->m->mapProperties.end())
11012 throw setError(VBOX_E_PASSWORD_INCORRECT,
11013 tr("The password given for the encrypted image is incorrect"));
11014 }
11015 else
11016 {
11017 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
11018 if (it == pBase->m->mapProperties.end())
11019 throw setError(VBOX_E_INVALID_OBJECT_STATE,
11020 tr("The image is not configured for encryption"));
11021
11022 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, it->second.c_str(), task.mstrCurrentPassword.c_str(),
11023 false /* fCreateKeyStore */);
11024 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
11025 if (vrc == VERR_VD_PASSWORD_INCORRECT)
11026 throw setError(VBOX_E_PASSWORD_INCORRECT,
11027 tr("The password to decrypt the image is incorrect"));
11028 else if (RT_FAILURE(vrc))
11029 throw setError(VBOX_E_INVALID_OBJECT_STATE,
11030 tr("Failed to load the decryption filter: %s"),
11031 i_vdError(vrc).c_str());
11032 }
11033
11034 if (task.mstrCipher.isNotEmpty())
11035 {
11036 if ( task.mstrNewPassword.isEmpty()
11037 && task.mstrNewPasswordId.isEmpty()
11038 && task.mstrCurrentPassword.isNotEmpty())
11039 {
11040 /* An empty password and password ID will default to the current password. */
11041 pszPasswordNew = task.mstrCurrentPassword.c_str();
11042 }
11043 else if (task.mstrNewPassword.isEmpty())
11044 throw setError(VBOX_E_OBJECT_NOT_FOUND,
11045 tr("A password must be given for the image encryption"));
11046 else if (task.mstrNewPasswordId.isEmpty())
11047 throw setError(VBOX_E_INVALID_OBJECT_STATE,
11048 tr("A valid identifier for the password must be given"));
11049 else
11050 pszPasswordNew = task.mstrNewPassword.c_str();
11051
11052 i_taskEncryptSettingsSetup(&CryptoSettingsWrite, task.mstrCipher.c_str(), NULL,
11053 pszPasswordNew, true /* fCreateKeyStore */);
11054 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_WRITE, CryptoSettingsWrite.vdFilterIfaces);
11055 if (RT_FAILURE(vrc))
11056 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
11057 tr("Failed to load the encryption filter: %s"),
11058 i_vdError(vrc).c_str());
11059 }
11060 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
11061 throw setError(VBOX_E_INVALID_OBJECT_STATE,
11062 tr("The password and password identifier must be empty if the output should be unencrypted"));
11063
11064 /* Open all media in the chain. */
11065 MediumLockList::Base::const_iterator mediumListBegin =
11066 task.mpMediumLockList->GetBegin();
11067 MediumLockList::Base::const_iterator mediumListEnd =
11068 task.mpMediumLockList->GetEnd();
11069 MediumLockList::Base::const_iterator mediumListLast =
11070 mediumListEnd;
11071 --mediumListLast;
11072 for (MediumLockList::Base::const_iterator it = mediumListBegin;
11073 it != mediumListEnd;
11074 ++it)
11075 {
11076 const MediumLock &mediumLock = *it;
11077 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
11078 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
11079
11080 Assert(pMedium->m->state == MediumState_LockedWrite);
11081
11082 /* Open all media but last in read-only mode. Do not handle
11083 * shareable media, as compaction and sharing are mutually
11084 * exclusive. */
11085 vrc = VDOpen(pDisk,
11086 pMedium->m->strFormat.c_str(),
11087 pMedium->m->strLocationFull.c_str(),
11088 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
11089 pMedium->m->vdImageIfaces);
11090 if (RT_FAILURE(vrc))
11091 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
11092 tr("Could not open the medium storage unit '%s'%s"),
11093 pMedium->m->strLocationFull.c_str(),
11094 i_vdError(vrc).c_str());
11095 }
11096
11097 Assert(m->state == MediumState_LockedWrite);
11098
11099 Utf8Str location(m->strLocationFull);
11100
11101 /* unlock before the potentially lengthy operation */
11102 thisLock.release();
11103
11104 vrc = VDPrepareWithFilters(pDisk, task.mVDOperationIfaces);
11105 if (RT_FAILURE(vrc))
11106 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
11107 tr("Could not prepare disk images for encryption (%Rrc): %s"),
11108 vrc, i_vdError(vrc).c_str());
11109
11110 thisLock.acquire();
11111 /* If everything went well set the new key store. */
11112 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
11113 if (it != pBase->m->mapProperties.end())
11114 pBase->m->mapProperties.erase(it);
11115
11116 /* Delete KeyId if encryption is removed or the password did change. */
11117 if ( task.mstrNewPasswordId.isNotEmpty()
11118 || task.mstrCipher.isEmpty())
11119 {
11120 it = pBase->m->mapProperties.find("CRYPT/KeyId");
11121 if (it != pBase->m->mapProperties.end())
11122 pBase->m->mapProperties.erase(it);
11123 }
11124
11125 if (CryptoSettingsWrite.pszKeyStore)
11126 {
11127 pBase->m->mapProperties["CRYPT/KeyStore"] = Utf8Str(CryptoSettingsWrite.pszKeyStore);
11128 if (task.mstrNewPasswordId.isNotEmpty())
11129 pBase->m->mapProperties["CRYPT/KeyId"] = task.mstrNewPasswordId;
11130 }
11131
11132 if (CryptoSettingsRead.pszCipherReturned)
11133 RTStrFree(CryptoSettingsRead.pszCipherReturned);
11134
11135 if (CryptoSettingsWrite.pszCipherReturned)
11136 RTStrFree(CryptoSettingsWrite.pszCipherReturned);
11137
11138 thisLock.release();
11139 pBase->i_markRegistriesModified();
11140 m->pVirtualBox->i_saveModifiedRegistries();
11141 }
11142 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
11143
11144 if (pvBuf)
11145 RTMemFree(pvBuf);
11146
11147 VDDestroy(pDisk);
11148# else
11149 throw setError(VBOX_E_NOT_SUPPORTED,
11150 tr("Encryption is not supported because extension pack support is not built in"));
11151# endif
11152 }
11153 catch (HRESULT hrcXcpt) { hrc = hrcXcpt; }
11154
11155 /* Everything is explicitly unlocked when the task exits,
11156 * as the task destruction also destroys the media chain. */
11157
11158 return hrc;
11159}
11160
11161/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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