VirtualBox

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

Last change on this file since 86186 was 86125, checked in by vboxsync, 4 years ago

Main: bugref:9623: Restricted the scope of the multi attaching the same image twice by DVD only.

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