VirtualBox

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

Last change on this file since 63360 was 63256, checked in by vboxsync, 8 years ago

Main: warnings

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