VirtualBox

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

Last change on this file since 94173 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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

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