VirtualBox

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

Last change on this file since 107229 was 107229, checked in by vboxsync, 6 months ago

Main/Medium: Address broken reference counting of medium back references
when snapshots are involved. Follow-up to r165743 to handle the
scenario of running a restored snapshot with attached removable media
since in this case there won't be a call to Medium::i_removeBackReference()
for the removable media associated with the machine since the snapshot
is running and as such the reference count remains at '1' for the
removable media device(s) even after all the back references in the
snapshots are removed.

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

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